LCOV - code coverage report
Current view: top level - src/backend/utils/adt - varlena.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 21.3 % 2475 526
Test Date: 2026-01-26 10:56:24 Functions: 28.7 % 143 41
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 12.2 % 1357 165

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * varlena.c
       4                 :             :  *        Functions for the variable-length built-in types.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *        src/backend/utils/adt/varlena.c
      12                 :             :  *
      13                 :             :  *-------------------------------------------------------------------------
      14                 :             :  */
      15                 :             : #include "postgres.h"
      16                 :             : 
      17                 :             : #include <ctype.h>
      18                 :             : #include <limits.h>
      19                 :             : 
      20                 :             : #include "access/detoast.h"
      21                 :             : #include "access/toast_compression.h"
      22                 :             : #include "catalog/pg_collation.h"
      23                 :             : #include "catalog/pg_type.h"
      24                 :             : #include "common/hashfn.h"
      25                 :             : #include "common/int.h"
      26                 :             : #include "common/unicode_category.h"
      27                 :             : #include "common/unicode_norm.h"
      28                 :             : #include "common/unicode_version.h"
      29                 :             : #include "funcapi.h"
      30                 :             : #include "lib/hyperloglog.h"
      31                 :             : #include "libpq/pqformat.h"
      32                 :             : #include "miscadmin.h"
      33                 :             : #include "nodes/execnodes.h"
      34                 :             : #include "parser/scansup.h"
      35                 :             : #include "port/pg_bswap.h"
      36                 :             : #include "regex/regex.h"
      37                 :             : #include "utils/builtins.h"
      38                 :             : #include "utils/guc.h"
      39                 :             : #include "utils/lsyscache.h"
      40                 :             : #include "utils/memutils.h"
      41                 :             : #include "utils/pg_locale.h"
      42                 :             : #include "utils/sortsupport.h"
      43                 :             : #include "utils/varlena.h"
      44                 :             : 
      45                 :             : typedef struct varlena VarString;
      46                 :             : 
      47                 :             : /*
      48                 :             :  * State for text_position_* functions.
      49                 :             :  */
      50                 :             : typedef struct
      51                 :             : {
      52                 :             :         pg_locale_t locale;                     /* collation used for substring matching */
      53                 :             :         bool            is_multibyte_char_in_char;      /* need to check char boundaries? */
      54                 :             :         bool            greedy;                 /* find longest possible substring? */
      55                 :             : 
      56                 :             :         char       *str1;                       /* haystack string */
      57                 :             :         char       *str2;                       /* needle string */
      58                 :             :         int                     len1;                   /* string lengths in bytes */
      59                 :             :         int                     len2;
      60                 :             : 
      61                 :             :         /* Skip table for Boyer-Moore-Horspool search algorithm: */
      62                 :             :         int                     skiptablemask;  /* mask for ANDing with skiptable subscripts */
      63                 :             :         int                     skiptable[256]; /* skip distance for given mismatched char */
      64                 :             : 
      65                 :             :         /*
      66                 :             :          * Note that with nondeterministic collations, the length of the last
      67                 :             :          * match is not necessarily equal to the length of the "needle" passed in.
      68                 :             :          */
      69                 :             :         char       *last_match;         /* pointer to last match in 'str1' */
      70                 :             :         int                     last_match_len; /* length of last match */
      71                 :             :         int                     last_match_len_tmp; /* same but for internal use */
      72                 :             : 
      73                 :             :         /*
      74                 :             :          * Sometimes we need to convert the byte position of a match to a
      75                 :             :          * character position.  These store the last position that was converted,
      76                 :             :          * so that on the next call, we can continue from that point, rather than
      77                 :             :          * count characters from the very beginning.
      78                 :             :          */
      79                 :             :         char       *refpoint;           /* pointer within original haystack string */
      80                 :             :         int                     refpos;                 /* 0-based character offset of the same point */
      81                 :             : } TextPositionState;
      82                 :             : 
      83                 :             : typedef struct
      84                 :             : {
      85                 :             :         char       *buf1;                       /* 1st string, or abbreviation original string
      86                 :             :                                                                  * buf */
      87                 :             :         char       *buf2;                       /* 2nd string, or abbreviation strxfrm() buf */
      88                 :             :         int                     buflen1;                /* Allocated length of buf1 */
      89                 :             :         int                     buflen2;                /* Allocated length of buf2 */
      90                 :             :         int                     last_len1;              /* Length of last buf1 string/strxfrm() input */
      91                 :             :         int                     last_len2;              /* Length of last buf2 string/strxfrm() blob */
      92                 :             :         int                     last_returned;  /* Last comparison result (cache) */
      93                 :             :         bool            cache_blob;             /* Does buf2 contain strxfrm() blob, etc? */
      94                 :             :         bool            collate_c;
      95                 :             :         Oid                     typid;                  /* Actual datatype (text/bpchar/name) */
      96                 :             :         hyperLogLogState abbr_card; /* Abbreviated key cardinality state */
      97                 :             :         hyperLogLogState full_card; /* Full key cardinality state */
      98                 :             :         double          prop_card;              /* Required cardinality proportion */
      99                 :             :         pg_locale_t locale;
     100                 :             : } VarStringSortSupport;
     101                 :             : 
     102                 :             : /*
     103                 :             :  * Output data for split_text(): we output either to an array or a table.
     104                 :             :  * tupstore and tupdesc must be set up in advance to output to a table.
     105                 :             :  */
     106                 :             : typedef struct
     107                 :             : {
     108                 :             :         ArrayBuildState *astate;
     109                 :             :         Tuplestorestate *tupstore;
     110                 :             :         TupleDesc       tupdesc;
     111                 :             : } SplitTextOutputData;
     112                 :             : 
     113                 :             : /*
     114                 :             :  * This should be large enough that most strings will fit, but small enough
     115                 :             :  * that we feel comfortable putting it on the stack
     116                 :             :  */
     117                 :             : #define TEXTBUFLEN              1024
     118                 :             : 
     119                 :             : #define DatumGetVarStringP(X)           ((VarString *) PG_DETOAST_DATUM(X))
     120                 :             : #define DatumGetVarStringPP(X)          ((VarString *) PG_DETOAST_DATUM_PACKED(X))
     121                 :             : 
     122                 :             : static int      varstrfastcmp_c(Datum x, Datum y, SortSupport ssup);
     123                 :             : static int      bpcharfastcmp_c(Datum x, Datum y, SortSupport ssup);
     124                 :             : static int      namefastcmp_c(Datum x, Datum y, SortSupport ssup);
     125                 :             : static int      varlenafastcmp_locale(Datum x, Datum y, SortSupport ssup);
     126                 :             : static int      namefastcmp_locale(Datum x, Datum y, SortSupport ssup);
     127                 :             : static int      varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup);
     128                 :             : static Datum varstr_abbrev_convert(Datum original, SortSupport ssup);
     129                 :             : static bool varstr_abbrev_abort(int memtupcount, SortSupport ssup);
     130                 :             : static int32 text_length(Datum str);
     131                 :             : static text *text_catenate(text *t1, text *t2);
     132                 :             : static text *text_substring(Datum str,
     133                 :             :                                                         int32 start,
     134                 :             :                                                         int32 length,
     135                 :             :                                                         bool length_not_specified);
     136                 :             : static text *text_overlay(text *t1, text *t2, int sp, int sl);
     137                 :             : static int      text_position(text *t1, text *t2, Oid collid);
     138                 :             : static void text_position_setup(text *t1, text *t2, Oid collid, TextPositionState *state);
     139                 :             : static bool text_position_next(TextPositionState *state);
     140                 :             : static char *text_position_next_internal(char *start_ptr, TextPositionState *state);
     141                 :             : static char *text_position_get_match_ptr(TextPositionState *state);
     142                 :             : static int      text_position_get_match_pos(TextPositionState *state);
     143                 :             : static void text_position_cleanup(TextPositionState *state);
     144                 :             : static void check_collation_set(Oid collid);
     145                 :             : static int      text_cmp(text *arg1, text *arg2, Oid collid);
     146                 :             : static void appendStringInfoText(StringInfo str, const text *t);
     147                 :             : static bool split_text(FunctionCallInfo fcinfo, SplitTextOutputData *tstate);
     148                 :             : static void split_text_accum_result(SplitTextOutputData *tstate,
     149                 :             :                                                                         text *field_value,
     150                 :             :                                                                         text *null_string,
     151                 :             :                                                                         Oid collation);
     152                 :             : static text *array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
     153                 :             :                                                                         const char *fldsep, const char *null_string);
     154                 :             : static StringInfo makeStringAggState(FunctionCallInfo fcinfo);
     155                 :             : static bool text_format_parse_digits(const char **ptr, const char *end_ptr,
     156                 :             :                                                                          int *value);
     157                 :             : static const char *text_format_parse_format(const char *start_ptr,
     158                 :             :                                                                                         const char *end_ptr,
     159                 :             :                                                                                         int *argpos, int *widthpos,
     160                 :             :                                                                                         int *flags, int *width);
     161                 :             : static void text_format_string_conversion(StringInfo buf, char conversion,
     162                 :             :                                                                                   FmgrInfo *typOutputInfo,
     163                 :             :                                                                                   Datum value, bool isNull,
     164                 :             :                                                                                   int flags, int width);
     165                 :             : static void text_format_append_string(StringInfo buf, const char *str,
     166                 :             :                                                                           int flags, int width);
     167                 :             : 
     168                 :             : 
     169                 :             : /*****************************************************************************
     170                 :             :  *       CONVERSION ROUTINES EXPORTED FOR USE BY C CODE                                                  *
     171                 :             :  *****************************************************************************/
     172                 :             : 
     173                 :             : /*
     174                 :             :  * cstring_to_text
     175                 :             :  *
     176                 :             :  * Create a text value from a null-terminated C string.
     177                 :             :  *
     178                 :             :  * The new text value is freshly palloc'd with a full-size VARHDR.
     179                 :             :  */
     180                 :             : text *
     181                 :     1863370 : cstring_to_text(const char *s)
     182                 :             : {
     183                 :     1863370 :         return cstring_to_text_with_len(s, strlen(s));
     184                 :             : }
     185                 :             : 
     186                 :             : /*
     187                 :             :  * cstring_to_text_with_len
     188                 :             :  *
     189                 :             :  * Same as cstring_to_text except the caller specifies the string length;
     190                 :             :  * the string need not be null_terminated.
     191                 :             :  */
     192                 :             : text *
     193                 :     2000548 : cstring_to_text_with_len(const char *s, int len)
     194                 :             : {
     195                 :     2000548 :         text       *result = (text *) palloc(len + VARHDRSZ);
     196                 :             : 
     197                 :     2000548 :         SET_VARSIZE(result, len + VARHDRSZ);
     198                 :     2000548 :         memcpy(VARDATA(result), s, len);
     199                 :             : 
     200                 :     4001096 :         return result;
     201                 :     2000548 : }
     202                 :             : 
     203                 :             : /*
     204                 :             :  * text_to_cstring
     205                 :             :  *
     206                 :             :  * Create a palloc'd, null-terminated C string from a text value.
     207                 :             :  *
     208                 :             :  * We support being passed a compressed or toasted text value.
     209                 :             :  * This is a bit bogus since such values shouldn't really be referred to as
     210                 :             :  * "text *", but it seems useful for robustness.  If we didn't handle that
     211                 :             :  * case here, we'd need another routine that did, anyway.
     212                 :             :  */
     213                 :             : char *
     214                 :      640280 : text_to_cstring(const text *t)
     215                 :             : {
     216                 :             :         /* must cast away the const, unfortunately */
     217                 :      640280 :         text       *tunpacked = pg_detoast_datum_packed(unconstify(text *, t));
     218                 :      640280 :         int                     len = VARSIZE_ANY_EXHDR(tunpacked);
     219                 :      640280 :         char       *result;
     220                 :             : 
     221                 :      640280 :         result = (char *) palloc(len + 1);
     222                 :      640280 :         memcpy(result, VARDATA_ANY(tunpacked), len);
     223                 :      640280 :         result[len] = '\0';
     224                 :             : 
     225         [ +  + ]:      640280 :         if (tunpacked != t)
     226                 :        2612 :                 pfree(tunpacked);
     227                 :             : 
     228                 :     1280560 :         return result;
     229                 :      640280 : }
     230                 :             : 
     231                 :             : /*
     232                 :             :  * text_to_cstring_buffer
     233                 :             :  *
     234                 :             :  * Copy a text value into a caller-supplied buffer of size dst_len.
     235                 :             :  *
     236                 :             :  * The text string is truncated if necessary to fit.  The result is
     237                 :             :  * guaranteed null-terminated (unless dst_len == 0).
     238                 :             :  *
     239                 :             :  * We support being passed a compressed or toasted text value.
     240                 :             :  * This is a bit bogus since such values shouldn't really be referred to as
     241                 :             :  * "text *", but it seems useful for robustness.  If we didn't handle that
     242                 :             :  * case here, we'd need another routine that did, anyway.
     243                 :             :  */
     244                 :             : void
     245                 :         163 : text_to_cstring_buffer(const text *src, char *dst, size_t dst_len)
     246                 :             : {
     247                 :             :         /* must cast away the const, unfortunately */
     248                 :         163 :         text       *srcunpacked = pg_detoast_datum_packed(unconstify(text *, src));
     249                 :         163 :         size_t          src_len = VARSIZE_ANY_EXHDR(srcunpacked);
     250                 :             : 
     251         [ -  + ]:         163 :         if (dst_len > 0)
     252                 :             :         {
     253                 :         163 :                 dst_len--;
     254         [ +  - ]:         163 :                 if (dst_len >= src_len)
     255                 :         163 :                         dst_len = src_len;
     256                 :             :                 else                                    /* ensure truncation is encoding-safe */
     257                 :           0 :                         dst_len = pg_mbcliplen(VARDATA_ANY(srcunpacked), src_len, dst_len);
     258                 :         163 :                 memcpy(dst, VARDATA_ANY(srcunpacked), dst_len);
     259                 :         163 :                 dst[dst_len] = '\0';
     260                 :         163 :         }
     261                 :             : 
     262         [ +  - ]:         163 :         if (srcunpacked != src)
     263                 :           0 :                 pfree(srcunpacked);
     264                 :         163 : }
     265                 :             : 
     266                 :             : 
     267                 :             : /*****************************************************************************
     268                 :             :  *       USER I/O ROUTINES                                                                                                               *
     269                 :             :  *****************************************************************************/
     270                 :             : 
     271                 :             : /*
     272                 :             :  *              textin                  - converts cstring to internal representation
     273                 :             :  */
     274                 :             : Datum
     275                 :      599708 : textin(PG_FUNCTION_ARGS)
     276                 :             : {
     277                 :      599708 :         char       *inputText = PG_GETARG_CSTRING(0);
     278                 :             : 
     279                 :     1199416 :         PG_RETURN_TEXT_P(cstring_to_text(inputText));
     280                 :      599708 : }
     281                 :             : 
     282                 :             : /*
     283                 :             :  *              textout                 - converts internal representation to cstring
     284                 :             :  */
     285                 :             : Datum
     286                 :      250571 : textout(PG_FUNCTION_ARGS)
     287                 :             : {
     288                 :      250571 :         Datum           txt = PG_GETARG_DATUM(0);
     289                 :             : 
     290                 :      501142 :         PG_RETURN_CSTRING(TextDatumGetCString(txt));
     291                 :      250571 : }
     292                 :             : 
     293                 :             : /*
     294                 :             :  *              textrecv                        - converts external binary format to text
     295                 :             :  */
     296                 :             : Datum
     297                 :           3 : textrecv(PG_FUNCTION_ARGS)
     298                 :             : {
     299                 :           3 :         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
     300                 :           3 :         text       *result;
     301                 :           3 :         char       *str;
     302                 :           3 :         int                     nbytes;
     303                 :             : 
     304                 :           3 :         str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     305                 :             : 
     306                 :           3 :         result = cstring_to_text_with_len(str, nbytes);
     307                 :           3 :         pfree(str);
     308                 :           6 :         PG_RETURN_TEXT_P(result);
     309                 :           3 : }
     310                 :             : 
     311                 :             : /*
     312                 :             :  *              textsend                        - converts text to binary format
     313                 :             :  */
     314                 :             : Datum
     315                 :           3 : textsend(PG_FUNCTION_ARGS)
     316                 :             : {
     317                 :           3 :         text       *t = PG_GETARG_TEXT_PP(0);
     318                 :           3 :         StringInfoData buf;
     319                 :             : 
     320                 :           3 :         pq_begintypsend(&buf);
     321                 :           3 :         pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
     322                 :           6 :         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     323                 :           3 : }
     324                 :             : 
     325                 :             : 
     326                 :             : /*
     327                 :             :  *              unknownin                       - converts cstring to internal representation
     328                 :             :  */
     329                 :             : Datum
     330                 :           0 : unknownin(PG_FUNCTION_ARGS)
     331                 :             : {
     332                 :           0 :         char       *str = PG_GETARG_CSTRING(0);
     333                 :             : 
     334                 :             :         /* representation is same as cstring */
     335                 :           0 :         PG_RETURN_CSTRING(pstrdup(str));
     336                 :           0 : }
     337                 :             : 
     338                 :             : /*
     339                 :             :  *              unknownout                      - converts internal representation to cstring
     340                 :             :  */
     341                 :             : Datum
     342                 :         149 : unknownout(PG_FUNCTION_ARGS)
     343                 :             : {
     344                 :             :         /* representation is same as cstring */
     345                 :         149 :         char       *str = PG_GETARG_CSTRING(0);
     346                 :             : 
     347                 :         298 :         PG_RETURN_CSTRING(pstrdup(str));
     348                 :         149 : }
     349                 :             : 
     350                 :             : /*
     351                 :             :  *              unknownrecv                     - converts external binary format to unknown
     352                 :             :  */
     353                 :             : Datum
     354                 :           0 : unknownrecv(PG_FUNCTION_ARGS)
     355                 :             : {
     356                 :           0 :         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
     357                 :           0 :         char       *str;
     358                 :           0 :         int                     nbytes;
     359                 :             : 
     360                 :           0 :         str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     361                 :             :         /* representation is same as cstring */
     362                 :           0 :         PG_RETURN_CSTRING(str);
     363                 :           0 : }
     364                 :             : 
     365                 :             : /*
     366                 :             :  *              unknownsend                     - converts unknown to binary format
     367                 :             :  */
     368                 :             : Datum
     369                 :           0 : unknownsend(PG_FUNCTION_ARGS)
     370                 :             : {
     371                 :             :         /* representation is same as cstring */
     372                 :           0 :         char       *str = PG_GETARG_CSTRING(0);
     373                 :           0 :         StringInfoData buf;
     374                 :             : 
     375                 :           0 :         pq_begintypsend(&buf);
     376                 :           0 :         pq_sendtext(&buf, str, strlen(str));
     377                 :           0 :         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     378                 :           0 : }
     379                 :             : 
     380                 :             : 
     381                 :             : /* ========== PUBLIC ROUTINES ========== */
     382                 :             : 
     383                 :             : /*
     384                 :             :  * textlen -
     385                 :             :  *        returns the logical length of a text*
     386                 :             :  *         (which is less than the VARSIZE of the text*)
     387                 :             :  */
     388                 :             : Datum
     389                 :       70637 : textlen(PG_FUNCTION_ARGS)
     390                 :             : {
     391                 :       70637 :         Datum           str = PG_GETARG_DATUM(0);
     392                 :             : 
     393                 :             :         /* try to avoid decompressing argument */
     394                 :      141274 :         PG_RETURN_INT32(text_length(str));
     395                 :       70637 : }
     396                 :             : 
     397                 :             : /*
     398                 :             :  * text_length -
     399                 :             :  *      Does the real work for textlen()
     400                 :             :  *
     401                 :             :  *      This is broken out so it can be called directly by other string processing
     402                 :             :  *      functions.  Note that the argument is passed as a Datum, to indicate that
     403                 :             :  *      it may still be in compressed form.  We can avoid decompressing it at all
     404                 :             :  *      in some cases.
     405                 :             :  */
     406                 :             : static int32
     407                 :       70639 : text_length(Datum str)
     408                 :             : {
     409                 :             :         /* fastpath when max encoding length is one */
     410         [ -  + ]:       70639 :         if (pg_database_encoding_max_length() == 1)
     411                 :           0 :                 return (toast_raw_datum_size(str) - VARHDRSZ);
     412                 :             :         else
     413                 :             :         {
     414                 :       70639 :                 text       *t = DatumGetTextPP(str);
     415                 :             : 
     416                 :       70639 :                 return (pg_mbstrlen_with_len(VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t)));
     417                 :       70639 :         }
     418                 :       70639 : }
     419                 :             : 
     420                 :             : /*
     421                 :             :  * textoctetlen -
     422                 :             :  *        returns the physical length of a text*
     423                 :             :  *         (which is less than the VARSIZE of the text*)
     424                 :             :  */
     425                 :             : Datum
     426                 :           6 : textoctetlen(PG_FUNCTION_ARGS)
     427                 :             : {
     428                 :           6 :         Datum           str = PG_GETARG_DATUM(0);
     429                 :             : 
     430                 :             :         /* We need not detoast the input at all */
     431                 :          12 :         PG_RETURN_INT32(toast_raw_datum_size(str) - VARHDRSZ);
     432                 :           6 : }
     433                 :             : 
     434                 :             : /*
     435                 :             :  * textcat -
     436                 :             :  *        takes two text* and returns a text* that is the concatenation of
     437                 :             :  *        the two.
     438                 :             :  *
     439                 :             :  * Rewritten by Sapa, sapa@hq.icb.chel.su. 8-Jul-96.
     440                 :             :  * Updated by Thomas, Thomas.Lockhart@jpl.nasa.gov 1997-07-10.
     441                 :             :  * Allocate space for output in all cases.
     442                 :             :  * XXX - thomas 1997-07-10
     443                 :             :  */
     444                 :             : Datum
     445                 :      325246 : textcat(PG_FUNCTION_ARGS)
     446                 :             : {
     447                 :      325246 :         text       *t1 = PG_GETARG_TEXT_PP(0);
     448                 :      325246 :         text       *t2 = PG_GETARG_TEXT_PP(1);
     449                 :             : 
     450                 :      650492 :         PG_RETURN_TEXT_P(text_catenate(t1, t2));
     451                 :      325246 : }
     452                 :             : 
     453                 :             : /*
     454                 :             :  * text_catenate
     455                 :             :  *      Guts of textcat(), broken out so it can be used by other functions
     456                 :             :  *
     457                 :             :  * Arguments can be in short-header form, but not compressed or out-of-line
     458                 :             :  */
     459                 :             : static text *
     460                 :      325254 : text_catenate(text *t1, text *t2)
     461                 :             : {
     462                 :      325254 :         text       *result;
     463                 :      325254 :         int                     len1,
     464                 :             :                                 len2,
     465                 :             :                                 len;
     466                 :      325254 :         char       *ptr;
     467                 :             : 
     468                 :      325254 :         len1 = VARSIZE_ANY_EXHDR(t1);
     469                 :      325254 :         len2 = VARSIZE_ANY_EXHDR(t2);
     470                 :             : 
     471                 :             :         /* paranoia ... probably should throw error instead? */
     472         [ +  - ]:      325254 :         if (len1 < 0)
     473                 :           0 :                 len1 = 0;
     474         [ +  - ]:      325254 :         if (len2 < 0)
     475                 :           0 :                 len2 = 0;
     476                 :             : 
     477                 :      325254 :         len = len1 + len2 + VARHDRSZ;
     478                 :      325254 :         result = (text *) palloc(len);
     479                 :             : 
     480                 :             :         /* Set size of result string... */
     481                 :      325254 :         SET_VARSIZE(result, len);
     482                 :             : 
     483                 :             :         /* Fill data field of result string... */
     484                 :      325254 :         ptr = VARDATA(result);
     485         [ +  + ]:      325254 :         if (len1 > 0)
     486                 :      325138 :                 memcpy(ptr, VARDATA_ANY(t1), len1);
     487         [ +  + ]:      325254 :         if (len2 > 0)
     488                 :      325223 :                 memcpy(ptr + len1, VARDATA_ANY(t2), len2);
     489                 :             : 
     490                 :      650508 :         return result;
     491                 :      325254 : }
     492                 :             : 
     493                 :             : /*
     494                 :             :  * charlen_to_bytelen()
     495                 :             :  *      Compute the number of bytes occupied by n characters starting at *p
     496                 :             :  *
     497                 :             :  * It is caller's responsibility that there actually are n characters;
     498                 :             :  * the string need not be null-terminated.
     499                 :             :  */
     500                 :             : static int
     501                 :           0 : charlen_to_bytelen(const char *p, int n)
     502                 :             : {
     503         [ #  # ]:           0 :         if (pg_database_encoding_max_length() == 1)
     504                 :             :         {
     505                 :             :                 /* Optimization for single-byte encodings */
     506                 :           0 :                 return n;
     507                 :             :         }
     508                 :             :         else
     509                 :             :         {
     510                 :           0 :                 const char *s;
     511                 :             : 
     512         [ #  # ]:           0 :                 for (s = p; n > 0; n--)
     513                 :           0 :                         s += pg_mblen(s);
     514                 :             : 
     515                 :           0 :                 return s - p;
     516                 :           0 :         }
     517                 :           0 : }
     518                 :             : 
     519                 :             : /*
     520                 :             :  * text_substr()
     521                 :             :  * Return a substring starting at the specified position.
     522                 :             :  * - thomas 1997-12-31
     523                 :             :  *
     524                 :             :  * Input:
     525                 :             :  *      - string
     526                 :             :  *      - starting position (is one-based)
     527                 :             :  *      - string length
     528                 :             :  *
     529                 :             :  * If the starting position is zero or less, then return from the start of the string
     530                 :             :  *      adjusting the length to be consistent with the "negative start" per SQL.
     531                 :             :  * If the length is less than zero, return the remaining string.
     532                 :             :  *
     533                 :             :  * Added multibyte support.
     534                 :             :  * - Tatsuo Ishii 1998-4-21
     535                 :             :  * Changed behavior if starting position is less than one to conform to SQL behavior.
     536                 :             :  * Formerly returned the entire string; now returns a portion.
     537                 :             :  * - Thomas Lockhart 1998-12-10
     538                 :             :  * Now uses faster TOAST-slicing interface
     539                 :             :  * - John Gray 2002-02-22
     540                 :             :  * Remove "#ifdef MULTIBYTE" and test for encoding_max_length instead. Change
     541                 :             :  * behaviors conflicting with SQL to meet SQL (if E = S + L < S throw
     542                 :             :  * error; if E < 1, return '', not entire string). Fixed MB related bug when
     543                 :             :  * S > LC and < LC + 4 sometimes garbage characters are returned.
     544                 :             :  * - Joe Conway 2002-08-10
     545                 :             :  */
     546                 :             : Datum
     547                 :       36593 : text_substr(PG_FUNCTION_ARGS)
     548                 :             : {
     549                 :       36593 :         PG_RETURN_TEXT_P(text_substring(PG_GETARG_DATUM(0),
     550                 :             :                                                                         PG_GETARG_INT32(1),
     551                 :             :                                                                         PG_GETARG_INT32(2),
     552                 :             :                                                                         false));
     553                 :             : }
     554                 :             : 
     555                 :             : /*
     556                 :             :  * text_substr_no_len -
     557                 :             :  *        Wrapper to avoid opr_sanity failure due to
     558                 :             :  *        one function accepting a different number of args.
     559                 :             :  */
     560                 :             : Datum
     561                 :           5 : text_substr_no_len(PG_FUNCTION_ARGS)
     562                 :             : {
     563                 :           5 :         PG_RETURN_TEXT_P(text_substring(PG_GETARG_DATUM(0),
     564                 :             :                                                                         PG_GETARG_INT32(1),
     565                 :             :                                                                         -1, true));
     566                 :             : }
     567                 :             : 
     568                 :             : /*
     569                 :             :  * text_substring -
     570                 :             :  *      Does the real work for text_substr() and text_substr_no_len()
     571                 :             :  *
     572                 :             :  *      This is broken out so it can be called directly by other string processing
     573                 :             :  *      functions.  Note that the argument is passed as a Datum, to indicate that
     574                 :             :  *      it may still be in compressed/toasted form.  We can avoid detoasting all
     575                 :             :  *      of it in some cases.
     576                 :             :  *
     577                 :             :  *      The result is always a freshly palloc'd datum.
     578                 :             :  */
     579                 :             : static text *
     580                 :       43278 : text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
     581                 :             : {
     582                 :       43278 :         int32           eml = pg_database_encoding_max_length();
     583                 :       43278 :         int32           S = start;              /* start position */
     584                 :       43278 :         int32           S1;                             /* adjusted start position */
     585                 :       43278 :         int32           L1;                             /* adjusted substring length */
     586                 :       43278 :         int32           E;                              /* end position */
     587                 :             : 
     588                 :             :         /*
     589                 :             :          * SQL99 says S can be zero or negative (which we don't document), but we
     590                 :             :          * still must fetch from the start of the string.
     591                 :             :          * https://www.postgresql.org/message-id/170905442373.643.11536838320909376197%40wrigleys.postgresql.org
     592                 :             :          */
     593         [ +  + ]:       43278 :         S1 = Max(S, 1);
     594                 :             : 
     595                 :             :         /* life is easy if the encoding max length is 1 */
     596         [ -  + ]:       43278 :         if (eml == 1)
     597                 :             :         {
     598         [ #  # ]:           0 :                 if (length_not_specified)       /* special case - get length to end of
     599                 :             :                                                                          * string */
     600                 :           0 :                         L1 = -1;
     601         [ #  # ]:           0 :                 else if (length < 0)
     602                 :             :                 {
     603                 :             :                         /* SQL99 says to throw an error for E < S, i.e., negative length */
     604   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     605                 :             :                                         (errcode(ERRCODE_SUBSTRING_ERROR),
     606                 :             :                                          errmsg("negative substring length not allowed")));
     607                 :           0 :                         L1 = -1;                        /* silence stupider compilers */
     608                 :           0 :                 }
     609         [ #  # ]:           0 :                 else if (pg_add_s32_overflow(S, length, &E))
     610                 :             :                 {
     611                 :             :                         /*
     612                 :             :                          * L could be large enough for S + L to overflow, in which case
     613                 :             :                          * the substring must run to end of string.
     614                 :             :                          */
     615                 :           0 :                         L1 = -1;
     616                 :           0 :                 }
     617                 :             :                 else
     618                 :             :                 {
     619                 :             :                         /*
     620                 :             :                          * A zero or negative value for the end position can happen if the
     621                 :             :                          * start was negative or one. SQL99 says to return a zero-length
     622                 :             :                          * string.
     623                 :             :                          */
     624         [ #  # ]:           0 :                         if (E < 1)
     625                 :           0 :                                 return cstring_to_text("");
     626                 :             : 
     627                 :           0 :                         L1 = E - S1;
     628                 :             :                 }
     629                 :             : 
     630                 :             :                 /*
     631                 :             :                  * If the start position is past the end of the string, SQL99 says to
     632                 :             :                  * return a zero-length string -- DatumGetTextPSlice() will do that
     633                 :             :                  * for us.  We need only convert S1 to zero-based starting position.
     634                 :             :                  */
     635                 :           0 :                 return DatumGetTextPSlice(str, S1 - 1, L1);
     636                 :             :         }
     637         [ +  - ]:       43278 :         else if (eml > 1)
     638                 :             :         {
     639                 :             :                 /*
     640                 :             :                  * When encoding max length is > 1, we can't get LC without
     641                 :             :                  * detoasting, so we'll grab a conservatively large slice now and go
     642                 :             :                  * back later to do the right thing
     643                 :             :                  */
     644                 :       43278 :                 int32           slice_start;
     645                 :       43278 :                 int32           slice_size;
     646                 :       43278 :                 int32           slice_strlen;
     647                 :       43278 :                 text       *slice;
     648                 :       43278 :                 int32           E1;
     649                 :       43278 :                 int32           i;
     650                 :       43278 :                 char       *p;
     651                 :       43278 :                 char       *s;
     652                 :       43278 :                 text       *ret;
     653                 :             : 
     654                 :             :                 /*
     655                 :             :                  * We need to start at position zero because there is no way to know
     656                 :             :                  * in advance which byte offset corresponds to the supplied start
     657                 :             :                  * position.
     658                 :             :                  */
     659                 :       43278 :                 slice_start = 0;
     660                 :             : 
     661         [ +  + ]:       43278 :                 if (length_not_specified)       /* special case - get length to end of
     662                 :             :                                                                          * string */
     663                 :           9 :                         slice_size = L1 = -1;
     664         [ +  + ]:       43269 :                 else if (length < 0)
     665                 :             :                 {
     666                 :             :                         /* SQL99 says to throw an error for E < S, i.e., negative length */
     667   [ +  -  +  - ]:           2 :                         ereport(ERROR,
     668                 :             :                                         (errcode(ERRCODE_SUBSTRING_ERROR),
     669                 :             :                                          errmsg("negative substring length not allowed")));
     670                 :           0 :                         slice_size = L1 = -1;   /* silence stupider compilers */
     671                 :           0 :                 }
     672         [ +  + ]:       43267 :                 else if (pg_add_s32_overflow(S, length, &E))
     673                 :             :                 {
     674                 :             :                         /*
     675                 :             :                          * L could be large enough for S + L to overflow, in which case
     676                 :             :                          * the substring must run to end of string.
     677                 :             :                          */
     678                 :           1 :                         slice_size = L1 = -1;
     679                 :           1 :                 }
     680                 :             :                 else
     681                 :             :                 {
     682                 :             :                         /*
     683                 :             :                          * A zero or negative value for the end position can happen if the
     684                 :             :                          * start was negative or one. SQL99 says to return a zero-length
     685                 :             :                          * string.
     686                 :             :                          */
     687         [ +  - ]:       43266 :                         if (E < 1)
     688                 :           0 :                                 return cstring_to_text("");
     689                 :             : 
     690                 :             :                         /*
     691                 :             :                          * if E is past the end of the string, the tuple toaster will
     692                 :             :                          * truncate the length for us
     693                 :             :                          */
     694                 :       43266 :                         L1 = E - S1;
     695                 :             : 
     696                 :             :                         /*
     697                 :             :                          * Total slice size in bytes can't be any longer than the start
     698                 :             :                          * position plus substring length times the encoding max length.
     699                 :             :                          * If that overflows, we can just use -1.
     700                 :             :                          */
     701         [ +  + ]:       43266 :                         if (pg_mul_s32_overflow(E, eml, &slice_size))
     702                 :           1 :                                 slice_size = -1;
     703                 :             :                 }
     704                 :             : 
     705                 :             :                 /*
     706                 :             :                  * If we're working with an untoasted source, no need to do an extra
     707                 :             :                  * copying step.
     708                 :             :                  */
     709   [ +  +  +  + ]:       43276 :                 if (VARATT_IS_COMPRESSED(DatumGetPointer(str)) ||
     710                 :       43265 :                         VARATT_IS_EXTERNAL(DatumGetPointer(str)))
     711                 :          30 :                         slice = DatumGetTextPSlice(str, slice_start, slice_size);
     712                 :             :                 else
     713                 :       43246 :                         slice = (text *) DatumGetPointer(str);
     714                 :             : 
     715                 :             :                 /* see if we got back an empty string */
     716         [ +  - ]:       43276 :                 if (VARSIZE_ANY_EXHDR(slice) == 0)
     717                 :             :                 {
     718         [ #  # ]:           0 :                         if (slice != (text *) DatumGetPointer(str))
     719                 :           0 :                                 pfree(slice);
     720                 :           0 :                         return cstring_to_text("");
     721                 :             :                 }
     722                 :             : 
     723                 :             :                 /* Now we can get the actual length of the slice in MB characters */
     724                 :       86552 :                 slice_strlen = pg_mbstrlen_with_len(VARDATA_ANY(slice),
     725                 :       43276 :                                                                                         VARSIZE_ANY_EXHDR(slice));
     726                 :             : 
     727                 :             :                 /*
     728                 :             :                  * Check that the start position wasn't > slice_strlen. If so, SQL99
     729                 :             :                  * says to return a zero-length string.
     730                 :             :                  */
     731         [ +  + ]:       43276 :                 if (S1 > slice_strlen)
     732                 :             :                 {
     733         [ +  - ]:           1 :                         if (slice != (text *) DatumGetPointer(str))
     734                 :           0 :                                 pfree(slice);
     735                 :           1 :                         return cstring_to_text("");
     736                 :             :                 }
     737                 :             : 
     738                 :             :                 /*
     739                 :             :                  * Adjust L1 and E1 now that we know the slice string length. Again
     740                 :             :                  * remember that S1 is one based, and slice_start is zero based.
     741                 :             :                  */
     742         [ +  + ]:       43275 :                 if (L1 > -1)
     743         [ +  + ]:       43266 :                         E1 = Min(S1 + L1, slice_start + 1 + slice_strlen);
     744                 :             :                 else
     745                 :           9 :                         E1 = slice_start + 1 + slice_strlen;
     746                 :             : 
     747                 :             :                 /*
     748                 :             :                  * Find the start position in the slice; remember S1 is not zero based
     749                 :             :                  */
     750                 :       43275 :                 p = VARDATA_ANY(slice);
     751         [ +  + ]:      846863 :                 for (i = 0; i < S1 - 1; i++)
     752                 :      803588 :                         p += pg_mblen(p);
     753                 :             : 
     754                 :             :                 /* hang onto a pointer to our start position */
     755                 :       43275 :                 s = p;
     756                 :             : 
     757                 :             :                 /*
     758                 :             :                  * Count the actual bytes used by the substring of the requested
     759                 :             :                  * length.
     760                 :             :                  */
     761         [ +  + ]:     1239056 :                 for (i = S1; i < E1; i++)
     762                 :     1195781 :                         p += pg_mblen(p);
     763                 :             : 
     764                 :       43275 :                 ret = (text *) palloc(VARHDRSZ + (p - s));
     765                 :       43275 :                 SET_VARSIZE(ret, VARHDRSZ + (p - s));
     766                 :       43275 :                 memcpy(VARDATA(ret), s, (p - s));
     767                 :             : 
     768         [ +  + ]:       43275 :                 if (slice != (text *) DatumGetPointer(str))
     769                 :          30 :                         pfree(slice);
     770                 :             : 
     771                 :       43275 :                 return ret;
     772                 :       43276 :         }
     773                 :             :         else
     774   [ #  #  #  # ]:           0 :                 elog(ERROR, "invalid backend encoding: encoding max length < 1");
     775                 :             : 
     776                 :             :         /* not reached: suppress compiler warning */
     777                 :           0 :         return NULL;
     778                 :       43276 : }
     779                 :             : 
     780                 :             : /*
     781                 :             :  * textoverlay
     782                 :             :  *      Replace specified substring of first string with second
     783                 :             :  *
     784                 :             :  * The SQL standard defines OVERLAY() in terms of substring and concatenation.
     785                 :             :  * This code is a direct implementation of what the standard says.
     786                 :             :  */
     787                 :             : Datum
     788                 :           2 : textoverlay(PG_FUNCTION_ARGS)
     789                 :             : {
     790                 :           2 :         text       *t1 = PG_GETARG_TEXT_PP(0);
     791                 :           2 :         text       *t2 = PG_GETARG_TEXT_PP(1);
     792                 :           2 :         int                     sp = PG_GETARG_INT32(2);        /* substring start position */
     793                 :           2 :         int                     sl = PG_GETARG_INT32(3);        /* substring length */
     794                 :             : 
     795                 :           4 :         PG_RETURN_TEXT_P(text_overlay(t1, t2, sp, sl));
     796                 :           2 : }
     797                 :             : 
     798                 :             : Datum
     799                 :           2 : textoverlay_no_len(PG_FUNCTION_ARGS)
     800                 :             : {
     801                 :           2 :         text       *t1 = PG_GETARG_TEXT_PP(0);
     802                 :           2 :         text       *t2 = PG_GETARG_TEXT_PP(1);
     803                 :           2 :         int                     sp = PG_GETARG_INT32(2);        /* substring start position */
     804                 :           2 :         int                     sl;
     805                 :             : 
     806                 :           2 :         sl = text_length(PointerGetDatum(t2));  /* defaults to length(t2) */
     807                 :           4 :         PG_RETURN_TEXT_P(text_overlay(t1, t2, sp, sl));
     808                 :           2 : }
     809                 :             : 
     810                 :             : static text *
     811                 :           4 : text_overlay(text *t1, text *t2, int sp, int sl)
     812                 :             : {
     813                 :           4 :         text       *result;
     814                 :           4 :         text       *s1;
     815                 :           4 :         text       *s2;
     816                 :           4 :         int                     sp_pl_sl;
     817                 :             : 
     818                 :             :         /*
     819                 :             :          * Check for possible integer-overflow cases.  For negative sp, throw a
     820                 :             :          * "substring length" error because that's what should be expected
     821                 :             :          * according to the spec's definition of OVERLAY().
     822                 :             :          */
     823         [ +  - ]:           4 :         if (sp <= 0)
     824   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     825                 :             :                                 (errcode(ERRCODE_SUBSTRING_ERROR),
     826                 :             :                                  errmsg("negative substring length not allowed")));
     827         [ +  - ]:           4 :         if (pg_add_s32_overflow(sp, sl, &sp_pl_sl))
     828   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     829                 :             :                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     830                 :             :                                  errmsg("integer out of range")));
     831                 :             : 
     832                 :           4 :         s1 = text_substring(PointerGetDatum(t1), 1, sp - 1, false);
     833                 :           4 :         s2 = text_substring(PointerGetDatum(t1), sp_pl_sl, -1, true);
     834                 :           4 :         result = text_catenate(s1, t2);
     835                 :           4 :         result = text_catenate(result, s2);
     836                 :             : 
     837                 :           8 :         return result;
     838                 :           4 : }
     839                 :             : 
     840                 :             : /*
     841                 :             :  * textpos -
     842                 :             :  *        Return the position of the specified substring.
     843                 :             :  *        Implements the SQL POSITION() function.
     844                 :             :  *        Ref: A Guide To The SQL Standard, Date & Darwen, 1997
     845                 :             :  * - thomas 1997-07-27
     846                 :             :  */
     847                 :             : Datum
     848                 :          11 : textpos(PG_FUNCTION_ARGS)
     849                 :             : {
     850                 :          11 :         text       *str = PG_GETARG_TEXT_PP(0);
     851                 :          11 :         text       *search_str = PG_GETARG_TEXT_PP(1);
     852                 :             : 
     853                 :          22 :         PG_RETURN_INT32((int32) text_position(str, search_str, PG_GET_COLLATION()));
     854                 :          11 : }
     855                 :             : 
     856                 :             : /*
     857                 :             :  * text_position -
     858                 :             :  *      Does the real work for textpos()
     859                 :             :  *
     860                 :             :  * Inputs:
     861                 :             :  *              t1 - string to be searched
     862                 :             :  *              t2 - pattern to match within t1
     863                 :             :  * Result:
     864                 :             :  *              Character index of the first matched char, starting from 1,
     865                 :             :  *              or 0 if no match.
     866                 :             :  *
     867                 :             :  *      This is broken out so it can be called directly by other string processing
     868                 :             :  *      functions.
     869                 :             :  */
     870                 :             : static int
     871                 :          11 : text_position(text *t1, text *t2, Oid collid)
     872                 :             : {
     873                 :          11 :         TextPositionState state;
     874                 :          11 :         int                     result;
     875                 :             : 
     876                 :          11 :         check_collation_set(collid);
     877                 :             : 
     878                 :             :         /* Empty needle always matches at position 1 */
     879         [ +  + ]:          11 :         if (VARSIZE_ANY_EXHDR(t2) < 1)
     880                 :           2 :                 return 1;
     881                 :             : 
     882                 :             :         /* Otherwise, can't match if haystack is shorter than needle */
     883   [ +  +  -  + ]:           9 :         if (VARSIZE_ANY_EXHDR(t1) < VARSIZE_ANY_EXHDR(t2) &&
     884                 :           1 :                 pg_newlocale_from_collation(collid)->deterministic)
     885                 :           1 :                 return 0;
     886                 :             : 
     887                 :           8 :         text_position_setup(t1, t2, collid, &state);
     888                 :             :         /* don't need greedy mode here */
     889                 :           8 :         state.greedy = false;
     890                 :             : 
     891         [ +  + ]:           8 :         if (!text_position_next(&state))
     892                 :           1 :                 result = 0;
     893                 :             :         else
     894                 :           7 :                 result = text_position_get_match_pos(&state);
     895                 :           8 :         text_position_cleanup(&state);
     896                 :           8 :         return result;
     897                 :          11 : }
     898                 :             : 
     899                 :             : 
     900                 :             : /*
     901                 :             :  * text_position_setup, text_position_next, text_position_cleanup -
     902                 :             :  *      Component steps of text_position()
     903                 :             :  *
     904                 :             :  * These are broken out so that a string can be efficiently searched for
     905                 :             :  * multiple occurrences of the same pattern.  text_position_next may be
     906                 :             :  * called multiple times, and it advances to the next match on each call.
     907                 :             :  * text_position_get_match_ptr() and text_position_get_match_pos() return
     908                 :             :  * a pointer or 1-based character position of the last match, respectively.
     909                 :             :  *
     910                 :             :  * The "state" variable is normally just a local variable in the caller.
     911                 :             :  *
     912                 :             :  * NOTE: text_position_next skips over the matched portion.  For example,
     913                 :             :  * searching for "xx" in "xxx" returns only one match, not two.
     914                 :             :  */
     915                 :             : 
     916                 :             : static void
     917                 :           0 : text_position_setup(text *t1, text *t2, Oid collid, TextPositionState *state)
     918                 :             : {
     919                 :           0 :         int                     len1 = VARSIZE_ANY_EXHDR(t1);
     920                 :           0 :         int                     len2 = VARSIZE_ANY_EXHDR(t2);
     921                 :             : 
     922                 :           0 :         check_collation_set(collid);
     923                 :             : 
     924                 :           0 :         state->locale = pg_newlocale_from_collation(collid);
     925                 :             : 
     926                 :             :         /*
     927                 :             :          * Most callers need greedy mode, but some might want to unset this to
     928                 :             :          * optimize.
     929                 :             :          */
     930                 :           0 :         state->greedy = true;
     931                 :             : 
     932         [ #  # ]:           0 :         Assert(len2 > 0);
     933                 :             : 
     934                 :             :         /*
     935                 :             :          * Even with a multi-byte encoding, we perform the search using the raw
     936                 :             :          * byte sequence, ignoring multibyte issues.  For UTF-8, that works fine,
     937                 :             :          * because in UTF-8 the byte sequence of one character cannot contain
     938                 :             :          * another character.  For other multi-byte encodings, we do the search
     939                 :             :          * initially as a simple byte search, ignoring multibyte issues, but
     940                 :             :          * verify afterwards that the match we found is at a character boundary,
     941                 :             :          * and continue the search if it was a false match.
     942                 :             :          */
     943         [ #  # ]:           0 :         if (pg_database_encoding_max_length() == 1)
     944                 :           0 :                 state->is_multibyte_char_in_char = false;
     945         [ #  # ]:           0 :         else if (GetDatabaseEncoding() == PG_UTF8)
     946                 :           0 :                 state->is_multibyte_char_in_char = false;
     947                 :             :         else
     948                 :           0 :                 state->is_multibyte_char_in_char = true;
     949                 :             : 
     950                 :           0 :         state->str1 = VARDATA_ANY(t1);
     951                 :           0 :         state->str2 = VARDATA_ANY(t2);
     952                 :           0 :         state->len1 = len1;
     953                 :           0 :         state->len2 = len2;
     954                 :           0 :         state->last_match = NULL;
     955                 :           0 :         state->refpoint = state->str1;
     956                 :           0 :         state->refpos = 0;
     957                 :             : 
     958                 :             :         /*
     959                 :             :          * Prepare the skip table for Boyer-Moore-Horspool searching.  In these
     960                 :             :          * notes we use the terminology that the "haystack" is the string to be
     961                 :             :          * searched (t1) and the "needle" is the pattern being sought (t2).
     962                 :             :          *
     963                 :             :          * If the needle is empty or bigger than the haystack then there is no
     964                 :             :          * point in wasting cycles initializing the table.  We also choose not to
     965                 :             :          * use B-M-H for needles of length 1, since the skip table can't possibly
     966                 :             :          * save anything in that case.
     967                 :             :          *
     968                 :             :          * (With nondeterministic collations, the search is already
     969                 :             :          * multibyte-aware, so we don't need this.)
     970                 :             :          */
     971   [ #  #  #  #  :           0 :         if (len1 >= len2 && len2 > 1 && state->locale->deterministic)
                   #  # ]
     972                 :             :         {
     973                 :           0 :                 int                     searchlength = len1 - len2;
     974                 :           0 :                 int                     skiptablemask;
     975                 :           0 :                 int                     last;
     976                 :           0 :                 int                     i;
     977                 :           0 :                 const char *str2 = state->str2;
     978                 :             : 
     979                 :             :                 /*
     980                 :             :                  * First we must determine how much of the skip table to use.  The
     981                 :             :                  * declaration of TextPositionState allows up to 256 elements, but for
     982                 :             :                  * short search problems we don't really want to have to initialize so
     983                 :             :                  * many elements --- it would take too long in comparison to the
     984                 :             :                  * actual search time.  So we choose a useful skip table size based on
     985                 :             :                  * the haystack length minus the needle length.  The closer the needle
     986                 :             :                  * length is to the haystack length the less useful skipping becomes.
     987                 :             :                  *
     988                 :             :                  * Note: since we use bit-masking to select table elements, the skip
     989                 :             :                  * table size MUST be a power of 2, and so the mask must be 2^N-1.
     990                 :             :                  */
     991         [ #  # ]:           0 :                 if (searchlength < 16)
     992                 :           0 :                         skiptablemask = 3;
     993         [ #  # ]:           0 :                 else if (searchlength < 64)
     994                 :           0 :                         skiptablemask = 7;
     995         [ #  # ]:           0 :                 else if (searchlength < 128)
     996                 :           0 :                         skiptablemask = 15;
     997         [ #  # ]:           0 :                 else if (searchlength < 512)
     998                 :           0 :                         skiptablemask = 31;
     999         [ #  # ]:           0 :                 else if (searchlength < 2048)
    1000                 :           0 :                         skiptablemask = 63;
    1001         [ #  # ]:           0 :                 else if (searchlength < 4096)
    1002                 :           0 :                         skiptablemask = 127;
    1003                 :             :                 else
    1004                 :           0 :                         skiptablemask = 255;
    1005                 :           0 :                 state->skiptablemask = skiptablemask;
    1006                 :             : 
    1007                 :             :                 /*
    1008                 :             :                  * Initialize the skip table.  We set all elements to the needle
    1009                 :             :                  * length, since this is the correct skip distance for any character
    1010                 :             :                  * not found in the needle.
    1011                 :             :                  */
    1012         [ #  # ]:           0 :                 for (i = 0; i <= skiptablemask; i++)
    1013                 :           0 :                         state->skiptable[i] = len2;
    1014                 :             : 
    1015                 :             :                 /*
    1016                 :             :                  * Now examine the needle.  For each character except the last one,
    1017                 :             :                  * set the corresponding table element to the appropriate skip
    1018                 :             :                  * distance.  Note that when two characters share the same skip table
    1019                 :             :                  * entry, the one later in the needle must determine the skip
    1020                 :             :                  * distance.
    1021                 :             :                  */
    1022                 :           0 :                 last = len2 - 1;
    1023                 :             : 
    1024         [ #  # ]:           0 :                 for (i = 0; i < last; i++)
    1025                 :           0 :                         state->skiptable[(unsigned char) str2[i] & skiptablemask] = last - i;
    1026                 :           0 :         }
    1027                 :           0 : }
    1028                 :             : 
    1029                 :             : /*
    1030                 :             :  * Advance to the next match, starting from the end of the previous match
    1031                 :             :  * (or the beginning of the string, on first call).  Returns true if a match
    1032                 :             :  * is found.
    1033                 :             :  *
    1034                 :             :  * Note that this refuses to match an empty-string needle.  Most callers
    1035                 :             :  * will have handled that case specially and we'll never see it here.
    1036                 :             :  */
    1037                 :             : static bool
    1038                 :           0 : text_position_next(TextPositionState *state)
    1039                 :             : {
    1040                 :           0 :         int                     needle_len = state->len2;
    1041                 :           0 :         char       *start_ptr;
    1042                 :           0 :         char       *matchptr;
    1043                 :             : 
    1044         [ #  # ]:           0 :         if (needle_len <= 0)
    1045                 :           0 :                 return false;                   /* result for empty pattern */
    1046                 :             : 
    1047                 :             :         /* Start from the point right after the previous match. */
    1048         [ #  # ]:           0 :         if (state->last_match)
    1049                 :           0 :                 start_ptr = state->last_match + state->last_match_len;
    1050                 :             :         else
    1051                 :           0 :                 start_ptr = state->str1;
    1052                 :             : 
    1053                 :             : retry:
    1054                 :           0 :         matchptr = text_position_next_internal(start_ptr, state);
    1055                 :             : 
    1056         [ #  # ]:           0 :         if (!matchptr)
    1057                 :           0 :                 return false;
    1058                 :             : 
    1059                 :             :         /*
    1060                 :             :          * Found a match for the byte sequence.  If this is a multibyte encoding,
    1061                 :             :          * where one character's byte sequence can appear inside a longer
    1062                 :             :          * multi-byte character, we need to verify that the match was at a
    1063                 :             :          * character boundary, not in the middle of a multi-byte character.
    1064                 :             :          */
    1065   [ #  #  #  # ]:           0 :         if (state->is_multibyte_char_in_char && state->locale->deterministic)
    1066                 :             :         {
    1067                 :             :                 /* Walk one character at a time, until we reach the match. */
    1068                 :             : 
    1069                 :             :                 /* the search should never move backwards. */
    1070         [ #  # ]:           0 :                 Assert(state->refpoint <= matchptr);
    1071                 :             : 
    1072         [ #  # ]:           0 :                 while (state->refpoint < matchptr)
    1073                 :             :                 {
    1074                 :             :                         /* step to next character. */
    1075                 :           0 :                         state->refpoint += pg_mblen(state->refpoint);
    1076                 :           0 :                         state->refpos++;
    1077                 :             : 
    1078                 :             :                         /*
    1079                 :             :                          * If we stepped over the match's start position, then it was a
    1080                 :             :                          * false positive, where the byte sequence appeared in the middle
    1081                 :             :                          * of a multi-byte character.  Skip it, and continue the search at
    1082                 :             :                          * the next character boundary.
    1083                 :             :                          */
    1084         [ #  # ]:           0 :                         if (state->refpoint > matchptr)
    1085                 :             :                         {
    1086                 :           0 :                                 start_ptr = state->refpoint;
    1087                 :           0 :                                 goto retry;
    1088                 :             :                         }
    1089                 :             :                 }
    1090                 :           0 :         }
    1091                 :             : 
    1092                 :           0 :         state->last_match = matchptr;
    1093                 :           0 :         state->last_match_len = state->last_match_len_tmp;
    1094                 :           0 :         return true;
    1095                 :           0 : }
    1096                 :             : 
    1097                 :             : /*
    1098                 :             :  * Subroutine of text_position_next().  This searches for the raw byte
    1099                 :             :  * sequence, ignoring any multi-byte encoding issues.  Returns the first
    1100                 :             :  * match starting at 'start_ptr', or NULL if no match is found.
    1101                 :             :  */
    1102                 :             : static char *
    1103                 :           0 : text_position_next_internal(char *start_ptr, TextPositionState *state)
    1104                 :             : {
    1105                 :           0 :         int                     haystack_len = state->len1;
    1106                 :           0 :         int                     needle_len = state->len2;
    1107                 :           0 :         int                     skiptablemask = state->skiptablemask;
    1108                 :           0 :         const char *haystack = state->str1;
    1109                 :           0 :         const char *needle = state->str2;
    1110                 :           0 :         const char *haystack_end = &haystack[haystack_len];
    1111                 :           0 :         const char *hptr;
    1112                 :             : 
    1113         [ #  # ]:           0 :         Assert(start_ptr >= haystack && start_ptr <= haystack_end);
    1114         [ #  # ]:           0 :         Assert(needle_len > 0);
    1115                 :             : 
    1116                 :           0 :         state->last_match_len_tmp = needle_len;
    1117                 :             : 
    1118         [ #  # ]:           0 :         if (!state->locale->deterministic)
    1119                 :             :         {
    1120                 :             :                 /*
    1121                 :             :                  * With a nondeterministic collation, we have to use an unoptimized
    1122                 :             :                  * route.  We walk through the haystack and see if at each position
    1123                 :             :                  * there is a substring of the remaining string that is equal to the
    1124                 :             :                  * needle under the given collation.
    1125                 :             :                  *
    1126                 :             :                  * Note, the found substring could have a different length than the
    1127                 :             :                  * needle.  Callers that want to skip over the found string need to
    1128                 :             :                  * read the length of the found substring from last_match_len rather
    1129                 :             :                  * than just using the length of their needle.
    1130                 :             :                  *
    1131                 :             :                  * Most callers will require "greedy" semantics, meaning that we need
    1132                 :             :                  * to find the longest such substring, not the shortest.  For callers
    1133                 :             :                  * that don't need greedy semantics, we can finish on the first match.
    1134                 :             :                  *
    1135                 :             :                  * This loop depends on the assumption that the needle is nonempty and
    1136                 :             :                  * any matching substring must also be nonempty.  (Even if the
    1137                 :             :                  * collation would accept an empty match, returning one would send
    1138                 :             :                  * callers that search for successive matches into an infinite loop.)
    1139                 :             :                  */
    1140                 :           0 :                 const char *result_hptr = NULL;
    1141                 :             : 
    1142                 :           0 :                 hptr = start_ptr;
    1143         [ #  # ]:           0 :                 while (hptr < haystack_end)
    1144                 :             :                 {
    1145                 :           0 :                         const char *test_end;
    1146                 :             : 
    1147                 :             :                         /*
    1148                 :             :                          * First check the common case that there is a match in the
    1149                 :             :                          * haystack of exactly the length of the needle.
    1150                 :             :                          */
    1151         [ #  # ]:           0 :                         if (!state->greedy &&
    1152   [ #  #  #  # ]:           0 :                                 haystack_end - hptr >= needle_len &&
    1153                 :           0 :                                 pg_strncoll(hptr, needle_len, needle, needle_len, state->locale) == 0)
    1154                 :           0 :                                 return (char *) hptr;
    1155                 :             : 
    1156                 :             :                         /*
    1157                 :             :                          * Else check if any of the non-empty substrings starting at hptr
    1158                 :             :                          * compare equal to the needle.
    1159                 :             :                          */
    1160                 :           0 :                         test_end = hptr;
    1161                 :           0 :                         do
    1162                 :             :                         {
    1163                 :           0 :                                 test_end += pg_mblen(test_end);
    1164         [ #  # ]:           0 :                                 if (pg_strncoll(hptr, (test_end - hptr), needle, needle_len, state->locale) == 0)
    1165                 :             :                                 {
    1166                 :           0 :                                         state->last_match_len_tmp = (test_end - hptr);
    1167                 :           0 :                                         result_hptr = hptr;
    1168         [ #  # ]:           0 :                                         if (!state->greedy)
    1169                 :           0 :                                                 break;
    1170                 :           0 :                                 }
    1171         [ #  # ]:           0 :                         } while (test_end < haystack_end);
    1172                 :             : 
    1173         [ #  # ]:           0 :                         if (result_hptr)
    1174                 :           0 :                                 break;
    1175                 :             : 
    1176                 :           0 :                         hptr += pg_mblen(hptr);
    1177      [ #  #  # ]:           0 :                 }
    1178                 :             : 
    1179                 :           0 :                 return (char *) result_hptr;
    1180                 :           0 :         }
    1181         [ #  # ]:           0 :         else if (needle_len == 1)
    1182                 :             :         {
    1183                 :             :                 /* No point in using B-M-H for a one-character needle */
    1184                 :           0 :                 char            nchar = *needle;
    1185                 :             : 
    1186                 :           0 :                 hptr = start_ptr;
    1187         [ #  # ]:           0 :                 while (hptr < haystack_end)
    1188                 :             :                 {
    1189         [ #  # ]:           0 :                         if (*hptr == nchar)
    1190                 :           0 :                                 return (char *) hptr;
    1191                 :           0 :                         hptr++;
    1192                 :             :                 }
    1193         [ #  # ]:           0 :         }
    1194                 :             :         else
    1195                 :             :         {
    1196                 :           0 :                 const char *needle_last = &needle[needle_len - 1];
    1197                 :             : 
    1198                 :             :                 /* Start at startpos plus the length of the needle */
    1199                 :           0 :                 hptr = start_ptr + needle_len - 1;
    1200         [ #  # ]:           0 :                 while (hptr < haystack_end)
    1201                 :             :                 {
    1202                 :             :                         /* Match the needle scanning *backward* */
    1203                 :           0 :                         const char *nptr;
    1204                 :           0 :                         const char *p;
    1205                 :             : 
    1206                 :           0 :                         nptr = needle_last;
    1207                 :           0 :                         p = hptr;
    1208         [ #  # ]:           0 :                         while (*nptr == *p)
    1209                 :             :                         {
    1210                 :             :                                 /* Matched it all?      If so, return 1-based position */
    1211         [ #  # ]:           0 :                                 if (nptr == needle)
    1212                 :           0 :                                         return (char *) p;
    1213                 :           0 :                                 nptr--, p--;
    1214                 :             :                         }
    1215                 :             : 
    1216                 :             :                         /*
    1217                 :             :                          * No match, so use the haystack char at hptr to decide how far to
    1218                 :             :                          * advance.  If the needle had any occurrence of that character
    1219                 :             :                          * (or more precisely, one sharing the same skiptable entry)
    1220                 :             :                          * before its last character, then we advance far enough to align
    1221                 :             :                          * the last such needle character with that haystack position.
    1222                 :             :                          * Otherwise we can advance by the whole needle length.
    1223                 :             :                          */
    1224                 :           0 :                         hptr += state->skiptable[(unsigned char) *hptr & skiptablemask];
    1225         [ #  # ]:           0 :                 }
    1226         [ #  # ]:           0 :         }
    1227                 :             : 
    1228                 :           0 :         return 0;                                       /* not found */
    1229                 :           0 : }
    1230                 :             : 
    1231                 :             : /*
    1232                 :             :  * Return a pointer to the current match.
    1233                 :             :  *
    1234                 :             :  * The returned pointer points into the original haystack string.
    1235                 :             :  */
    1236                 :             : static char *
    1237                 :           0 : text_position_get_match_ptr(TextPositionState *state)
    1238                 :             : {
    1239                 :           0 :         return state->last_match;
    1240                 :             : }
    1241                 :             : 
    1242                 :             : /*
    1243                 :             :  * Return the offset of the current match.
    1244                 :             :  *
    1245                 :             :  * The offset is in characters, 1-based.
    1246                 :             :  */
    1247                 :             : static int
    1248                 :           0 : text_position_get_match_pos(TextPositionState *state)
    1249                 :             : {
    1250                 :             :         /* Convert the byte position to char position. */
    1251                 :           0 :         state->refpos += pg_mbstrlen_with_len(state->refpoint,
    1252                 :           0 :                                                                                   state->last_match - state->refpoint);
    1253                 :           0 :         state->refpoint = state->last_match;
    1254                 :           0 :         return state->refpos + 1;
    1255                 :             : }
    1256                 :             : 
    1257                 :             : /*
    1258                 :             :  * Reset search state to the initial state installed by text_position_setup.
    1259                 :             :  *
    1260                 :             :  * The next call to text_position_next will search from the beginning
    1261                 :             :  * of the string.
    1262                 :             :  */
    1263                 :             : static void
    1264                 :           0 : text_position_reset(TextPositionState *state)
    1265                 :             : {
    1266                 :           0 :         state->last_match = NULL;
    1267                 :           0 :         state->refpoint = state->str1;
    1268                 :           0 :         state->refpos = 0;
    1269                 :           0 : }
    1270                 :             : 
    1271                 :             : static void
    1272                 :           0 : text_position_cleanup(TextPositionState *state)
    1273                 :             : {
    1274                 :             :         /* no cleanup needed */
    1275                 :           0 : }
    1276                 :             : 
    1277                 :             : 
    1278                 :             : static void
    1279                 :     1680327 : check_collation_set(Oid collid)
    1280                 :             : {
    1281         [ +  + ]:     1680327 :         if (!OidIsValid(collid))
    1282                 :             :         {
    1283                 :             :                 /*
    1284                 :             :                  * This typically means that the parser could not resolve a conflict
    1285                 :             :                  * of implicit collations, so report it that way.
    1286                 :             :                  */
    1287   [ +  -  +  - ]:           5 :                 ereport(ERROR,
    1288                 :             :                                 (errcode(ERRCODE_INDETERMINATE_COLLATION),
    1289                 :             :                                  errmsg("could not determine which collation to use for string comparison"),
    1290                 :             :                                  errhint("Use the COLLATE clause to set the collation explicitly.")));
    1291                 :           0 :         }
    1292                 :     1680322 : }
    1293                 :             : 
    1294                 :             : /*
    1295                 :             :  * varstr_cmp()
    1296                 :             :  *
    1297                 :             :  * Comparison function for text strings with given lengths, using the
    1298                 :             :  * appropriate locale. Returns an integer less than, equal to, or greater than
    1299                 :             :  * zero, indicating whether arg1 is less than, equal to, or greater than arg2.
    1300                 :             :  *
    1301                 :             :  * Note: many functions that depend on this are marked leakproof; therefore,
    1302                 :             :  * avoid reporting the actual contents of the input when throwing errors.
    1303                 :             :  * All errors herein should be things that can't happen except on corrupt
    1304                 :             :  * data, anyway; otherwise we will have trouble with indexing strings that
    1305                 :             :  * would cause them.
    1306                 :             :  */
    1307                 :             : int
    1308                 :      993077 : varstr_cmp(const char *arg1, int len1, const char *arg2, int len2, Oid collid)
    1309                 :             : {
    1310                 :      993077 :         int                     result;
    1311                 :      993077 :         pg_locale_t mylocale;
    1312                 :             : 
    1313                 :      993077 :         check_collation_set(collid);
    1314                 :             : 
    1315                 :      993077 :         mylocale = pg_newlocale_from_collation(collid);
    1316                 :             : 
    1317         [ +  + ]:      993077 :         if (mylocale->collate_is_c)
    1318                 :             :         {
    1319         [ +  + ]:      169440 :                 result = memcmp(arg1, arg2, Min(len1, len2));
    1320   [ +  +  +  + ]:      169440 :                 if ((result == 0) && (len1 != len2))
    1321                 :       13976 :                         result = (len1 < len2) ? -1 : 1;
    1322                 :      169440 :         }
    1323                 :             :         else
    1324                 :             :         {
    1325                 :             :                 /*
    1326                 :             :                  * memcmp() can't tell us which of two unequal strings sorts first,
    1327                 :             :                  * but it's a cheap way to tell if they're equal.  Testing shows that
    1328                 :             :                  * memcmp() followed by strcoll() is only trivially slower than
    1329                 :             :                  * strcoll() by itself, so we don't lose much if this doesn't work out
    1330                 :             :                  * very often, and if it does - for example, because there are many
    1331                 :             :                  * equal strings in the input - then we win big by avoiding expensive
    1332                 :             :                  * collation-aware comparisons.
    1333                 :             :                  */
    1334   [ +  +  +  + ]:      823637 :                 if (len1 == len2 && memcmp(arg1, arg2, len1) == 0)
    1335                 :      308406 :                         return 0;
    1336                 :             : 
    1337                 :      515231 :                 result = pg_strncoll(arg1, len1, arg2, len2, mylocale);
    1338                 :             : 
    1339                 :             :                 /* Break tie if necessary. */
    1340   [ +  +  +  - ]:      515231 :                 if (result == 0 && mylocale->deterministic)
    1341                 :             :                 {
    1342         [ #  # ]:           0 :                         result = memcmp(arg1, arg2, Min(len1, len2));
    1343   [ #  #  #  # ]:           0 :                         if ((result == 0) && (len1 != len2))
    1344                 :           0 :                                 result = (len1 < len2) ? -1 : 1;
    1345                 :           0 :                 }
    1346                 :             :         }
    1347                 :             : 
    1348                 :      684671 :         return result;
    1349                 :      993077 : }
    1350                 :             : 
    1351                 :             : /* text_cmp()
    1352                 :             :  * Internal comparison function for text strings.
    1353                 :             :  * Returns -1, 0 or 1
    1354                 :             :  */
    1355                 :             : static int
    1356                 :      726758 : text_cmp(text *arg1, text *arg2, Oid collid)
    1357                 :             : {
    1358                 :      726758 :         char       *a1p,
    1359                 :             :                            *a2p;
    1360                 :      726758 :         int                     len1,
    1361                 :             :                                 len2;
    1362                 :             : 
    1363                 :      726758 :         a1p = VARDATA_ANY(arg1);
    1364                 :      726758 :         a2p = VARDATA_ANY(arg2);
    1365                 :             : 
    1366                 :      726758 :         len1 = VARSIZE_ANY_EXHDR(arg1);
    1367                 :      726758 :         len2 = VARSIZE_ANY_EXHDR(arg2);
    1368                 :             : 
    1369                 :     1453516 :         return varstr_cmp(a1p, len1, a2p, len2, collid);
    1370                 :      726758 : }
    1371                 :             : 
    1372                 :             : /*
    1373                 :             :  * Comparison functions for text strings.
    1374                 :             :  *
    1375                 :             :  * Note: btree indexes need these routines not to leak memory; therefore,
    1376                 :             :  * be careful to free working copies of toasted datums.  Most places don't
    1377                 :             :  * need to be so careful.
    1378                 :             :  */
    1379                 :             : 
    1380                 :             : Datum
    1381                 :      638916 : texteq(PG_FUNCTION_ARGS)
    1382                 :             : {
    1383                 :      638916 :         Oid                     collid = PG_GET_COLLATION();
    1384                 :      638916 :         pg_locale_t mylocale = 0;
    1385                 :      638916 :         bool            result;
    1386                 :             : 
    1387                 :      638916 :         check_collation_set(collid);
    1388                 :             : 
    1389                 :      638916 :         mylocale = pg_newlocale_from_collation(collid);
    1390                 :             : 
    1391         [ +  + ]:      638916 :         if (mylocale->deterministic)
    1392                 :             :         {
    1393                 :      638190 :                 Datum           arg1 = PG_GETARG_DATUM(0);
    1394                 :      638190 :                 Datum           arg2 = PG_GETARG_DATUM(1);
    1395                 :      638190 :                 Size            len1,
    1396                 :             :                                         len2;
    1397                 :             : 
    1398                 :             :                 /*
    1399                 :             :                  * Since we only care about equality or not-equality, we can avoid all
    1400                 :             :                  * the expense of strcoll() here, and just do bitwise comparison.  In
    1401                 :             :                  * fact, we don't even have to do a bitwise comparison if we can show
    1402                 :             :                  * the lengths of the strings are unequal; which might save us from
    1403                 :             :                  * having to detoast one or both values.
    1404                 :             :                  */
    1405                 :      638190 :                 len1 = toast_raw_datum_size(arg1);
    1406                 :      638190 :                 len2 = toast_raw_datum_size(arg2);
    1407         [ +  + ]:      638190 :                 if (len1 != len2)
    1408                 :      285870 :                         result = false;
    1409                 :             :                 else
    1410                 :             :                 {
    1411                 :      352320 :                         text       *targ1 = DatumGetTextPP(arg1);
    1412                 :      352320 :                         text       *targ2 = DatumGetTextPP(arg2);
    1413                 :             : 
    1414                 :     1056960 :                         result = (memcmp(VARDATA_ANY(targ1), VARDATA_ANY(targ2),
    1415                 :      704640 :                                                          len1 - VARHDRSZ) == 0);
    1416                 :             : 
    1417         [ +  + ]:      352320 :                         PG_FREE_IF_COPY(targ1, 0);
    1418         [ +  - ]:      352320 :                         PG_FREE_IF_COPY(targ2, 1);
    1419                 :      352320 :                 }
    1420                 :      638190 :         }
    1421                 :             :         else
    1422                 :             :         {
    1423                 :         726 :                 text       *arg1 = PG_GETARG_TEXT_PP(0);
    1424                 :         726 :                 text       *arg2 = PG_GETARG_TEXT_PP(1);
    1425                 :             : 
    1426                 :         726 :                 result = (text_cmp(arg1, arg2, collid) == 0);
    1427                 :             : 
    1428         [ +  - ]:         726 :                 PG_FREE_IF_COPY(arg1, 0);
    1429         [ +  - ]:         726 :                 PG_FREE_IF_COPY(arg2, 1);
    1430                 :         726 :         }
    1431                 :             : 
    1432                 :     1277832 :         PG_RETURN_BOOL(result);
    1433                 :      638916 : }
    1434                 :             : 
    1435                 :             : Datum
    1436                 :        3335 : textne(PG_FUNCTION_ARGS)
    1437                 :             : {
    1438                 :        3335 :         Oid                     collid = PG_GET_COLLATION();
    1439                 :        3335 :         pg_locale_t mylocale;
    1440                 :        3335 :         bool            result;
    1441                 :             : 
    1442                 :        3335 :         check_collation_set(collid);
    1443                 :             : 
    1444                 :        3335 :         mylocale = pg_newlocale_from_collation(collid);
    1445                 :             : 
    1446         [ +  + ]:        3335 :         if (mylocale->deterministic)
    1447                 :             :         {
    1448                 :        3331 :                 Datum           arg1 = PG_GETARG_DATUM(0);
    1449                 :        3331 :                 Datum           arg2 = PG_GETARG_DATUM(1);
    1450                 :        3331 :                 Size            len1,
    1451                 :             :                                         len2;
    1452                 :             : 
    1453                 :             :                 /* See comment in texteq() */
    1454                 :        3331 :                 len1 = toast_raw_datum_size(arg1);
    1455                 :        3331 :                 len2 = toast_raw_datum_size(arg2);
    1456         [ +  + ]:        3331 :                 if (len1 != len2)
    1457                 :         397 :                         result = true;
    1458                 :             :                 else
    1459                 :             :                 {
    1460                 :        2934 :                         text       *targ1 = DatumGetTextPP(arg1);
    1461                 :        2934 :                         text       *targ2 = DatumGetTextPP(arg2);
    1462                 :             : 
    1463                 :        8802 :                         result = (memcmp(VARDATA_ANY(targ1), VARDATA_ANY(targ2),
    1464                 :        5868 :                                                          len1 - VARHDRSZ) != 0);
    1465                 :             : 
    1466         [ +  - ]:        2934 :                         PG_FREE_IF_COPY(targ1, 0);
    1467         [ +  - ]:        2934 :                         PG_FREE_IF_COPY(targ2, 1);
    1468                 :        2934 :                 }
    1469                 :        3331 :         }
    1470                 :             :         else
    1471                 :             :         {
    1472                 :           4 :                 text       *arg1 = PG_GETARG_TEXT_PP(0);
    1473                 :           4 :                 text       *arg2 = PG_GETARG_TEXT_PP(1);
    1474                 :             : 
    1475                 :           4 :                 result = (text_cmp(arg1, arg2, collid) != 0);
    1476                 :             : 
    1477         [ +  - ]:           4 :                 PG_FREE_IF_COPY(arg1, 0);
    1478         [ +  - ]:           4 :                 PG_FREE_IF_COPY(arg2, 1);
    1479                 :           4 :         }
    1480                 :             : 
    1481                 :        6670 :         PG_RETURN_BOOL(result);
    1482                 :        3335 : }
    1483                 :             : 
    1484                 :             : Datum
    1485                 :      120138 : text_lt(PG_FUNCTION_ARGS)
    1486                 :             : {
    1487                 :      120138 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    1488                 :      120138 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    1489                 :      120138 :         bool            result;
    1490                 :             : 
    1491                 :      120138 :         result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) < 0);
    1492                 :             : 
    1493         [ +  - ]:      120138 :         PG_FREE_IF_COPY(arg1, 0);
    1494         [ +  - ]:      120138 :         PG_FREE_IF_COPY(arg2, 1);
    1495                 :             : 
    1496                 :      240276 :         PG_RETURN_BOOL(result);
    1497                 :      120138 : }
    1498                 :             : 
    1499                 :             : Datum
    1500                 :       51855 : text_le(PG_FUNCTION_ARGS)
    1501                 :             : {
    1502                 :       51855 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    1503                 :       51855 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    1504                 :       51855 :         bool            result;
    1505                 :             : 
    1506                 :       51855 :         result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) <= 0);
    1507                 :             : 
    1508         [ +  + ]:       51855 :         PG_FREE_IF_COPY(arg1, 0);
    1509         [ +  - ]:       51855 :         PG_FREE_IF_COPY(arg2, 1);
    1510                 :             : 
    1511                 :      103710 :         PG_RETURN_BOOL(result);
    1512                 :       51855 : }
    1513                 :             : 
    1514                 :             : Datum
    1515                 :      117899 : text_gt(PG_FUNCTION_ARGS)
    1516                 :             : {
    1517                 :      117899 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    1518                 :      117899 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    1519                 :      117899 :         bool            result;
    1520                 :             : 
    1521                 :      117899 :         result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) > 0);
    1522                 :             : 
    1523         [ +  - ]:      117899 :         PG_FREE_IF_COPY(arg1, 0);
    1524         [ +  - ]:      117899 :         PG_FREE_IF_COPY(arg2, 1);
    1525                 :             : 
    1526                 :      235798 :         PG_RETURN_BOOL(result);
    1527                 :      117899 : }
    1528                 :             : 
    1529                 :             : Datum
    1530                 :       28336 : text_ge(PG_FUNCTION_ARGS)
    1531                 :             : {
    1532                 :       28336 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    1533                 :       28336 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    1534                 :       28336 :         bool            result;
    1535                 :             : 
    1536                 :       28336 :         result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) >= 0);
    1537                 :             : 
    1538         [ +  - ]:       28336 :         PG_FREE_IF_COPY(arg1, 0);
    1539         [ +  - ]:       28336 :         PG_FREE_IF_COPY(arg2, 1);
    1540                 :             : 
    1541                 :       56672 :         PG_RETURN_BOOL(result);
    1542                 :       28336 : }
    1543                 :             : 
    1544                 :             : Datum
    1545                 :        6319 : text_starts_with(PG_FUNCTION_ARGS)
    1546                 :             : {
    1547                 :        6319 :         Datum           arg1 = PG_GETARG_DATUM(0);
    1548                 :        6319 :         Datum           arg2 = PG_GETARG_DATUM(1);
    1549                 :        6319 :         Oid                     collid = PG_GET_COLLATION();
    1550                 :        6319 :         pg_locale_t mylocale;
    1551                 :        6319 :         bool            result;
    1552                 :        6319 :         Size            len1,
    1553                 :             :                                 len2;
    1554                 :             : 
    1555                 :        6319 :         check_collation_set(collid);
    1556                 :             : 
    1557                 :        6319 :         mylocale = pg_newlocale_from_collation(collid);
    1558                 :             : 
    1559         [ +  - ]:        6319 :         if (!mylocale->deterministic)
    1560   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    1561                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1562                 :             :                                  errmsg("nondeterministic collations are not supported for substring searches")));
    1563                 :             : 
    1564                 :        6319 :         len1 = toast_raw_datum_size(arg1);
    1565                 :        6319 :         len2 = toast_raw_datum_size(arg2);
    1566         [ -  + ]:        6319 :         if (len2 > len1)
    1567                 :           0 :                 result = false;
    1568                 :             :         else
    1569                 :             :         {
    1570                 :        6319 :                 text       *targ1 = text_substring(arg1, 1, len2, false);
    1571                 :        6319 :                 text       *targ2 = DatumGetTextPP(arg2);
    1572                 :             : 
    1573                 :       18957 :                 result = (memcmp(VARDATA_ANY(targ1), VARDATA_ANY(targ2),
    1574                 :       12638 :                                                  VARSIZE_ANY_EXHDR(targ2)) == 0);
    1575                 :             : 
    1576         [ -  + ]:        6319 :                 PG_FREE_IF_COPY(targ1, 0);
    1577         [ +  - ]:        6319 :                 PG_FREE_IF_COPY(targ2, 1);
    1578                 :        6319 :         }
    1579                 :             : 
    1580                 :       12638 :         PG_RETURN_BOOL(result);
    1581                 :        6319 : }
    1582                 :             : 
    1583                 :             : Datum
    1584                 :      369168 : bttextcmp(PG_FUNCTION_ARGS)
    1585                 :             : {
    1586                 :      369168 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    1587                 :      369168 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    1588                 :      369168 :         int32           result;
    1589                 :             : 
    1590                 :      369168 :         result = text_cmp(arg1, arg2, PG_GET_COLLATION());
    1591                 :             : 
    1592         [ +  + ]:      369168 :         PG_FREE_IF_COPY(arg1, 0);
    1593         [ +  + ]:      369168 :         PG_FREE_IF_COPY(arg2, 1);
    1594                 :             : 
    1595                 :      738336 :         PG_RETURN_INT32(result);
    1596                 :      369168 : }
    1597                 :             : 
    1598                 :             : Datum
    1599                 :        4052 : bttextsortsupport(PG_FUNCTION_ARGS)
    1600                 :             : {
    1601                 :        4052 :         SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
    1602                 :        4052 :         Oid                     collid = ssup->ssup_collation;
    1603                 :        4052 :         MemoryContext oldcontext;
    1604                 :             : 
    1605                 :        4052 :         oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
    1606                 :             : 
    1607                 :             :         /* Use generic string SortSupport */
    1608                 :        4052 :         varstr_sortsupport(ssup, TEXTOID, collid);
    1609                 :             : 
    1610                 :        4052 :         MemoryContextSwitchTo(oldcontext);
    1611                 :             : 
    1612                 :        4052 :         PG_RETURN_VOID();
    1613                 :        4052 : }
    1614                 :             : 
    1615                 :             : /*
    1616                 :             :  * Generic sortsupport interface for character type's operator classes.
    1617                 :             :  * Includes locale support, and support for BpChar semantics (i.e. removing
    1618                 :             :  * trailing spaces before comparison).
    1619                 :             :  *
    1620                 :             :  * Relies on the assumption that text, VarChar, and BpChar all have the
    1621                 :             :  * same representation.
    1622                 :             :  */
    1623                 :             : void
    1624                 :       10279 : varstr_sortsupport(SortSupport ssup, Oid typid, Oid collid)
    1625                 :             : {
    1626                 :       10279 :         bool            abbreviate = ssup->abbreviate;
    1627                 :       10279 :         bool            collate_c = false;
    1628                 :       10279 :         VarStringSortSupport *sss;
    1629                 :       10279 :         pg_locale_t locale;
    1630                 :             : 
    1631                 :       10279 :         check_collation_set(collid);
    1632                 :             : 
    1633                 :       10279 :         locale = pg_newlocale_from_collation(collid);
    1634                 :             : 
    1635                 :             :         /*
    1636                 :             :          * If possible, set ssup->comparator to a function which can be used to
    1637                 :             :          * directly compare two datums.  If we can do this, we'll avoid the
    1638                 :             :          * overhead of a trip through the fmgr layer for every comparison, which
    1639                 :             :          * can be substantial.
    1640                 :             :          *
    1641                 :             :          * Most typically, we'll set the comparator to varlenafastcmp_locale,
    1642                 :             :          * which uses strcoll() to perform comparisons.  We use that for the
    1643                 :             :          * BpChar case too, but type NAME uses namefastcmp_locale. However, if
    1644                 :             :          * LC_COLLATE = C, we can make things quite a bit faster with
    1645                 :             :          * varstrfastcmp_c, bpcharfastcmp_c, or namefastcmp_c, all of which use
    1646                 :             :          * memcmp() rather than strcoll().
    1647                 :             :          */
    1648         [ +  + ]:       10279 :         if (locale->collate_is_c)
    1649                 :             :         {
    1650         [ +  + ]:        7427 :                 if (typid == BPCHAROID)
    1651                 :           3 :                         ssup->comparator = bpcharfastcmp_c;
    1652         [ +  + ]:        7424 :                 else if (typid == NAMEOID)
    1653                 :             :                 {
    1654                 :        6068 :                         ssup->comparator = namefastcmp_c;
    1655                 :             :                         /* Not supporting abbreviation with type NAME, for now */
    1656                 :        6068 :                         abbreviate = false;
    1657                 :        6068 :                 }
    1658                 :             :                 else
    1659                 :        1356 :                         ssup->comparator = varstrfastcmp_c;
    1660                 :             : 
    1661                 :        7427 :                 collate_c = true;
    1662                 :        7427 :         }
    1663                 :             :         else
    1664                 :             :         {
    1665                 :             :                 /*
    1666                 :             :                  * We use varlenafastcmp_locale except for type NAME.
    1667                 :             :                  */
    1668         [ -  + ]:        2852 :                 if (typid == NAMEOID)
    1669                 :             :                 {
    1670                 :           0 :                         ssup->comparator = namefastcmp_locale;
    1671                 :             :                         /* Not supporting abbreviation with type NAME, for now */
    1672                 :           0 :                         abbreviate = false;
    1673                 :           0 :                 }
    1674                 :             :                 else
    1675                 :        2852 :                         ssup->comparator = varlenafastcmp_locale;
    1676                 :             : 
    1677                 :             :                 /*
    1678                 :             :                  * Unfortunately, it seems that abbreviation for non-C collations is
    1679                 :             :                  * broken on many common platforms; see pg_strxfrm_enabled().
    1680                 :             :                  *
    1681                 :             :                  * Even apart from the risk of broken locales, it's possible that
    1682                 :             :                  * there are platforms where the use of abbreviated keys should be
    1683                 :             :                  * disabled at compile time.  For example, macOS's strxfrm()
    1684                 :             :                  * implementation is known to not effectively concentrate a
    1685                 :             :                  * significant amount of entropy from the original string in earlier
    1686                 :             :                  * transformed blobs.  It's possible that other supported platforms
    1687                 :             :                  * are similarly encumbered.  So, if we ever get past disabling this
    1688                 :             :                  * categorically, we may still want or need to disable it for
    1689                 :             :                  * particular platforms.
    1690                 :             :                  */
    1691         [ +  + ]:        2852 :                 if (!pg_strxfrm_enabled(locale))
    1692                 :        2721 :                         abbreviate = false;
    1693                 :             :         }
    1694                 :             : 
    1695                 :             :         /*
    1696                 :             :          * If we're using abbreviated keys, or if we're using a locale-aware
    1697                 :             :          * comparison, we need to initialize a VarStringSortSupport object. Both
    1698                 :             :          * cases will make use of the temporary buffers we initialize here for
    1699                 :             :          * scratch space (and to detect requirement for BpChar semantics from
    1700                 :             :          * caller), and the abbreviation case requires additional state.
    1701                 :             :          */
    1702   [ +  +  +  + ]:       10279 :         if (abbreviate || !collate_c)
    1703                 :             :         {
    1704                 :        3062 :                 sss = palloc_object(VarStringSortSupport);
    1705                 :        3062 :                 sss->buf1 = palloc(TEXTBUFLEN);
    1706                 :        3062 :                 sss->buflen1 = TEXTBUFLEN;
    1707                 :        3062 :                 sss->buf2 = palloc(TEXTBUFLEN);
    1708                 :        3062 :                 sss->buflen2 = TEXTBUFLEN;
    1709                 :             :                 /* Start with invalid values */
    1710                 :        3062 :                 sss->last_len1 = -1;
    1711                 :        3062 :                 sss->last_len2 = -1;
    1712                 :             :                 /* Initialize */
    1713                 :        3062 :                 sss->last_returned = 0;
    1714         [ +  + ]:        3062 :                 if (collate_c)
    1715                 :         210 :                         sss->locale = NULL;
    1716                 :             :                 else
    1717                 :        2852 :                         sss->locale = locale;
    1718                 :             : 
    1719                 :             :                 /*
    1720                 :             :                  * To avoid somehow confusing a strxfrm() blob and an original string,
    1721                 :             :                  * constantly keep track of the variety of data that buf1 and buf2
    1722                 :             :                  * currently contain.
    1723                 :             :                  *
    1724                 :             :                  * Comparisons may be interleaved with conversion calls.  Frequently,
    1725                 :             :                  * conversions and comparisons are batched into two distinct phases,
    1726                 :             :                  * but the correctness of caching cannot hinge upon this.  For
    1727                 :             :                  * comparison caching, buffer state is only trusted if cache_blob is
    1728                 :             :                  * found set to false, whereas strxfrm() caching only trusts the state
    1729                 :             :                  * when cache_blob is found set to true.
    1730                 :             :                  *
    1731                 :             :                  * Arbitrarily initialize cache_blob to true.
    1732                 :             :                  */
    1733                 :        3062 :                 sss->cache_blob = true;
    1734                 :        3062 :                 sss->collate_c = collate_c;
    1735                 :        3062 :                 sss->typid = typid;
    1736                 :        3062 :                 ssup->ssup_extra = sss;
    1737                 :             : 
    1738                 :             :                 /*
    1739                 :             :                  * If possible, plan to use the abbreviated keys optimization.  The
    1740                 :             :                  * core code may switch back to authoritative comparator should
    1741                 :             :                  * abbreviation be aborted.
    1742                 :             :                  */
    1743         [ +  + ]:        3062 :                 if (abbreviate)
    1744                 :             :                 {
    1745                 :         308 :                         sss->prop_card = 0.20;
    1746                 :         308 :                         initHyperLogLog(&sss->abbr_card, 10);
    1747                 :         308 :                         initHyperLogLog(&sss->full_card, 10);
    1748                 :         308 :                         ssup->abbrev_full_comparator = ssup->comparator;
    1749                 :         308 :                         ssup->comparator = ssup_datum_unsigned_cmp;
    1750                 :         308 :                         ssup->abbrev_converter = varstr_abbrev_convert;
    1751                 :         308 :                         ssup->abbrev_abort = varstr_abbrev_abort;
    1752                 :         308 :                 }
    1753                 :        3062 :         }
    1754                 :       10279 : }
    1755                 :             : 
    1756                 :             : /*
    1757                 :             :  * sortsupport comparison func (for C locale case)
    1758                 :             :  */
    1759                 :             : static int
    1760                 :      603274 : varstrfastcmp_c(Datum x, Datum y, SortSupport ssup)
    1761                 :             : {
    1762                 :      603274 :         VarString  *arg1 = DatumGetVarStringPP(x);
    1763                 :      603274 :         VarString  *arg2 = DatumGetVarStringPP(y);
    1764                 :      603274 :         char       *a1p,
    1765                 :             :                            *a2p;
    1766                 :      603274 :         int                     len1,
    1767                 :             :                                 len2,
    1768                 :             :                                 result;
    1769                 :             : 
    1770                 :      603274 :         a1p = VARDATA_ANY(arg1);
    1771                 :      603274 :         a2p = VARDATA_ANY(arg2);
    1772                 :             : 
    1773                 :      603274 :         len1 = VARSIZE_ANY_EXHDR(arg1);
    1774                 :      603274 :         len2 = VARSIZE_ANY_EXHDR(arg2);
    1775                 :             : 
    1776         [ +  + ]:      603274 :         result = memcmp(a1p, a2p, Min(len1, len2));
    1777   [ +  +  +  + ]:      603274 :         if ((result == 0) && (len1 != len2))
    1778                 :       23127 :                 result = (len1 < len2) ? -1 : 1;
    1779                 :             : 
    1780                 :             :         /* We can't afford to leak memory here. */
    1781         [ +  - ]:      603274 :         if (PointerGetDatum(arg1) != x)
    1782                 :           0 :                 pfree(arg1);
    1783         [ +  - ]:      603274 :         if (PointerGetDatum(arg2) != y)
    1784                 :           0 :                 pfree(arg2);
    1785                 :             : 
    1786                 :     1206548 :         return result;
    1787                 :      603274 : }
    1788                 :             : 
    1789                 :             : /*
    1790                 :             :  * sortsupport comparison func (for BpChar C locale case)
    1791                 :             :  *
    1792                 :             :  * BpChar outsources its sortsupport to this module.  Specialization for the
    1793                 :             :  * varstr_sortsupport BpChar case, modeled on
    1794                 :             :  * internal_bpchar_pattern_compare().
    1795                 :             :  */
    1796                 :             : static int
    1797                 :           0 : bpcharfastcmp_c(Datum x, Datum y, SortSupport ssup)
    1798                 :             : {
    1799                 :           0 :         BpChar     *arg1 = DatumGetBpCharPP(x);
    1800                 :           0 :         BpChar     *arg2 = DatumGetBpCharPP(y);
    1801                 :           0 :         char       *a1p,
    1802                 :             :                            *a2p;
    1803                 :           0 :         int                     len1,
    1804                 :             :                                 len2,
    1805                 :             :                                 result;
    1806                 :             : 
    1807                 :           0 :         a1p = VARDATA_ANY(arg1);
    1808                 :           0 :         a2p = VARDATA_ANY(arg2);
    1809                 :             : 
    1810                 :           0 :         len1 = bpchartruelen(a1p, VARSIZE_ANY_EXHDR(arg1));
    1811                 :           0 :         len2 = bpchartruelen(a2p, VARSIZE_ANY_EXHDR(arg2));
    1812                 :             : 
    1813         [ #  # ]:           0 :         result = memcmp(a1p, a2p, Min(len1, len2));
    1814   [ #  #  #  # ]:           0 :         if ((result == 0) && (len1 != len2))
    1815                 :           0 :                 result = (len1 < len2) ? -1 : 1;
    1816                 :             : 
    1817                 :             :         /* We can't afford to leak memory here. */
    1818         [ #  # ]:           0 :         if (PointerGetDatum(arg1) != x)
    1819                 :           0 :                 pfree(arg1);
    1820         [ #  # ]:           0 :         if (PointerGetDatum(arg2) != y)
    1821                 :           0 :                 pfree(arg2);
    1822                 :             : 
    1823                 :           0 :         return result;
    1824                 :           0 : }
    1825                 :             : 
    1826                 :             : /*
    1827                 :             :  * sortsupport comparison func (for NAME C locale case)
    1828                 :             :  */
    1829                 :             : static int
    1830                 :     1745195 : namefastcmp_c(Datum x, Datum y, SortSupport ssup)
    1831                 :             : {
    1832                 :     1745195 :         Name            arg1 = DatumGetName(x);
    1833                 :     1745195 :         Name            arg2 = DatumGetName(y);
    1834                 :             : 
    1835                 :     3490390 :         return strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN);
    1836                 :     1745195 : }
    1837                 :             : 
    1838                 :             : /*
    1839                 :             :  * sortsupport comparison func (for locale case with all varlena types)
    1840                 :             :  */
    1841                 :             : static int
    1842                 :     7373432 : varlenafastcmp_locale(Datum x, Datum y, SortSupport ssup)
    1843                 :             : {
    1844                 :     7373432 :         VarString  *arg1 = DatumGetVarStringPP(x);
    1845                 :     7373432 :         VarString  *arg2 = DatumGetVarStringPP(y);
    1846                 :     7373432 :         char       *a1p,
    1847                 :             :                            *a2p;
    1848                 :     7373432 :         int                     len1,
    1849                 :             :                                 len2,
    1850                 :             :                                 result;
    1851                 :             : 
    1852                 :     7373432 :         a1p = VARDATA_ANY(arg1);
    1853                 :     7373432 :         a2p = VARDATA_ANY(arg2);
    1854                 :             : 
    1855                 :     7373432 :         len1 = VARSIZE_ANY_EXHDR(arg1);
    1856                 :     7373432 :         len2 = VARSIZE_ANY_EXHDR(arg2);
    1857                 :             : 
    1858                 :     7373432 :         result = varstrfastcmp_locale(a1p, len1, a2p, len2, ssup);
    1859                 :             : 
    1860                 :             :         /* We can't afford to leak memory here. */
    1861         [ +  - ]:     7373432 :         if (PointerGetDatum(arg1) != x)
    1862                 :           0 :                 pfree(arg1);
    1863         [ +  - ]:     7373432 :         if (PointerGetDatum(arg2) != y)
    1864                 :           0 :                 pfree(arg2);
    1865                 :             : 
    1866                 :    14746864 :         return result;
    1867                 :     7373432 : }
    1868                 :             : 
    1869                 :             : /*
    1870                 :             :  * sortsupport comparison func (for locale case with NAME type)
    1871                 :             :  */
    1872                 :             : static int
    1873                 :           0 : namefastcmp_locale(Datum x, Datum y, SortSupport ssup)
    1874                 :             : {
    1875                 :           0 :         Name            arg1 = DatumGetName(x);
    1876                 :           0 :         Name            arg2 = DatumGetName(y);
    1877                 :             : 
    1878                 :           0 :         return varstrfastcmp_locale(NameStr(*arg1), strlen(NameStr(*arg1)),
    1879                 :           0 :                                                                 NameStr(*arg2), strlen(NameStr(*arg2)),
    1880                 :           0 :                                                                 ssup);
    1881                 :           0 : }
    1882                 :             : 
    1883                 :             : /*
    1884                 :             :  * sortsupport comparison func for locale cases
    1885                 :             :  */
    1886                 :             : static int
    1887                 :           0 : varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup)
    1888                 :             : {
    1889                 :           0 :         VarStringSortSupport *sss = (VarStringSortSupport *) ssup->ssup_extra;
    1890                 :           0 :         int                     result;
    1891                 :           0 :         bool            arg1_match;
    1892                 :             : 
    1893                 :             :         /* Fast pre-check for equality, as discussed in varstr_cmp() */
    1894   [ #  #  #  # ]:           0 :         if (len1 == len2 && memcmp(a1p, a2p, len1) == 0)
    1895                 :             :         {
    1896                 :             :                 /*
    1897                 :             :                  * No change in buf1 or buf2 contents, so avoid changing last_len1 or
    1898                 :             :                  * last_len2.  Existing contents of buffers might still be used by
    1899                 :             :                  * next call.
    1900                 :             :                  *
    1901                 :             :                  * It's fine to allow the comparison of BpChar padding bytes here,
    1902                 :             :                  * even though that implies that the memcmp() will usually be
    1903                 :             :                  * performed for BpChar callers (though multibyte characters could
    1904                 :             :                  * still prevent that from occurring).  The memcmp() is still very
    1905                 :             :                  * cheap, and BpChar's funny semantics have us remove trailing spaces
    1906                 :             :                  * (not limited to padding), so we need make no distinction between
    1907                 :             :                  * padding space characters and "real" space characters.
    1908                 :             :                  */
    1909                 :           0 :                 return 0;
    1910                 :             :         }
    1911                 :             : 
    1912         [ #  # ]:           0 :         if (sss->typid == BPCHAROID)
    1913                 :             :         {
    1914                 :             :                 /* Get true number of bytes, ignoring trailing spaces */
    1915                 :           0 :                 len1 = bpchartruelen(a1p, len1);
    1916                 :           0 :                 len2 = bpchartruelen(a2p, len2);
    1917                 :           0 :         }
    1918                 :             : 
    1919         [ #  # ]:           0 :         if (len1 >= sss->buflen1)
    1920                 :             :         {
    1921   [ #  #  #  #  :           0 :                 sss->buflen1 = Max(len1 + 1, Min(sss->buflen1 * 2, MaxAllocSize));
                   #  # ]
    1922                 :           0 :                 sss->buf1 = repalloc(sss->buf1, sss->buflen1);
    1923                 :           0 :         }
    1924         [ #  # ]:           0 :         if (len2 >= sss->buflen2)
    1925                 :             :         {
    1926   [ #  #  #  #  :           0 :                 sss->buflen2 = Max(len2 + 1, Min(sss->buflen2 * 2, MaxAllocSize));
                   #  # ]
    1927                 :           0 :                 sss->buf2 = repalloc(sss->buf2, sss->buflen2);
    1928                 :           0 :         }
    1929                 :             : 
    1930                 :             :         /*
    1931                 :             :          * We're likely to be asked to compare the same strings repeatedly, and
    1932                 :             :          * memcmp() is so much cheaper than strcoll() that it pays to try to cache
    1933                 :             :          * comparisons, even though in general there is no reason to think that
    1934                 :             :          * that will work out (every string datum may be unique).  Caching does
    1935                 :             :          * not slow things down measurably when it doesn't work out, and can speed
    1936                 :             :          * things up by rather a lot when it does.  In part, this is because the
    1937                 :             :          * memcmp() compares data from cachelines that are needed in L1 cache even
    1938                 :             :          * when the last comparison's result cannot be reused.
    1939                 :             :          */
    1940                 :           0 :         arg1_match = true;
    1941   [ #  #  #  # ]:           0 :         if (len1 != sss->last_len1 || memcmp(sss->buf1, a1p, len1) != 0)
    1942                 :             :         {
    1943                 :           0 :                 arg1_match = false;
    1944                 :           0 :                 memcpy(sss->buf1, a1p, len1);
    1945                 :           0 :                 sss->buf1[len1] = '\0';
    1946                 :           0 :                 sss->last_len1 = len1;
    1947                 :           0 :         }
    1948                 :             : 
    1949                 :             :         /*
    1950                 :             :          * If we're comparing the same two strings as last time, we can return the
    1951                 :             :          * same answer without calling strcoll() again.  This is more likely than
    1952                 :             :          * it seems (at least with moderate to low cardinality sets), because
    1953                 :             :          * quicksort compares the same pivot against many values.
    1954                 :             :          */
    1955   [ #  #  #  # ]:           0 :         if (len2 != sss->last_len2 || memcmp(sss->buf2, a2p, len2) != 0)
    1956                 :             :         {
    1957                 :           0 :                 memcpy(sss->buf2, a2p, len2);
    1958                 :           0 :                 sss->buf2[len2] = '\0';
    1959                 :           0 :                 sss->last_len2 = len2;
    1960                 :           0 :         }
    1961   [ #  #  #  # ]:           0 :         else if (arg1_match && !sss->cache_blob)
    1962                 :             :         {
    1963                 :             :                 /* Use result cached following last actual strcoll() call */
    1964                 :           0 :                 return sss->last_returned;
    1965                 :             :         }
    1966                 :             : 
    1967                 :           0 :         result = pg_strcoll(sss->buf1, sss->buf2, sss->locale);
    1968                 :             : 
    1969                 :             :         /* Break tie if necessary. */
    1970   [ #  #  #  # ]:           0 :         if (result == 0 && sss->locale->deterministic)
    1971                 :           0 :                 result = strcmp(sss->buf1, sss->buf2);
    1972                 :             : 
    1973                 :             :         /* Cache result, perhaps saving an expensive strcoll() call next time */
    1974                 :           0 :         sss->cache_blob = false;
    1975                 :           0 :         sss->last_returned = result;
    1976                 :           0 :         return result;
    1977                 :           0 : }
    1978                 :             : 
    1979                 :             : /*
    1980                 :             :  * Conversion routine for sortsupport.  Converts original to abbreviated key
    1981                 :             :  * representation.  Our encoding strategy is simple -- pack the first 8 bytes
    1982                 :             :  * of a strxfrm() blob into a Datum (on little-endian machines, the 8 bytes are
    1983                 :             :  * stored in reverse order), and treat it as an unsigned integer.  When the "C"
    1984                 :             :  * locale is used just memcpy() from original instead.
    1985                 :             :  */
    1986                 :             : static Datum
    1987                 :       75621 : varstr_abbrev_convert(Datum original, SortSupport ssup)
    1988                 :             : {
    1989                 :       75621 :         const size_t max_prefix_bytes = sizeof(Datum);
    1990                 :       75621 :         VarStringSortSupport *sss = (VarStringSortSupport *) ssup->ssup_extra;
    1991                 :       75621 :         VarString  *authoritative = DatumGetVarStringPP(original);
    1992                 :       75621 :         char       *authoritative_data = VARDATA_ANY(authoritative);
    1993                 :             : 
    1994                 :             :         /* working state */
    1995                 :       75621 :         Datum           res;
    1996                 :       75621 :         char       *pres;
    1997                 :       75621 :         int                     len;
    1998                 :       75621 :         uint32          hash;
    1999                 :             : 
    2000                 :       75621 :         pres = (char *) &res;
    2001                 :             :         /* memset(), so any non-overwritten bytes are NUL */
    2002                 :       75621 :         memset(pres, 0, max_prefix_bytes);
    2003                 :       75621 :         len = VARSIZE_ANY_EXHDR(authoritative);
    2004                 :             : 
    2005                 :             :         /* Get number of bytes, ignoring trailing spaces */
    2006         [ +  + ]:       75621 :         if (sss->typid == BPCHAROID)
    2007                 :          35 :                 len = bpchartruelen(authoritative_data, len);
    2008                 :             : 
    2009                 :             :         /*
    2010                 :             :          * If we're using the C collation, use memcpy(), rather than strxfrm(), to
    2011                 :             :          * abbreviate keys.  The full comparator for the C locale is also
    2012                 :             :          * memcmp().  This should be faster than strxfrm().
    2013                 :             :          */
    2014         [ +  + ]:       75621 :         if (sss->collate_c)
    2015         [ +  + ]:       75319 :                 memcpy(pres, authoritative_data, Min(len, max_prefix_bytes));
    2016                 :             :         else
    2017                 :             :         {
    2018                 :         302 :                 Size            bsize;
    2019                 :             : 
    2020                 :             :                 /*
    2021                 :             :                  * We're not using the C collation, so fall back on strxfrm or ICU
    2022                 :             :                  * analogs.
    2023                 :             :                  */
    2024                 :             : 
    2025                 :             :                 /* By convention, we use buffer 1 to store and NUL-terminate */
    2026         [ +  - ]:         302 :                 if (len >= sss->buflen1)
    2027                 :             :                 {
    2028   [ #  #  #  #  :           0 :                         sss->buflen1 = Max(len + 1, Min(sss->buflen1 * 2, MaxAllocSize));
                   #  # ]
    2029                 :           0 :                         sss->buf1 = repalloc(sss->buf1, sss->buflen1);
    2030                 :           0 :                 }
    2031                 :             : 
    2032                 :             :                 /* Might be able to reuse strxfrm() blob from last call */
    2033   [ +  +  +  -  :         302 :                 if (sss->last_len1 == len && sss->cache_blob &&
                   +  + ]
    2034                 :         150 :                         memcmp(sss->buf1, authoritative_data, len) == 0)
    2035                 :             :                 {
    2036         [ -  + ]:          28 :                         memcpy(pres, sss->buf2, Min(max_prefix_bytes, sss->last_len2));
    2037                 :             :                         /* No change affecting cardinality, so no hashing required */
    2038                 :          28 :                         goto done;
    2039                 :             :                 }
    2040                 :             : 
    2041                 :         274 :                 memcpy(sss->buf1, authoritative_data, len);
    2042                 :             : 
    2043                 :             :                 /*
    2044                 :             :                  * pg_strxfrm() and pg_strxfrm_prefix expect NUL-terminated strings.
    2045                 :             :                  */
    2046                 :         274 :                 sss->buf1[len] = '\0';
    2047                 :         274 :                 sss->last_len1 = len;
    2048                 :             : 
    2049         [ +  - ]:         274 :                 if (pg_strxfrm_prefix_enabled(sss->locale))
    2050                 :             :                 {
    2051         [ +  - ]:         274 :                         if (sss->buflen2 < max_prefix_bytes)
    2052                 :             :                         {
    2053   [ #  #  #  #  :           0 :                                 sss->buflen2 = Max(max_prefix_bytes,
                   #  # ]
    2054                 :             :                                                                    Min(sss->buflen2 * 2, MaxAllocSize));
    2055                 :           0 :                                 sss->buf2 = repalloc(sss->buf2, sss->buflen2);
    2056                 :           0 :                         }
    2057                 :             : 
    2058                 :         548 :                         bsize = pg_strxfrm_prefix(sss->buf2, sss->buf1,
    2059                 :         274 :                                                                           max_prefix_bytes, sss->locale);
    2060                 :         274 :                         sss->last_len2 = bsize;
    2061                 :         274 :                 }
    2062                 :             :                 else
    2063                 :             :                 {
    2064                 :             :                         /*
    2065                 :             :                          * Loop: Call pg_strxfrm(), possibly enlarge buffer, and try
    2066                 :             :                          * again.  The pg_strxfrm() function leaves the result buffer
    2067                 :             :                          * content undefined if the result did not fit, so we need to
    2068                 :             :                          * retry until everything fits, even though we only need the first
    2069                 :             :                          * few bytes in the end.
    2070                 :             :                          */
    2071                 :           0 :                         for (;;)
    2072                 :             :                         {
    2073                 :           0 :                                 bsize = pg_strxfrm(sss->buf2, sss->buf1, sss->buflen2,
    2074                 :           0 :                                                                    sss->locale);
    2075                 :             : 
    2076                 :           0 :                                 sss->last_len2 = bsize;
    2077         [ #  # ]:           0 :                                 if (bsize < sss->buflen2)
    2078                 :           0 :                                         break;
    2079                 :             : 
    2080                 :             :                                 /*
    2081                 :             :                                  * Grow buffer and retry.
    2082                 :             :                                  */
    2083   [ #  #  #  #  :           0 :                                 sss->buflen2 = Max(bsize + 1,
                   #  # ]
    2084                 :             :                                                                    Min(sss->buflen2 * 2, MaxAllocSize));
    2085                 :           0 :                                 sss->buf2 = repalloc(sss->buf2, sss->buflen2);
    2086                 :             :                         }
    2087                 :             :                 }
    2088                 :             : 
    2089                 :             :                 /*
    2090                 :             :                  * Every Datum byte is always compared.  This is safe because the
    2091                 :             :                  * strxfrm() blob is itself NUL terminated, leaving no danger of
    2092                 :             :                  * misinterpreting any NUL bytes not intended to be interpreted as
    2093                 :             :                  * logically representing termination.
    2094                 :             :                  */
    2095         [ -  + ]:         274 :                 memcpy(pres, sss->buf2, Min(max_prefix_bytes, bsize));
    2096      [ +  -  + ]:         302 :         }
    2097                 :             : 
    2098                 :             :         /*
    2099                 :             :          * Maintain approximate cardinality of both abbreviated keys and original,
    2100                 :             :          * authoritative keys using HyperLogLog.  Used as cheap insurance against
    2101                 :             :          * the worst case, where we do many string transformations for no saving
    2102                 :             :          * in full strcoll()-based comparisons.  These statistics are used by
    2103                 :             :          * varstr_abbrev_abort().
    2104                 :             :          *
    2105                 :             :          * First, Hash key proper, or a significant fraction of it.  Mix in length
    2106                 :             :          * in order to compensate for cases where differences are past
    2107                 :             :          * PG_CACHE_LINE_SIZE bytes, so as to limit the overhead of hashing.
    2108                 :             :          */
    2109                 :      151186 :         hash = DatumGetUInt32(hash_any((unsigned char *) authoritative_data,
    2110         [ +  + ]:       75593 :                                                                    Min(len, PG_CACHE_LINE_SIZE)));
    2111                 :             : 
    2112         [ +  + ]:       75593 :         if (len > PG_CACHE_LINE_SIZE)
    2113                 :           1 :                 hash ^= DatumGetUInt32(hash_uint32((uint32) len));
    2114                 :             : 
    2115                 :       75593 :         addHyperLogLog(&sss->full_card, hash);
    2116                 :             : 
    2117                 :             :         /* Hash abbreviated key */
    2118                 :             :         {
    2119                 :       75593 :                 uint32          tmp;
    2120                 :             : 
    2121                 :       75593 :                 tmp = DatumGetUInt32(res) ^ (uint32) (DatumGetUInt64(res) >> 32);
    2122                 :       75593 :                 hash = DatumGetUInt32(hash_uint32(tmp));
    2123                 :       75593 :         }
    2124                 :             : 
    2125                 :       75593 :         addHyperLogLog(&sss->abbr_card, hash);
    2126                 :             : 
    2127                 :             :         /* Cache result, perhaps saving an expensive strxfrm() call next time */
    2128                 :       75593 :         sss->cache_blob = true;
    2129                 :             : done:
    2130                 :             : 
    2131                 :             :         /*
    2132                 :             :          * Byteswap on little-endian machines.
    2133                 :             :          *
    2134                 :             :          * This is needed so that ssup_datum_unsigned_cmp() (an unsigned integer
    2135                 :             :          * 3-way comparator) works correctly on all platforms.  If we didn't do
    2136                 :             :          * this, the comparator would have to call memcmp() with a pair of
    2137                 :             :          * pointers to the first byte of each abbreviated key, which is slower.
    2138                 :             :          */
    2139                 :       75621 :         res = DatumBigEndianToNative(res);
    2140                 :             : 
    2141                 :             :         /* Don't leak memory here */
    2142         [ +  - ]:       75621 :         if (PointerGetDatum(authoritative) != original)
    2143                 :           0 :                 pfree(authoritative);
    2144                 :             : 
    2145                 :       75621 :         return res;
    2146                 :       75621 : }
    2147                 :             : 
    2148                 :             : /*
    2149                 :             :  * Callback for estimating effectiveness of abbreviated key optimization, using
    2150                 :             :  * heuristic rules.  Returns value indicating if the abbreviation optimization
    2151                 :             :  * should be aborted, based on its projected effectiveness.
    2152                 :             :  */
    2153                 :             : static bool
    2154                 :         225 : varstr_abbrev_abort(int memtupcount, SortSupport ssup)
    2155                 :             : {
    2156                 :         225 :         VarStringSortSupport *sss = (VarStringSortSupport *) ssup->ssup_extra;
    2157                 :         225 :         double          abbrev_distinct,
    2158                 :             :                                 key_distinct;
    2159                 :             : 
    2160         [ +  - ]:         225 :         Assert(ssup->abbreviate);
    2161                 :             : 
    2162                 :             :         /* Have a little patience */
    2163         [ +  + ]:         225 :         if (memtupcount < 100)
    2164                 :         116 :                 return false;
    2165                 :             : 
    2166                 :         109 :         abbrev_distinct = estimateHyperLogLog(&sss->abbr_card);
    2167                 :         109 :         key_distinct = estimateHyperLogLog(&sss->full_card);
    2168                 :             : 
    2169                 :             :         /*
    2170                 :             :          * Clamp cardinality estimates to at least one distinct value.  While
    2171                 :             :          * NULLs are generally disregarded, if only NULL values were seen so far,
    2172                 :             :          * that might misrepresent costs if we failed to clamp.
    2173                 :             :          */
    2174         [ +  - ]:         109 :         if (abbrev_distinct < 1.0)
    2175                 :           0 :                 abbrev_distinct = 1.0;
    2176                 :             : 
    2177         [ +  - ]:         109 :         if (key_distinct < 1.0)
    2178                 :           0 :                 key_distinct = 1.0;
    2179                 :             : 
    2180                 :             :         /*
    2181                 :             :          * In the worst case all abbreviated keys are identical, while at the same
    2182                 :             :          * time there are differences within full key strings not captured in
    2183                 :             :          * abbreviations.
    2184                 :             :          */
    2185         [ +  - ]:         109 :         if (trace_sort)
    2186                 :             :         {
    2187                 :           0 :                 double          norm_abbrev_card = abbrev_distinct / (double) memtupcount;
    2188                 :             : 
    2189   [ #  #  #  # ]:           0 :                 elog(LOG, "varstr_abbrev: abbrev_distinct after %d: %f "
    2190                 :             :                          "(key_distinct: %f, norm_abbrev_card: %f, prop_card: %f)",
    2191                 :             :                          memtupcount, abbrev_distinct, key_distinct, norm_abbrev_card,
    2192                 :             :                          sss->prop_card);
    2193                 :           0 :         }
    2194                 :             : 
    2195                 :             :         /*
    2196                 :             :          * If the number of distinct abbreviated keys approximately matches the
    2197                 :             :          * number of distinct authoritative original keys, that's reason enough to
    2198                 :             :          * proceed.  We can win even with a very low cardinality set if most
    2199                 :             :          * tie-breakers only memcmp().  This is by far the most important
    2200                 :             :          * consideration.
    2201                 :             :          *
    2202                 :             :          * While comparisons that are resolved at the abbreviated key level are
    2203                 :             :          * considerably cheaper than tie-breakers resolved with memcmp(), both of
    2204                 :             :          * those two outcomes are so much cheaper than a full strcoll() once
    2205                 :             :          * sorting is underway that it doesn't seem worth it to weigh abbreviated
    2206                 :             :          * cardinality against the overall size of the set in order to more
    2207                 :             :          * accurately model costs.  Assume that an abbreviated comparison, and an
    2208                 :             :          * abbreviated comparison with a cheap memcmp()-based authoritative
    2209                 :             :          * resolution are equivalent.
    2210                 :             :          */
    2211         [ +  - ]:         109 :         if (abbrev_distinct > key_distinct * sss->prop_card)
    2212                 :             :         {
    2213                 :             :                 /*
    2214                 :             :                  * When we have exceeded 10,000 tuples, decay required cardinality
    2215                 :             :                  * aggressively for next call.
    2216                 :             :                  *
    2217                 :             :                  * This is useful because the number of comparisons required on
    2218                 :             :                  * average increases at a linearithmic rate, and at roughly 10,000
    2219                 :             :                  * tuples that factor will start to dominate over the linear costs of
    2220                 :             :                  * string transformation (this is a conservative estimate).  The decay
    2221                 :             :                  * rate is chosen to be a little less aggressive than halving -- which
    2222                 :             :                  * (since we're called at points at which memtupcount has doubled)
    2223                 :             :                  * would never see the cost model actually abort past the first call
    2224                 :             :                  * following a decay.  This decay rate is mostly a precaution against
    2225                 :             :                  * a sudden, violent swing in how well abbreviated cardinality tracks
    2226                 :             :                  * full key cardinality.  The decay also serves to prevent a marginal
    2227                 :             :                  * case from being aborted too late, when too much has already been
    2228                 :             :                  * invested in string transformation.
    2229                 :             :                  *
    2230                 :             :                  * It's possible for sets of several million distinct strings with
    2231                 :             :                  * mere tens of thousands of distinct abbreviated keys to still
    2232                 :             :                  * benefit very significantly.  This will generally occur provided
    2233                 :             :                  * each abbreviated key is a proxy for a roughly uniform number of the
    2234                 :             :                  * set's full keys. If it isn't so, we hope to catch that early and
    2235                 :             :                  * abort.  If it isn't caught early, by the time the problem is
    2236                 :             :                  * apparent it's probably not worth aborting.
    2237                 :             :                  */
    2238         [ +  - ]:         109 :                 if (memtupcount > 10000)
    2239                 :           0 :                         sss->prop_card *= 0.65;
    2240                 :             : 
    2241                 :         109 :                 return false;
    2242                 :             :         }
    2243                 :             : 
    2244                 :             :         /*
    2245                 :             :          * Abort abbreviation strategy.
    2246                 :             :          *
    2247                 :             :          * The worst case, where all abbreviated keys are identical while all
    2248                 :             :          * original strings differ will typically only see a regression of about
    2249                 :             :          * 10% in execution time for small to medium sized lists of strings.
    2250                 :             :          * Whereas on modern CPUs where cache stalls are the dominant cost, we can
    2251                 :             :          * often expect very large improvements, particularly with sets of strings
    2252                 :             :          * of moderately high to high abbreviated cardinality.  There is little to
    2253                 :             :          * lose but much to gain, which our strategy reflects.
    2254                 :             :          */
    2255         [ #  # ]:           0 :         if (trace_sort)
    2256   [ #  #  #  # ]:           0 :                 elog(LOG, "varstr_abbrev: aborted abbreviation at %d "
    2257                 :             :                          "(abbrev_distinct: %f, key_distinct: %f, prop_card: %f)",
    2258                 :             :                          memtupcount, abbrev_distinct, key_distinct, sss->prop_card);
    2259                 :             : 
    2260                 :           0 :         return true;
    2261                 :         225 : }
    2262                 :             : 
    2263                 :             : /*
    2264                 :             :  * Generic equalimage support function for character type's operator classes.
    2265                 :             :  * Disables the use of deduplication with nondeterministic collations.
    2266                 :             :  */
    2267                 :             : Datum
    2268                 :         382 : btvarstrequalimage(PG_FUNCTION_ARGS)
    2269                 :             : {
    2270                 :             : #ifdef NOT_USED
    2271                 :             :         Oid                     opcintype = PG_GETARG_OID(0);
    2272                 :             : #endif
    2273                 :         382 :         Oid                     collid = PG_GET_COLLATION();
    2274                 :         382 :         pg_locale_t locale;
    2275                 :             : 
    2276                 :         382 :         check_collation_set(collid);
    2277                 :             : 
    2278                 :         382 :         locale = pg_newlocale_from_collation(collid);
    2279                 :             : 
    2280                 :         764 :         PG_RETURN_BOOL(locale->deterministic);
    2281                 :         382 : }
    2282                 :             : 
    2283                 :             : Datum
    2284                 :           0 : text_larger(PG_FUNCTION_ARGS)
    2285                 :             : {
    2286                 :           0 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    2287                 :           0 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    2288                 :           0 :         text       *result;
    2289                 :             : 
    2290         [ #  # ]:           0 :         result = ((text_cmp(arg1, arg2, PG_GET_COLLATION()) > 0) ? arg1 : arg2);
    2291                 :             : 
    2292                 :           0 :         PG_RETURN_TEXT_P(result);
    2293                 :           0 : }
    2294                 :             : 
    2295                 :             : Datum
    2296                 :           0 : text_smaller(PG_FUNCTION_ARGS)
    2297                 :             : {
    2298                 :           0 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    2299                 :           0 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    2300                 :           0 :         text       *result;
    2301                 :             : 
    2302         [ #  # ]:           0 :         result = ((text_cmp(arg1, arg2, PG_GET_COLLATION()) < 0) ? arg1 : arg2);
    2303                 :             : 
    2304                 :           0 :         PG_RETURN_TEXT_P(result);
    2305                 :           0 : }
    2306                 :             : 
    2307                 :             : 
    2308                 :             : /*
    2309                 :             :  * Cross-type comparison functions for types text and name.
    2310                 :             :  */
    2311                 :             : 
    2312                 :             : Datum
    2313                 :           0 : nameeqtext(PG_FUNCTION_ARGS)
    2314                 :             : {
    2315                 :           0 :         Name            arg1 = PG_GETARG_NAME(0);
    2316                 :           0 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    2317                 :           0 :         size_t          len1 = strlen(NameStr(*arg1));
    2318                 :           0 :         size_t          len2 = VARSIZE_ANY_EXHDR(arg2);
    2319                 :           0 :         Oid                     collid = PG_GET_COLLATION();
    2320                 :           0 :         bool            result;
    2321                 :             : 
    2322                 :           0 :         check_collation_set(collid);
    2323                 :             : 
    2324         [ #  # ]:           0 :         if (collid == C_COLLATION_OID)
    2325         [ #  # ]:           0 :                 result = (len1 == len2 &&
    2326                 :           0 :                                   memcmp(NameStr(*arg1), VARDATA_ANY(arg2), len1) == 0);
    2327                 :             :         else
    2328                 :           0 :                 result = (varstr_cmp(NameStr(*arg1), len1,
    2329                 :           0 :                                                          VARDATA_ANY(arg2), len2,
    2330                 :           0 :                                                          collid) == 0);
    2331                 :             : 
    2332         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg2, 1);
    2333                 :             : 
    2334                 :           0 :         PG_RETURN_BOOL(result);
    2335                 :           0 : }
    2336                 :             : 
    2337                 :             : Datum
    2338                 :           0 : texteqname(PG_FUNCTION_ARGS)
    2339                 :             : {
    2340                 :           0 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    2341                 :           0 :         Name            arg2 = PG_GETARG_NAME(1);
    2342                 :           0 :         size_t          len1 = VARSIZE_ANY_EXHDR(arg1);
    2343                 :           0 :         size_t          len2 = strlen(NameStr(*arg2));
    2344                 :           0 :         Oid                     collid = PG_GET_COLLATION();
    2345                 :           0 :         bool            result;
    2346                 :             : 
    2347                 :           0 :         check_collation_set(collid);
    2348                 :             : 
    2349         [ #  # ]:           0 :         if (collid == C_COLLATION_OID)
    2350         [ #  # ]:           0 :                 result = (len1 == len2 &&
    2351                 :           0 :                                   memcmp(VARDATA_ANY(arg1), NameStr(*arg2), len1) == 0);
    2352                 :             :         else
    2353                 :           0 :                 result = (varstr_cmp(VARDATA_ANY(arg1), len1,
    2354                 :           0 :                                                          NameStr(*arg2), len2,
    2355                 :           0 :                                                          collid) == 0);
    2356                 :             : 
    2357         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg1, 0);
    2358                 :             : 
    2359                 :           0 :         PG_RETURN_BOOL(result);
    2360                 :           0 : }
    2361                 :             : 
    2362                 :             : Datum
    2363                 :           0 : namenetext(PG_FUNCTION_ARGS)
    2364                 :             : {
    2365                 :           0 :         Name            arg1 = PG_GETARG_NAME(0);
    2366                 :           0 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    2367                 :           0 :         size_t          len1 = strlen(NameStr(*arg1));
    2368                 :           0 :         size_t          len2 = VARSIZE_ANY_EXHDR(arg2);
    2369                 :           0 :         Oid                     collid = PG_GET_COLLATION();
    2370                 :           0 :         bool            result;
    2371                 :             : 
    2372                 :           0 :         check_collation_set(collid);
    2373                 :             : 
    2374         [ #  # ]:           0 :         if (collid == C_COLLATION_OID)
    2375         [ #  # ]:           0 :                 result = !(len1 == len2 &&
    2376                 :           0 :                                    memcmp(NameStr(*arg1), VARDATA_ANY(arg2), len1) == 0);
    2377                 :             :         else
    2378                 :           0 :                 result = !(varstr_cmp(NameStr(*arg1), len1,
    2379                 :           0 :                                                           VARDATA_ANY(arg2), len2,
    2380                 :           0 :                                                           collid) == 0);
    2381                 :             : 
    2382         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg2, 1);
    2383                 :             : 
    2384                 :           0 :         PG_RETURN_BOOL(result);
    2385                 :           0 : }
    2386                 :             : 
    2387                 :             : Datum
    2388                 :           0 : textnename(PG_FUNCTION_ARGS)
    2389                 :             : {
    2390                 :           0 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    2391                 :           0 :         Name            arg2 = PG_GETARG_NAME(1);
    2392                 :           0 :         size_t          len1 = VARSIZE_ANY_EXHDR(arg1);
    2393                 :           0 :         size_t          len2 = strlen(NameStr(*arg2));
    2394                 :           0 :         Oid                     collid = PG_GET_COLLATION();
    2395                 :           0 :         bool            result;
    2396                 :             : 
    2397                 :           0 :         check_collation_set(collid);
    2398                 :             : 
    2399         [ #  # ]:           0 :         if (collid == C_COLLATION_OID)
    2400         [ #  # ]:           0 :                 result = !(len1 == len2 &&
    2401                 :           0 :                                    memcmp(VARDATA_ANY(arg1), NameStr(*arg2), len1) == 0);
    2402                 :             :         else
    2403                 :           0 :                 result = !(varstr_cmp(VARDATA_ANY(arg1), len1,
    2404                 :           0 :                                                           NameStr(*arg2), len2,
    2405                 :           0 :                                                           collid) == 0);
    2406                 :             : 
    2407         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg1, 0);
    2408                 :             : 
    2409                 :           0 :         PG_RETURN_BOOL(result);
    2410                 :           0 : }
    2411                 :             : 
    2412                 :             : Datum
    2413                 :           0 : btnametextcmp(PG_FUNCTION_ARGS)
    2414                 :             : {
    2415                 :           0 :         Name            arg1 = PG_GETARG_NAME(0);
    2416                 :           0 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    2417                 :           0 :         int32           result;
    2418                 :             : 
    2419                 :           0 :         result = varstr_cmp(NameStr(*arg1), strlen(NameStr(*arg1)),
    2420                 :           0 :                                                 VARDATA_ANY(arg2), VARSIZE_ANY_EXHDR(arg2),
    2421                 :           0 :                                                 PG_GET_COLLATION());
    2422                 :             : 
    2423         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg2, 1);
    2424                 :             : 
    2425                 :           0 :         PG_RETURN_INT32(result);
    2426                 :           0 : }
    2427                 :             : 
    2428                 :             : Datum
    2429                 :           0 : bttextnamecmp(PG_FUNCTION_ARGS)
    2430                 :             : {
    2431                 :           0 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    2432                 :           0 :         Name            arg2 = PG_GETARG_NAME(1);
    2433                 :           0 :         int32           result;
    2434                 :             : 
    2435                 :           0 :         result = varstr_cmp(VARDATA_ANY(arg1), VARSIZE_ANY_EXHDR(arg1),
    2436                 :           0 :                                                 NameStr(*arg2), strlen(NameStr(*arg2)),
    2437                 :           0 :                                                 PG_GET_COLLATION());
    2438                 :             : 
    2439         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg1, 0);
    2440                 :             : 
    2441                 :           0 :         PG_RETURN_INT32(result);
    2442                 :           0 : }
    2443                 :             : 
    2444                 :             : #define CmpCall(cmpfunc) \
    2445                 :             :         DatumGetInt32(DirectFunctionCall2Coll(cmpfunc, \
    2446                 :             :                                                                                   PG_GET_COLLATION(), \
    2447                 :             :                                                                                   PG_GETARG_DATUM(0), \
    2448                 :             :                                                                                   PG_GETARG_DATUM(1)))
    2449                 :             : 
    2450                 :             : Datum
    2451                 :           0 : namelttext(PG_FUNCTION_ARGS)
    2452                 :             : {
    2453                 :           0 :         PG_RETURN_BOOL(CmpCall(btnametextcmp) < 0);
    2454                 :             : }
    2455                 :             : 
    2456                 :             : Datum
    2457                 :           0 : nameletext(PG_FUNCTION_ARGS)
    2458                 :             : {
    2459                 :           0 :         PG_RETURN_BOOL(CmpCall(btnametextcmp) <= 0);
    2460                 :             : }
    2461                 :             : 
    2462                 :             : Datum
    2463                 :           0 : namegttext(PG_FUNCTION_ARGS)
    2464                 :             : {
    2465                 :           0 :         PG_RETURN_BOOL(CmpCall(btnametextcmp) > 0);
    2466                 :             : }
    2467                 :             : 
    2468                 :             : Datum
    2469                 :           0 : namegetext(PG_FUNCTION_ARGS)
    2470                 :             : {
    2471                 :           0 :         PG_RETURN_BOOL(CmpCall(btnametextcmp) >= 0);
    2472                 :             : }
    2473                 :             : 
    2474                 :             : Datum
    2475                 :           0 : textltname(PG_FUNCTION_ARGS)
    2476                 :             : {
    2477                 :           0 :         PG_RETURN_BOOL(CmpCall(bttextnamecmp) < 0);
    2478                 :             : }
    2479                 :             : 
    2480                 :             : Datum
    2481                 :           0 : textlename(PG_FUNCTION_ARGS)
    2482                 :             : {
    2483                 :           0 :         PG_RETURN_BOOL(CmpCall(bttextnamecmp) <= 0);
    2484                 :             : }
    2485                 :             : 
    2486                 :             : Datum
    2487                 :           0 : textgtname(PG_FUNCTION_ARGS)
    2488                 :             : {
    2489                 :           0 :         PG_RETURN_BOOL(CmpCall(bttextnamecmp) > 0);
    2490                 :             : }
    2491                 :             : 
    2492                 :             : Datum
    2493                 :           0 : textgename(PG_FUNCTION_ARGS)
    2494                 :             : {
    2495                 :           0 :         PG_RETURN_BOOL(CmpCall(bttextnamecmp) >= 0);
    2496                 :             : }
    2497                 :             : 
    2498                 :             : #undef CmpCall
    2499                 :             : 
    2500                 :             : 
    2501                 :             : /*
    2502                 :             :  * The following operators support character-by-character comparison
    2503                 :             :  * of text datums, to allow building indexes suitable for LIKE clauses.
    2504                 :             :  * Note that the regular texteq/textne comparison operators, and regular
    2505                 :             :  * support functions 1 and 2 with "C" collation are assumed to be
    2506                 :             :  * compatible with these!
    2507                 :             :  */
    2508                 :             : 
    2509                 :             : static int
    2510                 :           0 : internal_text_pattern_compare(text *arg1, text *arg2)
    2511                 :             : {
    2512                 :           0 :         int                     result;
    2513                 :           0 :         int                     len1,
    2514                 :             :                                 len2;
    2515                 :             : 
    2516                 :           0 :         len1 = VARSIZE_ANY_EXHDR(arg1);
    2517                 :           0 :         len2 = VARSIZE_ANY_EXHDR(arg2);
    2518                 :             : 
    2519         [ #  # ]:           0 :         result = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2));
    2520         [ #  # ]:           0 :         if (result != 0)
    2521                 :           0 :                 return result;
    2522         [ #  # ]:           0 :         else if (len1 < len2)
    2523                 :           0 :                 return -1;
    2524         [ #  # ]:           0 :         else if (len1 > len2)
    2525                 :           0 :                 return 1;
    2526                 :             :         else
    2527                 :           0 :                 return 0;
    2528                 :           0 : }
    2529                 :             : 
    2530                 :             : 
    2531                 :             : Datum
    2532                 :           0 : text_pattern_lt(PG_FUNCTION_ARGS)
    2533                 :             : {
    2534                 :           0 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    2535                 :           0 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    2536                 :           0 :         int                     result;
    2537                 :             : 
    2538                 :           0 :         result = internal_text_pattern_compare(arg1, arg2);
    2539                 :             : 
    2540         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg1, 0);
    2541         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg2, 1);
    2542                 :             : 
    2543                 :           0 :         PG_RETURN_BOOL(result < 0);
    2544                 :           0 : }
    2545                 :             : 
    2546                 :             : 
    2547                 :             : Datum
    2548                 :           0 : text_pattern_le(PG_FUNCTION_ARGS)
    2549                 :             : {
    2550                 :           0 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    2551                 :           0 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    2552                 :           0 :         int                     result;
    2553                 :             : 
    2554                 :           0 :         result = internal_text_pattern_compare(arg1, arg2);
    2555                 :             : 
    2556         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg1, 0);
    2557         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg2, 1);
    2558                 :             : 
    2559                 :           0 :         PG_RETURN_BOOL(result <= 0);
    2560                 :           0 : }
    2561                 :             : 
    2562                 :             : 
    2563                 :             : Datum
    2564                 :           0 : text_pattern_ge(PG_FUNCTION_ARGS)
    2565                 :             : {
    2566                 :           0 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    2567                 :           0 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    2568                 :           0 :         int                     result;
    2569                 :             : 
    2570                 :           0 :         result = internal_text_pattern_compare(arg1, arg2);
    2571                 :             : 
    2572         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg1, 0);
    2573         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg2, 1);
    2574                 :             : 
    2575                 :           0 :         PG_RETURN_BOOL(result >= 0);
    2576                 :           0 : }
    2577                 :             : 
    2578                 :             : 
    2579                 :             : Datum
    2580                 :           0 : text_pattern_gt(PG_FUNCTION_ARGS)
    2581                 :             : {
    2582                 :           0 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    2583                 :           0 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    2584                 :           0 :         int                     result;
    2585                 :             : 
    2586                 :           0 :         result = internal_text_pattern_compare(arg1, arg2);
    2587                 :             : 
    2588         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg1, 0);
    2589         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg2, 1);
    2590                 :             : 
    2591                 :           0 :         PG_RETURN_BOOL(result > 0);
    2592                 :           0 : }
    2593                 :             : 
    2594                 :             : 
    2595                 :             : Datum
    2596                 :           0 : bttext_pattern_cmp(PG_FUNCTION_ARGS)
    2597                 :             : {
    2598                 :           0 :         text       *arg1 = PG_GETARG_TEXT_PP(0);
    2599                 :           0 :         text       *arg2 = PG_GETARG_TEXT_PP(1);
    2600                 :           0 :         int                     result;
    2601                 :             : 
    2602                 :           0 :         result = internal_text_pattern_compare(arg1, arg2);
    2603                 :             : 
    2604         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg1, 0);
    2605         [ #  # ]:           0 :         PG_FREE_IF_COPY(arg2, 1);
    2606                 :             : 
    2607                 :           0 :         PG_RETURN_INT32(result);
    2608                 :           0 : }
    2609                 :             : 
    2610                 :             : 
    2611                 :             : Datum
    2612                 :           0 : bttext_pattern_sortsupport(PG_FUNCTION_ARGS)
    2613                 :             : {
    2614                 :           0 :         SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
    2615                 :           0 :         MemoryContext oldcontext;
    2616                 :             : 
    2617                 :           0 :         oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
    2618                 :             : 
    2619                 :             :         /* Use generic string SortSupport, forcing "C" collation */
    2620                 :           0 :         varstr_sortsupport(ssup, TEXTOID, C_COLLATION_OID);
    2621                 :             : 
    2622                 :           0 :         MemoryContextSwitchTo(oldcontext);
    2623                 :             : 
    2624                 :           0 :         PG_RETURN_VOID();
    2625                 :           0 : }
    2626                 :             : 
    2627                 :             : 
    2628                 :             : /* text_name()
    2629                 :             :  * Converts a text type to a Name type.
    2630                 :             :  */
    2631                 :             : Datum
    2632                 :           0 : text_name(PG_FUNCTION_ARGS)
    2633                 :             : {
    2634                 :           0 :         text       *s = PG_GETARG_TEXT_PP(0);
    2635                 :           0 :         Name            result;
    2636                 :           0 :         int                     len;
    2637                 :             : 
    2638                 :           0 :         len = VARSIZE_ANY_EXHDR(s);
    2639                 :             : 
    2640                 :             :         /* Truncate oversize input */
    2641         [ #  # ]:           0 :         if (len >= NAMEDATALEN)
    2642                 :           0 :                 len = pg_mbcliplen(VARDATA_ANY(s), len, NAMEDATALEN - 1);
    2643                 :             : 
    2644                 :             :         /* We use palloc0 here to ensure result is zero-padded */
    2645                 :           0 :         result = (Name) palloc0(NAMEDATALEN);
    2646                 :           0 :         memcpy(NameStr(*result), VARDATA_ANY(s), len);
    2647                 :             : 
    2648                 :           0 :         PG_RETURN_NAME(result);
    2649                 :           0 : }
    2650                 :             : 
    2651                 :             : /* name_text()
    2652                 :             :  * Converts a Name type to a text type.
    2653                 :             :  */
    2654                 :             : Datum
    2655                 :           0 : name_text(PG_FUNCTION_ARGS)
    2656                 :             : {
    2657                 :           0 :         Name            s = PG_GETARG_NAME(0);
    2658                 :             : 
    2659                 :           0 :         PG_RETURN_TEXT_P(cstring_to_text(NameStr(*s)));
    2660                 :           0 : }
    2661                 :             : 
    2662                 :             : 
    2663                 :             : /*
    2664                 :             :  * textToQualifiedNameList - convert a text object to list of names
    2665                 :             :  *
    2666                 :             :  * This implements the input parsing needed by nextval() and other
    2667                 :             :  * functions that take a text parameter representing a qualified name.
    2668                 :             :  * We split the name at dots, downcase if not double-quoted, and
    2669                 :             :  * truncate names if they're too long.
    2670                 :             :  */
    2671                 :             : List *
    2672                 :           0 : textToQualifiedNameList(text *textval)
    2673                 :             : {
    2674                 :           0 :         char       *rawname;
    2675                 :           0 :         List       *result = NIL;
    2676                 :           0 :         List       *namelist;
    2677                 :           0 :         ListCell   *l;
    2678                 :             : 
    2679                 :             :         /* Convert to C string (handles possible detoasting). */
    2680                 :             :         /* Note we rely on being able to modify rawname below. */
    2681                 :           0 :         rawname = text_to_cstring(textval);
    2682                 :             : 
    2683         [ #  # ]:           0 :         if (!SplitIdentifierString(rawname, '.', &namelist))
    2684   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    2685                 :             :                                 (errcode(ERRCODE_INVALID_NAME),
    2686                 :             :                                  errmsg("invalid name syntax")));
    2687                 :             : 
    2688         [ #  # ]:           0 :         if (namelist == NIL)
    2689   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    2690                 :             :                                 (errcode(ERRCODE_INVALID_NAME),
    2691                 :             :                                  errmsg("invalid name syntax")));
    2692                 :             : 
    2693   [ #  #  #  #  :           0 :         foreach(l, namelist)
                   #  # ]
    2694                 :             :         {
    2695                 :           0 :                 char       *curname = (char *) lfirst(l);
    2696                 :             : 
    2697                 :           0 :                 result = lappend(result, makeString(pstrdup(curname)));
    2698                 :           0 :         }
    2699                 :             : 
    2700                 :           0 :         pfree(rawname);
    2701                 :           0 :         list_free(namelist);
    2702                 :             : 
    2703                 :           0 :         return result;
    2704                 :           0 : }
    2705                 :             : 
    2706                 :             : /*
    2707                 :             :  * SplitIdentifierString --- parse a string containing identifiers
    2708                 :             :  *
    2709                 :             :  * This is the guts of textToQualifiedNameList, and is exported for use in
    2710                 :             :  * other situations such as parsing GUC variables.  In the GUC case, it's
    2711                 :             :  * important to avoid memory leaks, so the API is designed to minimize the
    2712                 :             :  * amount of stuff that needs to be allocated and freed.
    2713                 :             :  *
    2714                 :             :  * Inputs:
    2715                 :             :  *      rawstring: the input string; must be overwritable!      On return, it's
    2716                 :             :  *                         been modified to contain the separated identifiers.
    2717                 :             :  *      separator: the separator punctuation expected between identifiers
    2718                 :             :  *                         (typically '.' or ',').  Whitespace may also appear around
    2719                 :             :  *                         identifiers.
    2720                 :             :  * Outputs:
    2721                 :             :  *      namelist: filled with a palloc'd list of pointers to identifiers within
    2722                 :             :  *                        rawstring.  Caller should list_free() this even on error return.
    2723                 :             :  *
    2724                 :             :  * Returns true if okay, false if there is a syntax error in the string.
    2725                 :             :  *
    2726                 :             :  * Note that an empty string is considered okay here, though not in
    2727                 :             :  * textToQualifiedNameList.
    2728                 :             :  */
    2729                 :             : bool
    2730                 :           0 : SplitIdentifierString(char *rawstring, char separator,
    2731                 :             :                                           List **namelist)
    2732                 :             : {
    2733                 :           0 :         char       *nextp = rawstring;
    2734                 :           0 :         bool            done = false;
    2735                 :             : 
    2736                 :           0 :         *namelist = NIL;
    2737                 :             : 
    2738         [ #  # ]:           0 :         while (scanner_isspace(*nextp))
    2739                 :           0 :                 nextp++;                                /* skip leading whitespace */
    2740                 :             : 
    2741         [ #  # ]:           0 :         if (*nextp == '\0')
    2742                 :           0 :                 return true;                    /* empty string represents empty list */
    2743                 :             : 
    2744                 :             :         /* At the top of the loop, we are at start of a new identifier. */
    2745                 :           0 :         do
    2746                 :             :         {
    2747                 :           0 :                 char       *curname;
    2748                 :           0 :                 char       *endp;
    2749                 :             : 
    2750         [ #  # ]:           0 :                 if (*nextp == '"')
    2751                 :             :                 {
    2752                 :             :                         /* Quoted name --- collapse quote-quote pairs, no downcasing */
    2753                 :           0 :                         curname = nextp + 1;
    2754                 :           0 :                         for (;;)
    2755                 :             :                         {
    2756                 :           0 :                                 endp = strchr(nextp + 1, '"');
    2757         [ #  # ]:           0 :                                 if (endp == NULL)
    2758                 :           0 :                                         return false;   /* mismatched quotes */
    2759         [ #  # ]:           0 :                                 if (endp[1] != '"')
    2760                 :           0 :                                         break;          /* found end of quoted name */
    2761                 :             :                                 /* Collapse adjacent quotes into one quote, and look again */
    2762                 :           0 :                                 memmove(endp, endp + 1, strlen(endp));
    2763                 :           0 :                                 nextp = endp;
    2764                 :             :                         }
    2765                 :             :                         /* endp now points at the terminating quote */
    2766                 :           0 :                         nextp = endp + 1;
    2767                 :           0 :                 }
    2768                 :             :                 else
    2769                 :             :                 {
    2770                 :             :                         /* Unquoted name --- extends to separator or whitespace */
    2771                 :           0 :                         char       *downname;
    2772                 :           0 :                         int                     len;
    2773                 :             : 
    2774                 :           0 :                         curname = nextp;
    2775   [ #  #  #  #  :           0 :                         while (*nextp && *nextp != separator &&
                   #  # ]
    2776                 :           0 :                                    !scanner_isspace(*nextp))
    2777                 :           0 :                                 nextp++;
    2778                 :           0 :                         endp = nextp;
    2779         [ #  # ]:           0 :                         if (curname == nextp)
    2780                 :           0 :                                 return false;   /* empty unquoted name not allowed */
    2781                 :             : 
    2782                 :             :                         /*
    2783                 :             :                          * Downcase the identifier, using same code as main lexer does.
    2784                 :             :                          *
    2785                 :             :                          * XXX because we want to overwrite the input in-place, we cannot
    2786                 :             :                          * support a downcasing transformation that increases the string
    2787                 :             :                          * length.  This is not a problem given the current implementation
    2788                 :             :                          * of downcase_truncate_identifier, but we'll probably have to do
    2789                 :             :                          * something about this someday.
    2790                 :             :                          */
    2791                 :           0 :                         len = endp - curname;
    2792                 :           0 :                         downname = downcase_truncate_identifier(curname, len, false);
    2793         [ #  # ]:           0 :                         Assert(strlen(downname) <= len);
    2794                 :           0 :                         strncpy(curname, downname, len);        /* strncpy is required here */
    2795                 :           0 :                         pfree(downname);
    2796         [ #  # ]:           0 :                 }
    2797                 :             : 
    2798         [ #  # ]:           0 :                 while (scanner_isspace(*nextp))
    2799                 :           0 :                         nextp++;                        /* skip trailing whitespace */
    2800                 :             : 
    2801         [ #  # ]:           0 :                 if (*nextp == separator)
    2802                 :             :                 {
    2803                 :           0 :                         nextp++;
    2804         [ #  # ]:           0 :                         while (scanner_isspace(*nextp))
    2805                 :           0 :                                 nextp++;                /* skip leading whitespace for next */
    2806                 :             :                         /* we expect another name, so done remains false */
    2807                 :           0 :                 }
    2808         [ #  # ]:           0 :                 else if (*nextp == '\0')
    2809                 :           0 :                         done = true;
    2810                 :             :                 else
    2811                 :           0 :                         return false;           /* invalid syntax */
    2812                 :             : 
    2813                 :             :                 /* Now safe to overwrite separator with a null */
    2814                 :           0 :                 *endp = '\0';
    2815                 :             : 
    2816                 :             :                 /* Truncate name if it's overlength */
    2817                 :           0 :                 truncate_identifier(curname, strlen(curname), false);
    2818                 :             : 
    2819                 :             :                 /*
    2820                 :             :                  * Finished isolating current name --- add it to list
    2821                 :             :                  */
    2822                 :           0 :                 *namelist = lappend(*namelist, curname);
    2823                 :             : 
    2824                 :             :                 /* Loop back if we didn't reach end of string */
    2825   [ #  #  #  # ]:           0 :         } while (!done);
    2826                 :             : 
    2827                 :           0 :         return true;
    2828                 :           0 : }
    2829                 :             : 
    2830                 :             : 
    2831                 :             : /*
    2832                 :             :  * SplitDirectoriesString --- parse a string containing file/directory names
    2833                 :             :  *
    2834                 :             :  * This works fine on file names too; the function name is historical.
    2835                 :             :  *
    2836                 :             :  * This is similar to SplitIdentifierString, except that the parsing
    2837                 :             :  * rules are meant to handle pathnames instead of identifiers: there is
    2838                 :             :  * no downcasing, embedded spaces are allowed, the max length is MAXPGPATH-1,
    2839                 :             :  * and we apply canonicalize_path() to each extracted string.  Because of the
    2840                 :             :  * last, the returned strings are separately palloc'd rather than being
    2841                 :             :  * pointers into rawstring --- but we still scribble on rawstring.
    2842                 :             :  *
    2843                 :             :  * Inputs:
    2844                 :             :  *      rawstring: the input string; must be modifiable!
    2845                 :             :  *      separator: the separator punctuation expected between directories
    2846                 :             :  *                         (typically ',' or ';').  Whitespace may also appear around
    2847                 :             :  *                         directories.
    2848                 :             :  * Outputs:
    2849                 :             :  *      namelist: filled with a palloc'd list of directory names.
    2850                 :             :  *                        Caller should list_free_deep() this even on error return.
    2851                 :             :  *
    2852                 :             :  * Returns true if okay, false if there is a syntax error in the string.
    2853                 :             :  *
    2854                 :             :  * Note that an empty string is considered okay here.
    2855                 :             :  */
    2856                 :             : bool
    2857                 :           0 : SplitDirectoriesString(char *rawstring, char separator,
    2858                 :             :                                            List **namelist)
    2859                 :             : {
    2860                 :           0 :         char       *nextp = rawstring;
    2861                 :           0 :         bool            done = false;
    2862                 :             : 
    2863                 :           0 :         *namelist = NIL;
    2864                 :             : 
    2865         [ #  # ]:           0 :         while (scanner_isspace(*nextp))
    2866                 :           0 :                 nextp++;                                /* skip leading whitespace */
    2867                 :             : 
    2868         [ #  # ]:           0 :         if (*nextp == '\0')
    2869                 :           0 :                 return true;                    /* empty string represents empty list */
    2870                 :             : 
    2871                 :             :         /* At the top of the loop, we are at start of a new directory. */
    2872                 :           0 :         do
    2873                 :             :         {
    2874                 :           0 :                 char       *curname;
    2875                 :           0 :                 char       *endp;
    2876                 :             : 
    2877         [ #  # ]:           0 :                 if (*nextp == '"')
    2878                 :             :                 {
    2879                 :             :                         /* Quoted name --- collapse quote-quote pairs */
    2880                 :           0 :                         curname = nextp + 1;
    2881                 :           0 :                         for (;;)
    2882                 :             :                         {
    2883                 :           0 :                                 endp = strchr(nextp + 1, '"');
    2884         [ #  # ]:           0 :                                 if (endp == NULL)
    2885                 :           0 :                                         return false;   /* mismatched quotes */
    2886         [ #  # ]:           0 :                                 if (endp[1] != '"')
    2887                 :           0 :                                         break;          /* found end of quoted name */
    2888                 :             :                                 /* Collapse adjacent quotes into one quote, and look again */
    2889                 :           0 :                                 memmove(endp, endp + 1, strlen(endp));
    2890                 :           0 :                                 nextp = endp;
    2891                 :             :                         }
    2892                 :             :                         /* endp now points at the terminating quote */
    2893                 :           0 :                         nextp = endp + 1;
    2894                 :           0 :                 }
    2895                 :             :                 else
    2896                 :             :                 {
    2897                 :             :                         /* Unquoted name --- extends to separator or end of string */
    2898                 :           0 :                         curname = endp = nextp;
    2899   [ #  #  #  # ]:           0 :                         while (*nextp && *nextp != separator)
    2900                 :             :                         {
    2901                 :             :                                 /* trailing whitespace should not be included in name */
    2902         [ #  # ]:           0 :                                 if (!scanner_isspace(*nextp))
    2903                 :           0 :                                         endp = nextp + 1;
    2904                 :           0 :                                 nextp++;
    2905                 :             :                         }
    2906         [ #  # ]:           0 :                         if (curname == endp)
    2907                 :           0 :                                 return false;   /* empty unquoted name not allowed */
    2908                 :             :                 }
    2909                 :             : 
    2910         [ #  # ]:           0 :                 while (scanner_isspace(*nextp))
    2911                 :           0 :                         nextp++;                        /* skip trailing whitespace */
    2912                 :             : 
    2913         [ #  # ]:           0 :                 if (*nextp == separator)
    2914                 :             :                 {
    2915                 :           0 :                         nextp++;
    2916         [ #  # ]:           0 :                         while (scanner_isspace(*nextp))
    2917                 :           0 :                                 nextp++;                /* skip leading whitespace for next */
    2918                 :             :                         /* we expect another name, so done remains false */
    2919                 :           0 :                 }
    2920         [ #  # ]:           0 :                 else if (*nextp == '\0')
    2921                 :           0 :                         done = true;
    2922                 :             :                 else
    2923                 :           0 :                         return false;           /* invalid syntax */
    2924                 :             : 
    2925                 :             :                 /* Now safe to overwrite separator with a null */
    2926                 :           0 :                 *endp = '\0';
    2927                 :             : 
    2928                 :             :                 /* Truncate path if it's overlength */
    2929         [ #  # ]:           0 :                 if (strlen(curname) >= MAXPGPATH)
    2930                 :           0 :                         curname[MAXPGPATH - 1] = '\0';
    2931                 :             : 
    2932                 :             :                 /*
    2933                 :             :                  * Finished isolating current name --- add it to list
    2934                 :             :                  */
    2935                 :           0 :                 curname = pstrdup(curname);
    2936                 :           0 :                 canonicalize_path(curname);
    2937                 :           0 :                 *namelist = lappend(*namelist, curname);
    2938                 :             : 
    2939                 :             :                 /* Loop back if we didn't reach end of string */
    2940   [ #  #  #  # ]:           0 :         } while (!done);
    2941                 :             : 
    2942                 :           0 :         return true;
    2943                 :           0 : }
    2944                 :             : 
    2945                 :             : 
    2946                 :             : /*
    2947                 :             :  * SplitGUCList --- parse a string containing identifiers or file names
    2948                 :             :  *
    2949                 :             :  * This is used to split the value of a GUC_LIST_QUOTE GUC variable, without
    2950                 :             :  * presuming whether the elements will be taken as identifiers or file names.
    2951                 :             :  * We assume the input has already been through flatten_set_variable_args(),
    2952                 :             :  * so that we need never downcase (if appropriate, that was done already).
    2953                 :             :  * Nor do we ever truncate, since we don't know the correct max length.
    2954                 :             :  * We disallow embedded whitespace for simplicity (it shouldn't matter,
    2955                 :             :  * because any embedded whitespace should have led to double-quoting).
    2956                 :             :  * Otherwise the API is identical to SplitIdentifierString.
    2957                 :             :  *
    2958                 :             :  * XXX it's annoying to have so many copies of this string-splitting logic.
    2959                 :             :  * However, it's not clear that having one function with a bunch of option
    2960                 :             :  * flags would be much better.
    2961                 :             :  *
    2962                 :             :  * XXX there is a version of this function in src/bin/pg_dump/dumputils.c.
    2963                 :             :  * Be sure to update that if you have to change this.
    2964                 :             :  *
    2965                 :             :  * Inputs:
    2966                 :             :  *      rawstring: the input string; must be overwritable!      On return, it's
    2967                 :             :  *                         been modified to contain the separated identifiers.
    2968                 :             :  *      separator: the separator punctuation expected between identifiers
    2969                 :             :  *                         (typically '.' or ',').  Whitespace may also appear around
    2970                 :             :  *                         identifiers.
    2971                 :             :  * Outputs:
    2972                 :             :  *      namelist: filled with a palloc'd list of pointers to identifiers within
    2973                 :             :  *                        rawstring.  Caller should list_free() this even on error return.
    2974                 :             :  *
    2975                 :             :  * Returns true if okay, false if there is a syntax error in the string.
    2976                 :             :  */
    2977                 :             : bool
    2978                 :           0 : SplitGUCList(char *rawstring, char separator,
    2979                 :             :                          List **namelist)
    2980                 :             : {
    2981                 :           0 :         char       *nextp = rawstring;
    2982                 :           0 :         bool            done = false;
    2983                 :             : 
    2984                 :           0 :         *namelist = NIL;
    2985                 :             : 
    2986         [ #  # ]:           0 :         while (scanner_isspace(*nextp))
    2987                 :           0 :                 nextp++;                                /* skip leading whitespace */
    2988                 :             : 
    2989         [ #  # ]:           0 :         if (*nextp == '\0')
    2990                 :           0 :                 return true;                    /* empty string represents empty list */
    2991                 :             : 
    2992                 :             :         /* At the top of the loop, we are at start of a new identifier. */
    2993                 :           0 :         do
    2994                 :             :         {
    2995                 :           0 :                 char       *curname;
    2996                 :           0 :                 char       *endp;
    2997                 :             : 
    2998         [ #  # ]:           0 :                 if (*nextp == '"')
    2999                 :             :                 {
    3000                 :             :                         /* Quoted name --- collapse quote-quote pairs */
    3001                 :           0 :                         curname = nextp + 1;
    3002                 :           0 :                         for (;;)
    3003                 :             :                         {
    3004                 :           0 :                                 endp = strchr(nextp + 1, '"');
    3005         [ #  # ]:           0 :                                 if (endp == NULL)
    3006                 :           0 :                                         return false;   /* mismatched quotes */
    3007         [ #  # ]:           0 :                                 if (endp[1] != '"')
    3008                 :           0 :                                         break;          /* found end of quoted name */
    3009                 :             :                                 /* Collapse adjacent quotes into one quote, and look again */
    3010                 :           0 :                                 memmove(endp, endp + 1, strlen(endp));
    3011                 :           0 :                                 nextp = endp;
    3012                 :             :                         }
    3013                 :             :                         /* endp now points at the terminating quote */
    3014                 :           0 :                         nextp = endp + 1;
    3015                 :           0 :                 }
    3016                 :             :                 else
    3017                 :             :                 {
    3018                 :             :                         /* Unquoted name --- extends to separator or whitespace */
    3019                 :           0 :                         curname = nextp;
    3020   [ #  #  #  #  :           0 :                         while (*nextp && *nextp != separator &&
                   #  # ]
    3021                 :           0 :                                    !scanner_isspace(*nextp))
    3022                 :           0 :                                 nextp++;
    3023                 :           0 :                         endp = nextp;
    3024         [ #  # ]:           0 :                         if (curname == nextp)
    3025                 :           0 :                                 return false;   /* empty unquoted name not allowed */
    3026                 :             :                 }
    3027                 :             : 
    3028         [ #  # ]:           0 :                 while (scanner_isspace(*nextp))
    3029                 :           0 :                         nextp++;                        /* skip trailing whitespace */
    3030                 :             : 
    3031         [ #  # ]:           0 :                 if (*nextp == separator)
    3032                 :             :                 {
    3033                 :           0 :                         nextp++;
    3034         [ #  # ]:           0 :                         while (scanner_isspace(*nextp))
    3035                 :           0 :                                 nextp++;                /* skip leading whitespace for next */
    3036                 :             :                         /* we expect another name, so done remains false */
    3037                 :           0 :                 }
    3038         [ #  # ]:           0 :                 else if (*nextp == '\0')
    3039                 :           0 :                         done = true;
    3040                 :             :                 else
    3041                 :           0 :                         return false;           /* invalid syntax */
    3042                 :             : 
    3043                 :             :                 /* Now safe to overwrite separator with a null */
    3044                 :           0 :                 *endp = '\0';
    3045                 :             : 
    3046                 :             :                 /*
    3047                 :             :                  * Finished isolating current name --- add it to list
    3048                 :             :                  */
    3049                 :           0 :                 *namelist = lappend(*namelist, curname);
    3050                 :             : 
    3051                 :             :                 /* Loop back if we didn't reach end of string */
    3052   [ #  #  #  # ]:           0 :         } while (!done);
    3053                 :             : 
    3054                 :           0 :         return true;
    3055                 :           0 : }
    3056                 :             : 
    3057                 :             : /*
    3058                 :             :  * appendStringInfoText
    3059                 :             :  *
    3060                 :             :  * Append a text to str.
    3061                 :             :  * Like appendStringInfoString(str, text_to_cstring(t)) but faster.
    3062                 :             :  */
    3063                 :             : static void
    3064                 :           0 : appendStringInfoText(StringInfo str, const text *t)
    3065                 :             : {
    3066                 :           0 :         appendBinaryStringInfo(str, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
    3067                 :           0 : }
    3068                 :             : 
    3069                 :             : /*
    3070                 :             :  * replace_text
    3071                 :             :  * replace all occurrences of 'old_sub_str' in 'orig_str'
    3072                 :             :  * with 'new_sub_str' to form 'new_str'
    3073                 :             :  *
    3074                 :             :  * returns 'orig_str' if 'old_sub_str' == '' or 'orig_str' == ''
    3075                 :             :  * otherwise returns 'new_str'
    3076                 :             :  */
    3077                 :             : Datum
    3078                 :           0 : replace_text(PG_FUNCTION_ARGS)
    3079                 :             : {
    3080                 :           0 :         text       *src_text = PG_GETARG_TEXT_PP(0);
    3081                 :           0 :         text       *from_sub_text = PG_GETARG_TEXT_PP(1);
    3082                 :           0 :         text       *to_sub_text = PG_GETARG_TEXT_PP(2);
    3083                 :           0 :         int                     src_text_len;
    3084                 :           0 :         int                     from_sub_text_len;
    3085                 :           0 :         TextPositionState state;
    3086                 :           0 :         text       *ret_text;
    3087                 :           0 :         int                     chunk_len;
    3088                 :           0 :         char       *curr_ptr;
    3089                 :           0 :         char       *start_ptr;
    3090                 :           0 :         StringInfoData str;
    3091                 :           0 :         bool            found;
    3092                 :             : 
    3093                 :           0 :         src_text_len = VARSIZE_ANY_EXHDR(src_text);
    3094                 :           0 :         from_sub_text_len = VARSIZE_ANY_EXHDR(from_sub_text);
    3095                 :             : 
    3096                 :             :         /* Return unmodified source string if empty source or pattern */
    3097   [ #  #  #  # ]:           0 :         if (src_text_len < 1 || from_sub_text_len < 1)
    3098                 :             :         {
    3099                 :           0 :                 PG_RETURN_TEXT_P(src_text);
    3100                 :             :         }
    3101                 :             : 
    3102                 :           0 :         text_position_setup(src_text, from_sub_text, PG_GET_COLLATION(), &state);
    3103                 :             : 
    3104                 :           0 :         found = text_position_next(&state);
    3105                 :             : 
    3106                 :             :         /* When the from_sub_text is not found, there is nothing to do. */
    3107         [ #  # ]:           0 :         if (!found)
    3108                 :             :         {
    3109                 :           0 :                 text_position_cleanup(&state);
    3110                 :           0 :                 PG_RETURN_TEXT_P(src_text);
    3111                 :             :         }
    3112                 :           0 :         curr_ptr = text_position_get_match_ptr(&state);
    3113                 :           0 :         start_ptr = VARDATA_ANY(src_text);
    3114                 :             : 
    3115                 :           0 :         initStringInfo(&str);
    3116                 :             : 
    3117                 :           0 :         do
    3118                 :             :         {
    3119         [ #  # ]:           0 :                 CHECK_FOR_INTERRUPTS();
    3120                 :             : 
    3121                 :             :                 /* copy the data skipped over by last text_position_next() */
    3122                 :           0 :                 chunk_len = curr_ptr - start_ptr;
    3123                 :           0 :                 appendBinaryStringInfo(&str, start_ptr, chunk_len);
    3124                 :             : 
    3125                 :           0 :                 appendStringInfoText(&str, to_sub_text);
    3126                 :             : 
    3127                 :           0 :                 start_ptr = curr_ptr + state.last_match_len;
    3128                 :             : 
    3129                 :           0 :                 found = text_position_next(&state);
    3130         [ #  # ]:           0 :                 if (found)
    3131                 :           0 :                         curr_ptr = text_position_get_match_ptr(&state);
    3132         [ #  # ]:           0 :         }
    3133                 :           0 :         while (found);
    3134                 :             : 
    3135                 :             :         /* copy trailing data */
    3136                 :           0 :         chunk_len = ((char *) src_text + VARSIZE_ANY(src_text)) - start_ptr;
    3137                 :           0 :         appendBinaryStringInfo(&str, start_ptr, chunk_len);
    3138                 :             : 
    3139                 :           0 :         text_position_cleanup(&state);
    3140                 :             : 
    3141                 :           0 :         ret_text = cstring_to_text_with_len(str.data, str.len);
    3142                 :           0 :         pfree(str.data);
    3143                 :             : 
    3144                 :           0 :         PG_RETURN_TEXT_P(ret_text);
    3145                 :           0 : }
    3146                 :             : 
    3147                 :             : /*
    3148                 :             :  * check_replace_text_has_escape
    3149                 :             :  *
    3150                 :             :  * Returns 0 if text contains no backslashes that need processing.
    3151                 :             :  * Returns 1 if text contains backslashes, but not regexp submatch specifiers.
    3152                 :             :  * Returns 2 if text contains regexp submatch specifiers (\1 .. \9).
    3153                 :             :  */
    3154                 :             : static int
    3155                 :           0 : check_replace_text_has_escape(const text *replace_text)
    3156                 :             : {
    3157                 :           0 :         int                     result = 0;
    3158                 :           0 :         const char *p = VARDATA_ANY(replace_text);
    3159                 :           0 :         const char *p_end = p + VARSIZE_ANY_EXHDR(replace_text);
    3160                 :             : 
    3161         [ #  # ]:           0 :         while (p < p_end)
    3162                 :             :         {
    3163                 :             :                 /* Find next escape char, if any. */
    3164                 :           0 :                 p = memchr(p, '\\', p_end - p);
    3165         [ #  # ]:           0 :                 if (p == NULL)
    3166                 :           0 :                         break;
    3167                 :           0 :                 p++;
    3168                 :             :                 /* Note: a backslash at the end doesn't require extra processing. */
    3169         [ #  # ]:           0 :                 if (p < p_end)
    3170                 :             :                 {
    3171   [ #  #  #  # ]:           0 :                         if (*p >= '1' && *p <= '9')
    3172                 :           0 :                                 return 2;               /* Found a submatch specifier, so done */
    3173                 :           0 :                         result = 1;                     /* Found some other sequence, keep looking */
    3174                 :           0 :                         p++;
    3175                 :           0 :                 }
    3176                 :             :         }
    3177                 :           0 :         return result;
    3178                 :           0 : }
    3179                 :             : 
    3180                 :             : /*
    3181                 :             :  * appendStringInfoRegexpSubstr
    3182                 :             :  *
    3183                 :             :  * Append replace_text to str, substituting regexp back references for
    3184                 :             :  * \n escapes.  start_ptr is the start of the match in the source string,
    3185                 :             :  * at logical character position data_pos.
    3186                 :             :  */
    3187                 :             : static void
    3188                 :           0 : appendStringInfoRegexpSubstr(StringInfo str, text *replace_text,
    3189                 :             :                                                          regmatch_t *pmatch,
    3190                 :             :                                                          char *start_ptr, int data_pos)
    3191                 :             : {
    3192                 :           0 :         const char *p = VARDATA_ANY(replace_text);
    3193                 :           0 :         const char *p_end = p + VARSIZE_ANY_EXHDR(replace_text);
    3194                 :             : 
    3195         [ #  # ]:           0 :         while (p < p_end)
    3196                 :             :         {
    3197                 :           0 :                 const char *chunk_start = p;
    3198                 :           0 :                 int                     so;
    3199                 :           0 :                 int                     eo;
    3200                 :             : 
    3201                 :             :                 /* Find next escape char, if any. */
    3202                 :           0 :                 p = memchr(p, '\\', p_end - p);
    3203         [ #  # ]:           0 :                 if (p == NULL)
    3204                 :           0 :                         p = p_end;
    3205                 :             : 
    3206                 :             :                 /* Copy the text we just scanned over, if any. */
    3207         [ #  # ]:           0 :                 if (p > chunk_start)
    3208                 :           0 :                         appendBinaryStringInfo(str, chunk_start, p - chunk_start);
    3209                 :             : 
    3210                 :             :                 /* Done if at end of string, else advance over escape char. */
    3211         [ #  # ]:           0 :                 if (p >= p_end)
    3212                 :           0 :                         break;
    3213                 :           0 :                 p++;
    3214                 :             : 
    3215         [ #  # ]:           0 :                 if (p >= p_end)
    3216                 :             :                 {
    3217                 :             :                         /* Escape at very end of input.  Treat same as unexpected char */
    3218                 :           0 :                         appendStringInfoChar(str, '\\');
    3219                 :           0 :                         break;
    3220                 :             :                 }
    3221                 :             : 
    3222   [ #  #  #  # ]:           0 :                 if (*p >= '1' && *p <= '9')
    3223                 :             :                 {
    3224                 :             :                         /* Use the back reference of regexp. */
    3225                 :           0 :                         int                     idx = *p - '0';
    3226                 :             : 
    3227                 :           0 :                         so = pmatch[idx].rm_so;
    3228                 :           0 :                         eo = pmatch[idx].rm_eo;
    3229                 :           0 :                         p++;
    3230                 :           0 :                 }
    3231         [ #  # ]:           0 :                 else if (*p == '&')
    3232                 :             :                 {
    3233                 :             :                         /* Use the entire matched string. */
    3234                 :           0 :                         so = pmatch[0].rm_so;
    3235                 :           0 :                         eo = pmatch[0].rm_eo;
    3236                 :           0 :                         p++;
    3237                 :           0 :                 }
    3238         [ #  # ]:           0 :                 else if (*p == '\\')
    3239                 :             :                 {
    3240                 :             :                         /* \\ means transfer one \ to output. */
    3241                 :           0 :                         appendStringInfoChar(str, '\\');
    3242                 :           0 :                         p++;
    3243                 :           0 :                         continue;
    3244                 :             :                 }
    3245                 :             :                 else
    3246                 :             :                 {
    3247                 :             :                         /*
    3248                 :             :                          * If escape char is not followed by any expected char, just treat
    3249                 :             :                          * it as ordinary data to copy.  (XXX would it be better to throw
    3250                 :             :                          * an error?)
    3251                 :             :                          */
    3252                 :           0 :                         appendStringInfoChar(str, '\\');
    3253                 :           0 :                         continue;
    3254                 :             :                 }
    3255                 :             : 
    3256   [ #  #  #  # ]:           0 :                 if (so >= 0 && eo >= 0)
    3257                 :             :                 {
    3258                 :             :                         /*
    3259                 :             :                          * Copy the text that is back reference of regexp.  Note so and eo
    3260                 :             :                          * are counted in characters not bytes.
    3261                 :             :                          */
    3262                 :           0 :                         char       *chunk_start;
    3263                 :           0 :                         int                     chunk_len;
    3264                 :             : 
    3265         [ #  # ]:           0 :                         Assert(so >= data_pos);
    3266                 :           0 :                         chunk_start = start_ptr;
    3267                 :           0 :                         chunk_start += charlen_to_bytelen(chunk_start, so - data_pos);
    3268                 :           0 :                         chunk_len = charlen_to_bytelen(chunk_start, eo - so);
    3269                 :           0 :                         appendBinaryStringInfo(str, chunk_start, chunk_len);
    3270                 :           0 :                 }
    3271      [ #  #  # ]:           0 :         }
    3272                 :           0 : }
    3273                 :             : 
    3274                 :             : /*
    3275                 :             :  * replace_text_regexp
    3276                 :             :  *
    3277                 :             :  * replace substring(s) in src_text that match pattern with replace_text.
    3278                 :             :  * The replace_text can contain backslash markers to substitute
    3279                 :             :  * (parts of) the matched text.
    3280                 :             :  *
    3281                 :             :  * cflags: regexp compile flags.
    3282                 :             :  * collation: collation to use.
    3283                 :             :  * search_start: the character (not byte) offset in src_text at which to
    3284                 :             :  * begin searching.
    3285                 :             :  * n: if 0, replace all matches; if > 0, replace only the N'th match.
    3286                 :             :  */
    3287                 :             : text *
    3288                 :           0 : replace_text_regexp(text *src_text, text *pattern_text,
    3289                 :             :                                         text *replace_text,
    3290                 :             :                                         int cflags, Oid collation,
    3291                 :             :                                         int search_start, int n)
    3292                 :             : {
    3293                 :           0 :         text       *ret_text;
    3294                 :           0 :         regex_t    *re;
    3295                 :           0 :         int                     src_text_len = VARSIZE_ANY_EXHDR(src_text);
    3296                 :           0 :         int                     nmatches = 0;
    3297                 :           0 :         StringInfoData buf;
    3298                 :           0 :         regmatch_t      pmatch[10];             /* main match, plus \1 to \9 */
    3299                 :           0 :         int                     nmatch = lengthof(pmatch);
    3300                 :           0 :         pg_wchar   *data;
    3301                 :           0 :         size_t          data_len;
    3302                 :           0 :         int                     data_pos;
    3303                 :           0 :         char       *start_ptr;
    3304                 :           0 :         int                     escape_status;
    3305                 :             : 
    3306                 :           0 :         initStringInfo(&buf);
    3307                 :             : 
    3308                 :             :         /* Convert data string to wide characters. */
    3309                 :           0 :         data = (pg_wchar *) palloc((src_text_len + 1) * sizeof(pg_wchar));
    3310                 :           0 :         data_len = pg_mb2wchar_with_len(VARDATA_ANY(src_text), data, src_text_len);
    3311                 :             : 
    3312                 :             :         /* Check whether replace_text has escapes, especially regexp submatches. */
    3313                 :           0 :         escape_status = check_replace_text_has_escape(replace_text);
    3314                 :             : 
    3315                 :             :         /* If no regexp submatches, we can use REG_NOSUB. */
    3316         [ #  # ]:           0 :         if (escape_status < 2)
    3317                 :             :         {
    3318                 :           0 :                 cflags |= REG_NOSUB;
    3319                 :             :                 /* Also tell pg_regexec we only want the whole-match location. */
    3320                 :           0 :                 nmatch = 1;
    3321                 :           0 :         }
    3322                 :             : 
    3323                 :             :         /* Prepare the regexp. */
    3324                 :           0 :         re = RE_compile_and_cache(pattern_text, cflags, collation);
    3325                 :             : 
    3326                 :             :         /* start_ptr points to the data_pos'th character of src_text */
    3327                 :           0 :         start_ptr = (char *) VARDATA_ANY(src_text);
    3328                 :           0 :         data_pos = 0;
    3329                 :             : 
    3330         [ #  # ]:           0 :         while (search_start <= data_len)
    3331                 :             :         {
    3332                 :           0 :                 int                     regexec_result;
    3333                 :             : 
    3334         [ #  # ]:           0 :                 CHECK_FOR_INTERRUPTS();
    3335                 :             : 
    3336                 :           0 :                 regexec_result = pg_regexec(re,
    3337                 :           0 :                                                                         data,
    3338                 :           0 :                                                                         data_len,
    3339                 :           0 :                                                                         search_start,
    3340                 :             :                                                                         NULL,   /* no details */
    3341                 :           0 :                                                                         nmatch,
    3342                 :           0 :                                                                         pmatch,
    3343                 :             :                                                                         0);
    3344                 :             : 
    3345         [ #  # ]:           0 :                 if (regexec_result == REG_NOMATCH)
    3346                 :           0 :                         break;
    3347                 :             : 
    3348         [ #  # ]:           0 :                 if (regexec_result != REG_OKAY)
    3349                 :             :                 {
    3350                 :           0 :                         char            errMsg[100];
    3351                 :             : 
    3352                 :           0 :                         pg_regerror(regexec_result, re, errMsg, sizeof(errMsg));
    3353   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    3354                 :             :                                         (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
    3355                 :             :                                          errmsg("regular expression failed: %s", errMsg)));
    3356                 :           0 :                 }
    3357                 :             : 
    3358                 :             :                 /*
    3359                 :             :                  * Count matches, and decide whether to replace this match.
    3360                 :             :                  */
    3361                 :           0 :                 nmatches++;
    3362   [ #  #  #  # ]:           0 :                 if (n > 0 && nmatches != n)
    3363                 :             :                 {
    3364                 :             :                         /*
    3365                 :             :                          * No, so advance search_start, but not start_ptr/data_pos. (Thus,
    3366                 :             :                          * we treat the matched text as if it weren't matched, and copy it
    3367                 :             :                          * to the output later.)
    3368                 :             :                          */
    3369                 :           0 :                         search_start = pmatch[0].rm_eo;
    3370         [ #  # ]:           0 :                         if (pmatch[0].rm_so == pmatch[0].rm_eo)
    3371                 :           0 :                                 search_start++;
    3372                 :           0 :                         continue;
    3373                 :             :                 }
    3374                 :             : 
    3375                 :             :                 /*
    3376                 :             :                  * Copy the text to the left of the match position.  Note we are given
    3377                 :             :                  * character not byte indexes.
    3378                 :             :                  */
    3379         [ #  # ]:           0 :                 if (pmatch[0].rm_so - data_pos > 0)
    3380                 :             :                 {
    3381                 :           0 :                         int                     chunk_len;
    3382                 :             : 
    3383                 :           0 :                         chunk_len = charlen_to_bytelen(start_ptr,
    3384                 :           0 :                                                                                    pmatch[0].rm_so - data_pos);
    3385                 :           0 :                         appendBinaryStringInfo(&buf, start_ptr, chunk_len);
    3386                 :             : 
    3387                 :             :                         /*
    3388                 :             :                          * Advance start_ptr over that text, to avoid multiple rescans of
    3389                 :             :                          * it if the replace_text contains multiple back-references.
    3390                 :             :                          */
    3391                 :           0 :                         start_ptr += chunk_len;
    3392                 :           0 :                         data_pos = pmatch[0].rm_so;
    3393                 :           0 :                 }
    3394                 :             : 
    3395                 :             :                 /*
    3396                 :             :                  * Copy the replace_text, processing escapes if any are present.
    3397                 :             :                  */
    3398         [ #  # ]:           0 :                 if (escape_status > 0)
    3399                 :           0 :                         appendStringInfoRegexpSubstr(&buf, replace_text, pmatch,
    3400                 :           0 :                                                                                  start_ptr, data_pos);
    3401                 :             :                 else
    3402                 :           0 :                         appendStringInfoText(&buf, replace_text);
    3403                 :             : 
    3404                 :             :                 /* Advance start_ptr and data_pos over the matched text. */
    3405                 :           0 :                 start_ptr += charlen_to_bytelen(start_ptr,
    3406                 :           0 :                                                                                 pmatch[0].rm_eo - data_pos);
    3407                 :           0 :                 data_pos = pmatch[0].rm_eo;
    3408                 :             : 
    3409                 :             :                 /*
    3410                 :             :                  * If we only want to replace one occurrence, we're done.
    3411                 :             :                  */
    3412         [ #  # ]:           0 :                 if (n > 0)
    3413                 :           0 :                         break;
    3414                 :             : 
    3415                 :             :                 /*
    3416                 :             :                  * Advance search position.  Normally we start the next search at the
    3417                 :             :                  * end of the previous match; but if the match was of zero length, we
    3418                 :             :                  * have to advance by one character, or we'd just find the same match
    3419                 :             :                  * again.
    3420                 :             :                  */
    3421                 :           0 :                 search_start = data_pos;
    3422         [ #  # ]:           0 :                 if (pmatch[0].rm_so == pmatch[0].rm_eo)
    3423                 :           0 :                         search_start++;
    3424      [ #  #  # ]:           0 :         }
    3425                 :             : 
    3426                 :             :         /*
    3427                 :             :          * Copy the text to the right of the last match.
    3428                 :             :          */
    3429         [ #  # ]:           0 :         if (data_pos < data_len)
    3430                 :             :         {
    3431                 :           0 :                 int                     chunk_len;
    3432                 :             : 
    3433                 :           0 :                 chunk_len = ((char *) src_text + VARSIZE_ANY(src_text)) - start_ptr;
    3434                 :           0 :                 appendBinaryStringInfo(&buf, start_ptr, chunk_len);
    3435                 :           0 :         }
    3436                 :             : 
    3437                 :           0 :         ret_text = cstring_to_text_with_len(buf.data, buf.len);
    3438                 :           0 :         pfree(buf.data);
    3439                 :           0 :         pfree(data);
    3440                 :             : 
    3441                 :           0 :         return ret_text;
    3442                 :           0 : }
    3443                 :             : 
    3444                 :             : /*
    3445                 :             :  * split_part
    3446                 :             :  * parse input string based on provided field separator
    3447                 :             :  * return N'th item (1 based, negative counts from end)
    3448                 :             :  */
    3449                 :             : Datum
    3450                 :           0 : split_part(PG_FUNCTION_ARGS)
    3451                 :             : {
    3452                 :           0 :         text       *inputstring = PG_GETARG_TEXT_PP(0);
    3453                 :           0 :         text       *fldsep = PG_GETARG_TEXT_PP(1);
    3454                 :           0 :         int                     fldnum = PG_GETARG_INT32(2);
    3455                 :           0 :         int                     inputstring_len;
    3456                 :           0 :         int                     fldsep_len;
    3457                 :           0 :         TextPositionState state;
    3458                 :           0 :         char       *start_ptr;
    3459                 :           0 :         char       *end_ptr;
    3460                 :           0 :         text       *result_text;
    3461                 :           0 :         bool            found;
    3462                 :             : 
    3463                 :             :         /* field number is 1 based */
    3464         [ #  # ]:           0 :         if (fldnum == 0)
    3465   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    3466                 :             :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3467                 :             :                                  errmsg("field position must not be zero")));
    3468                 :             : 
    3469                 :           0 :         inputstring_len = VARSIZE_ANY_EXHDR(inputstring);
    3470                 :           0 :         fldsep_len = VARSIZE_ANY_EXHDR(fldsep);
    3471                 :             : 
    3472                 :             :         /* return empty string for empty input string */
    3473         [ #  # ]:           0 :         if (inputstring_len < 1)
    3474                 :           0 :                 PG_RETURN_TEXT_P(cstring_to_text(""));
    3475                 :             : 
    3476                 :             :         /* handle empty field separator */
    3477         [ #  # ]:           0 :         if (fldsep_len < 1)
    3478                 :             :         {
    3479                 :             :                 /* if first or last field, return input string, else empty string */
    3480   [ #  #  #  # ]:           0 :                 if (fldnum == 1 || fldnum == -1)
    3481                 :           0 :                         PG_RETURN_TEXT_P(inputstring);
    3482                 :             :                 else
    3483                 :           0 :                         PG_RETURN_TEXT_P(cstring_to_text(""));
    3484                 :             :         }
    3485                 :             : 
    3486                 :             :         /* find the first field separator */
    3487                 :           0 :         text_position_setup(inputstring, fldsep, PG_GET_COLLATION(), &state);
    3488                 :             : 
    3489                 :           0 :         found = text_position_next(&state);
    3490                 :             : 
    3491                 :             :         /* special case if fldsep not found at all */
    3492         [ #  # ]:           0 :         if (!found)
    3493                 :             :         {
    3494                 :           0 :                 text_position_cleanup(&state);
    3495                 :             :                 /* if first or last field, return input string, else empty string */
    3496   [ #  #  #  # ]:           0 :                 if (fldnum == 1 || fldnum == -1)
    3497                 :           0 :                         PG_RETURN_TEXT_P(inputstring);
    3498                 :             :                 else
    3499                 :           0 :                         PG_RETURN_TEXT_P(cstring_to_text(""));
    3500                 :             :         }
    3501                 :             : 
    3502                 :             :         /*
    3503                 :             :          * take care of a negative field number (i.e. count from the right) by
    3504                 :             :          * converting to a positive field number; we need total number of fields
    3505                 :             :          */
    3506         [ #  # ]:           0 :         if (fldnum < 0)
    3507                 :             :         {
    3508                 :             :                 /* we found a fldsep, so there are at least two fields */
    3509                 :           0 :                 int                     numfields = 2;
    3510                 :             : 
    3511         [ #  # ]:           0 :                 while (text_position_next(&state))
    3512                 :           0 :                         numfields++;
    3513                 :             : 
    3514                 :             :                 /* special case of last field does not require an extra pass */
    3515         [ #  # ]:           0 :                 if (fldnum == -1)
    3516                 :             :                 {
    3517                 :           0 :                         start_ptr = text_position_get_match_ptr(&state) + state.last_match_len;
    3518                 :           0 :                         end_ptr = VARDATA_ANY(inputstring) + inputstring_len;
    3519                 :           0 :                         text_position_cleanup(&state);
    3520                 :           0 :                         PG_RETURN_TEXT_P(cstring_to_text_with_len(start_ptr,
    3521                 :             :                                                                                                           end_ptr - start_ptr));
    3522                 :             :                 }
    3523                 :             : 
    3524                 :             :                 /* else, convert fldnum to positive notation */
    3525                 :           0 :                 fldnum += numfields + 1;
    3526                 :             : 
    3527                 :             :                 /* if nonexistent field, return empty string */
    3528         [ #  # ]:           0 :                 if (fldnum <= 0)
    3529                 :             :                 {
    3530                 :           0 :                         text_position_cleanup(&state);
    3531                 :           0 :                         PG_RETURN_TEXT_P(cstring_to_text(""));
    3532                 :             :                 }
    3533                 :             : 
    3534                 :             :                 /* reset to pointing at first match, but now with positive fldnum */
    3535                 :           0 :                 text_position_reset(&state);
    3536                 :           0 :                 found = text_position_next(&state);
    3537         [ #  # ]:           0 :                 Assert(found);
    3538         [ #  # ]:           0 :         }
    3539                 :             : 
    3540                 :             :         /* identify bounds of first field */
    3541                 :           0 :         start_ptr = VARDATA_ANY(inputstring);
    3542                 :           0 :         end_ptr = text_position_get_match_ptr(&state);
    3543                 :             : 
    3544   [ #  #  #  # ]:           0 :         while (found && --fldnum > 0)
    3545                 :             :         {
    3546                 :             :                 /* identify bounds of next field */
    3547                 :           0 :                 start_ptr = end_ptr + state.last_match_len;
    3548                 :           0 :                 found = text_position_next(&state);
    3549         [ #  # ]:           0 :                 if (found)
    3550                 :           0 :                         end_ptr = text_position_get_match_ptr(&state);
    3551                 :             :         }
    3552                 :             : 
    3553                 :           0 :         text_position_cleanup(&state);
    3554                 :             : 
    3555         [ #  # ]:           0 :         if (fldnum > 0)
    3556                 :             :         {
    3557                 :             :                 /* N'th field separator not found */
    3558                 :             :                 /* if last field requested, return it, else empty string */
    3559         [ #  # ]:           0 :                 if (fldnum == 1)
    3560                 :             :                 {
    3561                 :           0 :                         int                     last_len = start_ptr - VARDATA_ANY(inputstring);
    3562                 :             : 
    3563                 :           0 :                         result_text = cstring_to_text_with_len(start_ptr,
    3564                 :           0 :                                                                                                    inputstring_len - last_len);
    3565                 :           0 :                 }
    3566                 :             :                 else
    3567                 :           0 :                         result_text = cstring_to_text("");
    3568                 :           0 :         }
    3569                 :             :         else
    3570                 :             :         {
    3571                 :             :                 /* non-last field requested */
    3572                 :           0 :                 result_text = cstring_to_text_with_len(start_ptr, end_ptr - start_ptr);
    3573                 :             :         }
    3574                 :             : 
    3575                 :           0 :         PG_RETURN_TEXT_P(result_text);
    3576                 :           0 : }
    3577                 :             : 
    3578                 :             : /*
    3579                 :             :  * Convenience function to return true when two text params are equal.
    3580                 :             :  */
    3581                 :             : static bool
    3582                 :           0 : text_isequal(text *txt1, text *txt2, Oid collid)
    3583                 :             : {
    3584                 :           0 :         return DatumGetBool(DirectFunctionCall2Coll(texteq,
    3585                 :           0 :                                                                                                 collid,
    3586                 :           0 :                                                                                                 PointerGetDatum(txt1),
    3587                 :           0 :                                                                                                 PointerGetDatum(txt2)));
    3588                 :             : }
    3589                 :             : 
    3590                 :             : /*
    3591                 :             :  * text_to_array
    3592                 :             :  * parse input string and return text array of elements,
    3593                 :             :  * based on provided field separator
    3594                 :             :  */
    3595                 :             : Datum
    3596                 :           0 : text_to_array(PG_FUNCTION_ARGS)
    3597                 :             : {
    3598                 :           0 :         SplitTextOutputData tstate;
    3599                 :             : 
    3600                 :             :         /* For array output, tstate should start as all zeroes */
    3601                 :           0 :         memset(&tstate, 0, sizeof(tstate));
    3602                 :             : 
    3603         [ #  # ]:           0 :         if (!split_text(fcinfo, &tstate))
    3604                 :           0 :                 PG_RETURN_NULL();
    3605                 :             : 
    3606         [ #  # ]:           0 :         if (tstate.astate == NULL)
    3607                 :           0 :                 PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID));
    3608                 :             : 
    3609                 :           0 :         PG_RETURN_DATUM(makeArrayResult(tstate.astate,
    3610                 :             :                                                                         CurrentMemoryContext));
    3611                 :           0 : }
    3612                 :             : 
    3613                 :             : /*
    3614                 :             :  * text_to_array_null
    3615                 :             :  * parse input string and return text array of elements,
    3616                 :             :  * based on provided field separator and null string
    3617                 :             :  *
    3618                 :             :  * This is a separate entry point only to prevent the regression tests from
    3619                 :             :  * complaining about different argument sets for the same internal function.
    3620                 :             :  */
    3621                 :             : Datum
    3622                 :           0 : text_to_array_null(PG_FUNCTION_ARGS)
    3623                 :             : {
    3624                 :           0 :         return text_to_array(fcinfo);
    3625                 :             : }
    3626                 :             : 
    3627                 :             : /*
    3628                 :             :  * text_to_table
    3629                 :             :  * parse input string and return table of elements,
    3630                 :             :  * based on provided field separator
    3631                 :             :  */
    3632                 :             : Datum
    3633                 :           0 : text_to_table(PG_FUNCTION_ARGS)
    3634                 :             : {
    3635                 :           0 :         ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
    3636                 :           0 :         SplitTextOutputData tstate;
    3637                 :             : 
    3638                 :           0 :         tstate.astate = NULL;
    3639                 :           0 :         InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
    3640                 :           0 :         tstate.tupstore = rsi->setResult;
    3641                 :           0 :         tstate.tupdesc = rsi->setDesc;
    3642                 :             : 
    3643                 :           0 :         (void) split_text(fcinfo, &tstate);
    3644                 :             : 
    3645                 :           0 :         return (Datum) 0;
    3646                 :           0 : }
    3647                 :             : 
    3648                 :             : /*
    3649                 :             :  * text_to_table_null
    3650                 :             :  * parse input string and return table of elements,
    3651                 :             :  * based on provided field separator and null string
    3652                 :             :  *
    3653                 :             :  * This is a separate entry point only to prevent the regression tests from
    3654                 :             :  * complaining about different argument sets for the same internal function.
    3655                 :             :  */
    3656                 :             : Datum
    3657                 :           0 : text_to_table_null(PG_FUNCTION_ARGS)
    3658                 :             : {
    3659                 :           0 :         return text_to_table(fcinfo);
    3660                 :             : }
    3661                 :             : 
    3662                 :             : /*
    3663                 :             :  * Common code for text_to_array, text_to_array_null, text_to_table
    3664                 :             :  * and text_to_table_null functions.
    3665                 :             :  *
    3666                 :             :  * These are not strict so we have to test for null inputs explicitly.
    3667                 :             :  * Returns false if result is to be null, else returns true.
    3668                 :             :  *
    3669                 :             :  * Note that if the result is valid but empty (zero elements), we return
    3670                 :             :  * without changing *tstate --- caller must handle that case, too.
    3671                 :             :  */
    3672                 :             : static bool
    3673                 :           0 : split_text(FunctionCallInfo fcinfo, SplitTextOutputData *tstate)
    3674                 :             : {
    3675                 :           0 :         text       *inputstring;
    3676                 :           0 :         text       *fldsep;
    3677                 :           0 :         text       *null_string;
    3678                 :           0 :         Oid                     collation = PG_GET_COLLATION();
    3679                 :           0 :         int                     inputstring_len;
    3680                 :           0 :         int                     fldsep_len;
    3681                 :           0 :         char       *start_ptr;
    3682                 :           0 :         text       *result_text;
    3683                 :             : 
    3684                 :             :         /* when input string is NULL, then result is NULL too */
    3685         [ #  # ]:           0 :         if (PG_ARGISNULL(0))
    3686                 :           0 :                 return false;
    3687                 :             : 
    3688                 :           0 :         inputstring = PG_GETARG_TEXT_PP(0);
    3689                 :             : 
    3690                 :             :         /* fldsep can be NULL */
    3691         [ #  # ]:           0 :         if (!PG_ARGISNULL(1))
    3692                 :           0 :                 fldsep = PG_GETARG_TEXT_PP(1);
    3693                 :             :         else
    3694                 :           0 :                 fldsep = NULL;
    3695                 :             : 
    3696                 :             :         /* null_string can be NULL or omitted */
    3697   [ #  #  #  # ]:           0 :         if (PG_NARGS() > 2 && !PG_ARGISNULL(2))
    3698                 :           0 :                 null_string = PG_GETARG_TEXT_PP(2);
    3699                 :             :         else
    3700                 :           0 :                 null_string = NULL;
    3701                 :             : 
    3702         [ #  # ]:           0 :         if (fldsep != NULL)
    3703                 :             :         {
    3704                 :             :                 /*
    3705                 :             :                  * Normal case with non-null fldsep.  Use the text_position machinery
    3706                 :             :                  * to search for occurrences of fldsep.
    3707                 :             :                  */
    3708                 :           0 :                 TextPositionState state;
    3709                 :             : 
    3710                 :           0 :                 inputstring_len = VARSIZE_ANY_EXHDR(inputstring);
    3711                 :           0 :                 fldsep_len = VARSIZE_ANY_EXHDR(fldsep);
    3712                 :             : 
    3713                 :             :                 /* return empty set for empty input string */
    3714         [ #  # ]:           0 :                 if (inputstring_len < 1)
    3715                 :           0 :                         return true;
    3716                 :             : 
    3717                 :             :                 /* empty field separator: return input string as a one-element set */
    3718         [ #  # ]:           0 :                 if (fldsep_len < 1)
    3719                 :             :                 {
    3720                 :           0 :                         split_text_accum_result(tstate, inputstring,
    3721                 :           0 :                                                                         null_string, collation);
    3722                 :           0 :                         return true;
    3723                 :             :                 }
    3724                 :             : 
    3725                 :           0 :                 text_position_setup(inputstring, fldsep, collation, &state);
    3726                 :             : 
    3727                 :           0 :                 start_ptr = VARDATA_ANY(inputstring);
    3728                 :             : 
    3729                 :           0 :                 for (;;)
    3730                 :             :                 {
    3731                 :           0 :                         bool            found;
    3732                 :           0 :                         char       *end_ptr;
    3733                 :           0 :                         int                     chunk_len;
    3734                 :             : 
    3735         [ #  # ]:           0 :                         CHECK_FOR_INTERRUPTS();
    3736                 :             : 
    3737                 :           0 :                         found = text_position_next(&state);
    3738         [ #  # ]:           0 :                         if (!found)
    3739                 :             :                         {
    3740                 :             :                                 /* fetch last field */
    3741                 :           0 :                                 chunk_len = ((char *) inputstring + VARSIZE_ANY(inputstring)) - start_ptr;
    3742                 :           0 :                                 end_ptr = NULL; /* not used, but some compilers complain */
    3743                 :           0 :                         }
    3744                 :             :                         else
    3745                 :             :                         {
    3746                 :             :                                 /* fetch non-last field */
    3747                 :           0 :                                 end_ptr = text_position_get_match_ptr(&state);
    3748                 :           0 :                                 chunk_len = end_ptr - start_ptr;
    3749                 :             :                         }
    3750                 :             : 
    3751                 :             :                         /* build a temp text datum to pass to split_text_accum_result */
    3752                 :           0 :                         result_text = cstring_to_text_with_len(start_ptr, chunk_len);
    3753                 :             : 
    3754                 :             :                         /* stash away this field */
    3755                 :           0 :                         split_text_accum_result(tstate, result_text,
    3756                 :           0 :                                                                         null_string, collation);
    3757                 :             : 
    3758                 :           0 :                         pfree(result_text);
    3759                 :             : 
    3760         [ #  # ]:           0 :                         if (!found)
    3761                 :           0 :                                 break;
    3762                 :             : 
    3763                 :           0 :                         start_ptr = end_ptr + state.last_match_len;
    3764      [ #  #  # ]:           0 :                 }
    3765                 :             : 
    3766                 :           0 :                 text_position_cleanup(&state);
    3767         [ #  # ]:           0 :         }
    3768                 :             :         else
    3769                 :             :         {
    3770                 :             :                 /*
    3771                 :             :                  * When fldsep is NULL, each character in the input string becomes a
    3772                 :             :                  * separate element in the result set.  The separator is effectively
    3773                 :             :                  * the space between characters.
    3774                 :             :                  */
    3775                 :           0 :                 inputstring_len = VARSIZE_ANY_EXHDR(inputstring);
    3776                 :             : 
    3777                 :           0 :                 start_ptr = VARDATA_ANY(inputstring);
    3778                 :             : 
    3779         [ #  # ]:           0 :                 while (inputstring_len > 0)
    3780                 :             :                 {
    3781                 :           0 :                         int                     chunk_len = pg_mblen(start_ptr);
    3782                 :             : 
    3783         [ #  # ]:           0 :                         CHECK_FOR_INTERRUPTS();
    3784                 :             : 
    3785                 :             :                         /* build a temp text datum to pass to split_text_accum_result */
    3786                 :           0 :                         result_text = cstring_to_text_with_len(start_ptr, chunk_len);
    3787                 :             : 
    3788                 :             :                         /* stash away this field */
    3789                 :           0 :                         split_text_accum_result(tstate, result_text,
    3790                 :           0 :                                                                         null_string, collation);
    3791                 :             : 
    3792                 :           0 :                         pfree(result_text);
    3793                 :             : 
    3794                 :           0 :                         start_ptr += chunk_len;
    3795                 :           0 :                         inputstring_len -= chunk_len;
    3796                 :           0 :                 }
    3797                 :             :         }
    3798                 :             : 
    3799                 :           0 :         return true;
    3800                 :           0 : }
    3801                 :             : 
    3802                 :             : /*
    3803                 :             :  * Add text item to result set (table or array).
    3804                 :             :  *
    3805                 :             :  * This is also responsible for checking to see if the item matches
    3806                 :             :  * the null_string, in which case we should emit NULL instead.
    3807                 :             :  */
    3808                 :             : static void
    3809                 :           0 : split_text_accum_result(SplitTextOutputData *tstate,
    3810                 :             :                                                 text *field_value,
    3811                 :             :                                                 text *null_string,
    3812                 :             :                                                 Oid collation)
    3813                 :             : {
    3814                 :           0 :         bool            is_null = false;
    3815                 :             : 
    3816   [ #  #  #  # ]:           0 :         if (null_string && text_isequal(field_value, null_string, collation))
    3817                 :           0 :                 is_null = true;
    3818                 :             : 
    3819         [ #  # ]:           0 :         if (tstate->tupstore)
    3820                 :             :         {
    3821                 :           0 :                 Datum           values[1];
    3822                 :           0 :                 bool            nulls[1];
    3823                 :             : 
    3824                 :           0 :                 values[0] = PointerGetDatum(field_value);
    3825                 :           0 :                 nulls[0] = is_null;
    3826                 :             : 
    3827                 :           0 :                 tuplestore_putvalues(tstate->tupstore,
    3828                 :           0 :                                                          tstate->tupdesc,
    3829                 :           0 :                                                          values,
    3830                 :           0 :                                                          nulls);
    3831                 :           0 :         }
    3832                 :             :         else
    3833                 :             :         {
    3834                 :           0 :                 tstate->astate = accumArrayResult(tstate->astate,
    3835                 :           0 :                                                                                   PointerGetDatum(field_value),
    3836                 :           0 :                                                                                   is_null,
    3837                 :             :                                                                                   TEXTOID,
    3838                 :           0 :                                                                                   CurrentMemoryContext);
    3839                 :             :         }
    3840                 :           0 : }
    3841                 :             : 
    3842                 :             : /*
    3843                 :             :  * array_to_text
    3844                 :             :  * concatenate Cstring representation of input array elements
    3845                 :             :  * using provided field separator
    3846                 :             :  */
    3847                 :             : Datum
    3848                 :           0 : array_to_text(PG_FUNCTION_ARGS)
    3849                 :             : {
    3850                 :           0 :         ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
    3851                 :           0 :         char       *fldsep = text_to_cstring(PG_GETARG_TEXT_PP(1));
    3852                 :             : 
    3853                 :           0 :         PG_RETURN_TEXT_P(array_to_text_internal(fcinfo, v, fldsep, NULL));
    3854                 :           0 : }
    3855                 :             : 
    3856                 :             : /*
    3857                 :             :  * array_to_text_null
    3858                 :             :  * concatenate Cstring representation of input array elements
    3859                 :             :  * using provided field separator and null string
    3860                 :             :  *
    3861                 :             :  * This version is not strict so we have to test for null inputs explicitly.
    3862                 :             :  */
    3863                 :             : Datum
    3864                 :           0 : array_to_text_null(PG_FUNCTION_ARGS)
    3865                 :             : {
    3866                 :           0 :         ArrayType  *v;
    3867                 :           0 :         char       *fldsep;
    3868                 :           0 :         char       *null_string;
    3869                 :             : 
    3870                 :             :         /* returns NULL when first or second parameter is NULL */
    3871   [ #  #  #  # ]:           0 :         if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
    3872                 :           0 :                 PG_RETURN_NULL();
    3873                 :             : 
    3874                 :           0 :         v = PG_GETARG_ARRAYTYPE_P(0);
    3875                 :           0 :         fldsep = text_to_cstring(PG_GETARG_TEXT_PP(1));
    3876                 :             : 
    3877                 :             :         /* NULL null string is passed through as a null pointer */
    3878         [ #  # ]:           0 :         if (!PG_ARGISNULL(2))
    3879                 :           0 :                 null_string = text_to_cstring(PG_GETARG_TEXT_PP(2));
    3880                 :             :         else
    3881                 :           0 :                 null_string = NULL;
    3882                 :             : 
    3883                 :           0 :         PG_RETURN_TEXT_P(array_to_text_internal(fcinfo, v, fldsep, null_string));
    3884                 :           0 : }
    3885                 :             : 
    3886                 :             : /*
    3887                 :             :  * common code for array_to_text and array_to_text_null functions
    3888                 :             :  */
    3889                 :             : static text *
    3890                 :           0 : array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
    3891                 :             :                                            const char *fldsep, const char *null_string)
    3892                 :             : {
    3893                 :           0 :         text       *result;
    3894                 :           0 :         int                     nitems,
    3895                 :             :                            *dims,
    3896                 :             :                                 ndims;
    3897                 :           0 :         Oid                     element_type;
    3898                 :           0 :         int                     typlen;
    3899                 :           0 :         bool            typbyval;
    3900                 :           0 :         char            typalign;
    3901                 :           0 :         StringInfoData buf;
    3902                 :           0 :         bool            printed = false;
    3903                 :           0 :         char       *p;
    3904                 :           0 :         bits8      *bitmap;
    3905                 :           0 :         int                     bitmask;
    3906                 :           0 :         int                     i;
    3907                 :           0 :         ArrayMetaState *my_extra;
    3908                 :             : 
    3909                 :           0 :         ndims = ARR_NDIM(v);
    3910                 :           0 :         dims = ARR_DIMS(v);
    3911                 :           0 :         nitems = ArrayGetNItems(ndims, dims);
    3912                 :             : 
    3913                 :             :         /* if there are no elements, return an empty string */
    3914         [ #  # ]:           0 :         if (nitems == 0)
    3915                 :           0 :                 return cstring_to_text_with_len("", 0);
    3916                 :             : 
    3917                 :           0 :         element_type = ARR_ELEMTYPE(v);
    3918                 :           0 :         initStringInfo(&buf);
    3919                 :             : 
    3920                 :             :         /*
    3921                 :             :          * We arrange to look up info about element type, including its output
    3922                 :             :          * conversion proc, only once per series of calls, assuming the element
    3923                 :             :          * type doesn't change underneath us.
    3924                 :             :          */
    3925                 :           0 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    3926         [ #  # ]:           0 :         if (my_extra == NULL)
    3927                 :             :         {
    3928                 :           0 :                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    3929                 :             :                                                                                                           sizeof(ArrayMetaState));
    3930                 :           0 :                 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    3931                 :           0 :                 my_extra->element_type = ~element_type;
    3932                 :           0 :         }
    3933                 :             : 
    3934         [ #  # ]:           0 :         if (my_extra->element_type != element_type)
    3935                 :             :         {
    3936                 :             :                 /*
    3937                 :             :                  * Get info about element type, including its output conversion proc
    3938                 :             :                  */
    3939                 :           0 :                 get_type_io_data(element_type, IOFunc_output,
    3940                 :           0 :                                                  &my_extra->typlen, &my_extra->typbyval,
    3941                 :           0 :                                                  &my_extra->typalign, &my_extra->typdelim,
    3942                 :           0 :                                                  &my_extra->typioparam, &my_extra->typiofunc);
    3943                 :           0 :                 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
    3944                 :           0 :                                           fcinfo->flinfo->fn_mcxt);
    3945                 :           0 :                 my_extra->element_type = element_type;
    3946                 :           0 :         }
    3947                 :           0 :         typlen = my_extra->typlen;
    3948                 :           0 :         typbyval = my_extra->typbyval;
    3949                 :           0 :         typalign = my_extra->typalign;
    3950                 :             : 
    3951         [ #  # ]:           0 :         p = ARR_DATA_PTR(v);
    3952         [ #  # ]:           0 :         bitmap = ARR_NULLBITMAP(v);
    3953                 :           0 :         bitmask = 1;
    3954                 :             : 
    3955         [ #  # ]:           0 :         for (i = 0; i < nitems; i++)
    3956                 :             :         {
    3957                 :           0 :                 Datum           itemvalue;
    3958                 :           0 :                 char       *value;
    3959                 :             : 
    3960                 :             :                 /* Get source element, checking for NULL */
    3961   [ #  #  #  # ]:           0 :                 if (bitmap && (*bitmap & bitmask) == 0)
    3962                 :             :                 {
    3963                 :             :                         /* if null_string is NULL, we just ignore null elements */
    3964         [ #  # ]:           0 :                         if (null_string != NULL)
    3965                 :             :                         {
    3966         [ #  # ]:           0 :                                 if (printed)
    3967                 :           0 :                                         appendStringInfo(&buf, "%s%s", fldsep, null_string);
    3968                 :             :                                 else
    3969                 :           0 :                                         appendStringInfoString(&buf, null_string);
    3970                 :           0 :                                 printed = true;
    3971                 :           0 :                         }
    3972                 :           0 :                 }
    3973                 :             :                 else
    3974                 :             :                 {
    3975                 :           0 :                         itemvalue = fetch_att(p, typbyval, typlen);
    3976                 :             : 
    3977                 :           0 :                         value = OutputFunctionCall(&my_extra->proc, itemvalue);
    3978                 :             : 
    3979         [ #  # ]:           0 :                         if (printed)
    3980                 :           0 :                                 appendStringInfo(&buf, "%s%s", fldsep, value);
    3981                 :             :                         else
    3982                 :           0 :                                 appendStringInfoString(&buf, value);
    3983                 :           0 :                         printed = true;
    3984                 :             : 
    3985   [ #  #  #  #  :           0 :                         p = att_addlength_pointer(p, typlen, p);
                   #  # ]
    3986   [ #  #  #  #  :           0 :                         p = (char *) att_align_nominal(p, typalign);
             #  #  #  # ]
    3987                 :             :                 }
    3988                 :             : 
    3989                 :             :                 /* advance bitmap pointer if any */
    3990         [ #  # ]:           0 :                 if (bitmap)
    3991                 :             :                 {
    3992                 :           0 :                         bitmask <<= 1;
    3993         [ #  # ]:           0 :                         if (bitmask == 0x100)
    3994                 :             :                         {
    3995                 :           0 :                                 bitmap++;
    3996                 :           0 :                                 bitmask = 1;
    3997                 :           0 :                         }
    3998                 :           0 :                 }
    3999                 :           0 :         }
    4000                 :             : 
    4001                 :           0 :         result = cstring_to_text_with_len(buf.data, buf.len);
    4002                 :           0 :         pfree(buf.data);
    4003                 :             : 
    4004                 :           0 :         return result;
    4005                 :           0 : }
    4006                 :             : 
    4007                 :             : /*
    4008                 :             :  * Workhorse for to_bin, to_oct, and to_hex.  Note that base must be > 1 and <=
    4009                 :             :  * 16.
    4010                 :             :  */
    4011                 :             : static inline text *
    4012                 :           0 : convert_to_base(uint64 value, int base)
    4013                 :             : {
    4014                 :           0 :         const char *digits = "0123456789abcdef";
    4015                 :             : 
    4016                 :             :         /* We size the buffer for to_bin's longest possible return value. */
    4017                 :           0 :         char            buf[sizeof(uint64) * BITS_PER_BYTE];
    4018                 :           0 :         char       *const end = buf + sizeof(buf);
    4019                 :           0 :         char       *ptr = end;
    4020                 :             : 
    4021         [ #  # ]:           0 :         Assert(base > 1);
    4022         [ #  # ]:           0 :         Assert(base <= 16);
    4023                 :             : 
    4024                 :           0 :         do
    4025                 :             :         {
    4026                 :           0 :                 *--ptr = digits[value % base];
    4027                 :           0 :                 value /= base;
    4028   [ #  #  #  # ]:           0 :         } while (ptr > buf && value);
    4029                 :             : 
    4030                 :           0 :         return cstring_to_text_with_len(ptr, end - ptr);
    4031                 :           0 : }
    4032                 :             : 
    4033                 :             : /*
    4034                 :             :  * Convert an integer to a string containing a base-2 (binary) representation
    4035                 :             :  * of the number.
    4036                 :             :  */
    4037                 :             : Datum
    4038                 :           0 : to_bin32(PG_FUNCTION_ARGS)
    4039                 :             : {
    4040                 :           0 :         uint64          value = (uint32) PG_GETARG_INT32(0);
    4041                 :             : 
    4042                 :           0 :         PG_RETURN_TEXT_P(convert_to_base(value, 2));
    4043                 :           0 : }
    4044                 :             : Datum
    4045                 :           0 : to_bin64(PG_FUNCTION_ARGS)
    4046                 :             : {
    4047                 :           0 :         uint64          value = (uint64) PG_GETARG_INT64(0);
    4048                 :             : 
    4049                 :           0 :         PG_RETURN_TEXT_P(convert_to_base(value, 2));
    4050                 :           0 : }
    4051                 :             : 
    4052                 :             : /*
    4053                 :             :  * Convert an integer to a string containing a base-8 (oct) representation of
    4054                 :             :  * the number.
    4055                 :             :  */
    4056                 :             : Datum
    4057                 :           0 : to_oct32(PG_FUNCTION_ARGS)
    4058                 :             : {
    4059                 :           0 :         uint64          value = (uint32) PG_GETARG_INT32(0);
    4060                 :             : 
    4061                 :           0 :         PG_RETURN_TEXT_P(convert_to_base(value, 8));
    4062                 :           0 : }
    4063                 :             : Datum
    4064                 :           0 : to_oct64(PG_FUNCTION_ARGS)
    4065                 :             : {
    4066                 :           0 :         uint64          value = (uint64) PG_GETARG_INT64(0);
    4067                 :             : 
    4068                 :           0 :         PG_RETURN_TEXT_P(convert_to_base(value, 8));
    4069                 :           0 : }
    4070                 :             : 
    4071                 :             : /*
    4072                 :             :  * Convert an integer to a string containing a base-16 (hex) representation of
    4073                 :             :  * the number.
    4074                 :             :  */
    4075                 :             : Datum
    4076                 :           0 : to_hex32(PG_FUNCTION_ARGS)
    4077                 :             : {
    4078                 :           0 :         uint64          value = (uint32) PG_GETARG_INT32(0);
    4079                 :             : 
    4080                 :           0 :         PG_RETURN_TEXT_P(convert_to_base(value, 16));
    4081                 :           0 : }
    4082                 :             : Datum
    4083                 :           0 : to_hex64(PG_FUNCTION_ARGS)
    4084                 :             : {
    4085                 :           0 :         uint64          value = (uint64) PG_GETARG_INT64(0);
    4086                 :             : 
    4087                 :           0 :         PG_RETURN_TEXT_P(convert_to_base(value, 16));
    4088                 :           0 : }
    4089                 :             : 
    4090                 :             : /*
    4091                 :             :  * Return the size of a datum, possibly compressed
    4092                 :             :  *
    4093                 :             :  * Works on any data type
    4094                 :             :  */
    4095                 :             : Datum
    4096                 :           0 : pg_column_size(PG_FUNCTION_ARGS)
    4097                 :             : {
    4098                 :           0 :         Datum           value = PG_GETARG_DATUM(0);
    4099                 :           0 :         int32           result;
    4100                 :           0 :         int                     typlen;
    4101                 :             : 
    4102                 :             :         /* On first call, get the input type's typlen, and save at *fn_extra */
    4103         [ #  # ]:           0 :         if (fcinfo->flinfo->fn_extra == NULL)
    4104                 :             :         {
    4105                 :             :                 /* Lookup the datatype of the supplied argument */
    4106                 :           0 :                 Oid                     argtypeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
    4107                 :             : 
    4108                 :           0 :                 typlen = get_typlen(argtypeid);
    4109         [ #  # ]:           0 :                 if (typlen == 0)                /* should not happen */
    4110   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for type %u", argtypeid);
    4111                 :             : 
    4112                 :           0 :                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    4113                 :             :                                                                                                           sizeof(int));
    4114                 :           0 :                 *((int *) fcinfo->flinfo->fn_extra) = typlen;
    4115                 :           0 :         }
    4116                 :             :         else
    4117                 :           0 :                 typlen = *((int *) fcinfo->flinfo->fn_extra);
    4118                 :             : 
    4119         [ #  # ]:           0 :         if (typlen == -1)
    4120                 :             :         {
    4121                 :             :                 /* varlena type, possibly toasted */
    4122                 :           0 :                 result = toast_datum_size(value);
    4123                 :           0 :         }
    4124         [ #  # ]:           0 :         else if (typlen == -2)
    4125                 :             :         {
    4126                 :             :                 /* cstring */
    4127                 :           0 :                 result = strlen(DatumGetCString(value)) + 1;
    4128                 :           0 :         }
    4129                 :             :         else
    4130                 :             :         {
    4131                 :             :                 /* ordinary fixed-width type */
    4132                 :           0 :                 result = typlen;
    4133                 :             :         }
    4134                 :             : 
    4135                 :           0 :         PG_RETURN_INT32(result);
    4136                 :           0 : }
    4137                 :             : 
    4138                 :             : /*
    4139                 :             :  * Return the compression method stored in the compressed attribute.  Return
    4140                 :             :  * NULL for non varlena type or uncompressed data.
    4141                 :             :  */
    4142                 :             : Datum
    4143                 :           0 : pg_column_compression(PG_FUNCTION_ARGS)
    4144                 :             : {
    4145                 :           0 :         int                     typlen;
    4146                 :           0 :         char       *result;
    4147                 :           0 :         ToastCompressionId cmid;
    4148                 :             : 
    4149                 :             :         /* On first call, get the input type's typlen, and save at *fn_extra */
    4150         [ #  # ]:           0 :         if (fcinfo->flinfo->fn_extra == NULL)
    4151                 :             :         {
    4152                 :             :                 /* Lookup the datatype of the supplied argument */
    4153                 :           0 :                 Oid                     argtypeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
    4154                 :             : 
    4155                 :           0 :                 typlen = get_typlen(argtypeid);
    4156         [ #  # ]:           0 :                 if (typlen == 0)                /* should not happen */
    4157   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for type %u", argtypeid);
    4158                 :             : 
    4159                 :           0 :                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    4160                 :             :                                                                                                           sizeof(int));
    4161                 :           0 :                 *((int *) fcinfo->flinfo->fn_extra) = typlen;
    4162                 :           0 :         }
    4163                 :             :         else
    4164                 :           0 :                 typlen = *((int *) fcinfo->flinfo->fn_extra);
    4165                 :             : 
    4166         [ #  # ]:           0 :         if (typlen != -1)
    4167                 :           0 :                 PG_RETURN_NULL();
    4168                 :             : 
    4169                 :             :         /* get the compression method id stored in the compressed varlena */
    4170                 :           0 :         cmid = toast_get_compression_id((struct varlena *)
    4171                 :           0 :                                                                         DatumGetPointer(PG_GETARG_DATUM(0)));
    4172         [ #  # ]:           0 :         if (cmid == TOAST_INVALID_COMPRESSION_ID)
    4173                 :           0 :                 PG_RETURN_NULL();
    4174                 :             : 
    4175                 :             :         /* convert compression method id to compression method name */
    4176      [ #  #  # ]:           0 :         switch (cmid)
    4177                 :             :         {
    4178                 :             :                 case TOAST_PGLZ_COMPRESSION_ID:
    4179                 :           0 :                         result = "pglz";
    4180                 :           0 :                         break;
    4181                 :             :                 case TOAST_LZ4_COMPRESSION_ID:
    4182                 :           0 :                         result = "lz4";
    4183                 :           0 :                         break;
    4184                 :             :                 default:
    4185   [ #  #  #  # ]:           0 :                         elog(ERROR, "invalid compression method id %d", cmid);
    4186                 :           0 :         }
    4187                 :             : 
    4188                 :           0 :         PG_RETURN_TEXT_P(cstring_to_text(result));
    4189                 :           0 : }
    4190                 :             : 
    4191                 :             : /*
    4192                 :             :  * Return the chunk_id of the on-disk TOASTed value.  Return NULL if the value
    4193                 :             :  * is un-TOASTed or not on-disk.
    4194                 :             :  */
    4195                 :             : Datum
    4196                 :           0 : pg_column_toast_chunk_id(PG_FUNCTION_ARGS)
    4197                 :             : {
    4198                 :           0 :         int                     typlen;
    4199                 :           0 :         struct varlena *attr;
    4200                 :           0 :         struct varatt_external toast_pointer;
    4201                 :             : 
    4202                 :             :         /* On first call, get the input type's typlen, and save at *fn_extra */
    4203         [ #  # ]:           0 :         if (fcinfo->flinfo->fn_extra == NULL)
    4204                 :             :         {
    4205                 :             :                 /* Lookup the datatype of the supplied argument */
    4206                 :           0 :                 Oid                     argtypeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
    4207                 :             : 
    4208                 :           0 :                 typlen = get_typlen(argtypeid);
    4209         [ #  # ]:           0 :                 if (typlen == 0)                /* should not happen */
    4210   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for type %u", argtypeid);
    4211                 :             : 
    4212                 :           0 :                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    4213                 :             :                                                                                                           sizeof(int));
    4214                 :           0 :                 *((int *) fcinfo->flinfo->fn_extra) = typlen;
    4215                 :           0 :         }
    4216                 :             :         else
    4217                 :           0 :                 typlen = *((int *) fcinfo->flinfo->fn_extra);
    4218                 :             : 
    4219         [ #  # ]:           0 :         if (typlen != -1)
    4220                 :           0 :                 PG_RETURN_NULL();
    4221                 :             : 
    4222                 :           0 :         attr = (struct varlena *) DatumGetPointer(PG_GETARG_DATUM(0));
    4223                 :             : 
    4224         [ #  # ]:           0 :         if (!VARATT_IS_EXTERNAL_ONDISK(attr))
    4225                 :           0 :                 PG_RETURN_NULL();
    4226                 :             : 
    4227   [ #  #  #  # ]:           0 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
    4228                 :             : 
    4229                 :           0 :         PG_RETURN_OID(toast_pointer.va_valueid);
    4230                 :           0 : }
    4231                 :             : 
    4232                 :             : /*
    4233                 :             :  * string_agg - Concatenates values and returns string.
    4234                 :             :  *
    4235                 :             :  * Syntax: string_agg(value text, delimiter text) RETURNS text
    4236                 :             :  *
    4237                 :             :  * Note: Any NULL values are ignored. The first-call delimiter isn't
    4238                 :             :  * actually used at all, and on subsequent calls the delimiter precedes
    4239                 :             :  * the associated value.
    4240                 :             :  */
    4241                 :             : 
    4242                 :             : /* subroutine to initialize state */
    4243                 :             : static StringInfo
    4244                 :           0 : makeStringAggState(FunctionCallInfo fcinfo)
    4245                 :             : {
    4246                 :           0 :         StringInfo      state;
    4247                 :           0 :         MemoryContext aggcontext;
    4248                 :           0 :         MemoryContext oldcontext;
    4249                 :             : 
    4250         [ #  # ]:           0 :         if (!AggCheckCallContext(fcinfo, &aggcontext))
    4251                 :             :         {
    4252                 :             :                 /* cannot be called directly because of internal-type argument */
    4253   [ #  #  #  # ]:           0 :                 elog(ERROR, "string_agg_transfn called in non-aggregate context");
    4254                 :           0 :         }
    4255                 :             : 
    4256                 :             :         /*
    4257                 :             :          * Create state in aggregate context.  It'll stay there across subsequent
    4258                 :             :          * calls.
    4259                 :             :          */
    4260                 :           0 :         oldcontext = MemoryContextSwitchTo(aggcontext);
    4261                 :           0 :         state = makeStringInfo();
    4262                 :           0 :         MemoryContextSwitchTo(oldcontext);
    4263                 :             : 
    4264                 :           0 :         return state;
    4265                 :           0 : }
    4266                 :             : 
    4267                 :             : Datum
    4268                 :           0 : string_agg_transfn(PG_FUNCTION_ARGS)
    4269                 :             : {
    4270                 :           0 :         StringInfo      state;
    4271                 :             : 
    4272         [ #  # ]:           0 :         state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
    4273                 :             : 
    4274                 :             :         /* Append the value unless null, preceding it with the delimiter. */
    4275         [ #  # ]:           0 :         if (!PG_ARGISNULL(1))
    4276                 :             :         {
    4277                 :           0 :                 text       *value = PG_GETARG_TEXT_PP(1);
    4278                 :           0 :                 bool            isfirst = false;
    4279                 :             : 
    4280                 :             :                 /*
    4281                 :             :                  * You might think we can just throw away the first delimiter, however
    4282                 :             :                  * we must keep it as we may be a parallel worker doing partial
    4283                 :             :                  * aggregation building a state to send to the main process.  We need
    4284                 :             :                  * to keep the delimiter of every aggregation so that the combine
    4285                 :             :                  * function can properly join up the strings of two separately
    4286                 :             :                  * partially aggregated results.  The first delimiter is only stripped
    4287                 :             :                  * off in the final function.  To know how much to strip off the front
    4288                 :             :                  * of the string, we store the length of the first delimiter in the
    4289                 :             :                  * StringInfo's cursor field, which we don't otherwise need here.
    4290                 :             :                  */
    4291         [ #  # ]:           0 :                 if (state == NULL)
    4292                 :             :                 {
    4293                 :           0 :                         state = makeStringAggState(fcinfo);
    4294                 :           0 :                         isfirst = true;
    4295                 :           0 :                 }
    4296                 :             : 
    4297         [ #  # ]:           0 :                 if (!PG_ARGISNULL(2))
    4298                 :             :                 {
    4299                 :           0 :                         text       *delim = PG_GETARG_TEXT_PP(2);
    4300                 :             : 
    4301                 :           0 :                         appendStringInfoText(state, delim);
    4302         [ #  # ]:           0 :                         if (isfirst)
    4303                 :           0 :                                 state->cursor = VARSIZE_ANY_EXHDR(delim);
    4304                 :           0 :                 }
    4305                 :             : 
    4306                 :           0 :                 appendStringInfoText(state, value);
    4307                 :           0 :         }
    4308                 :             : 
    4309                 :             :         /*
    4310                 :             :          * The transition type for string_agg() is declared to be "internal",
    4311                 :             :          * which is a pass-by-value type the same size as a pointer.
    4312                 :             :          */
    4313         [ #  # ]:           0 :         if (state)
    4314                 :           0 :                 PG_RETURN_POINTER(state);
    4315                 :           0 :         PG_RETURN_NULL();
    4316         [ #  # ]:           0 : }
    4317                 :             : 
    4318                 :             : /*
    4319                 :             :  * string_agg_combine
    4320                 :             :  *              Aggregate combine function for string_agg(text) and string_agg(bytea)
    4321                 :             :  */
    4322                 :             : Datum
    4323                 :           0 : string_agg_combine(PG_FUNCTION_ARGS)
    4324                 :             : {
    4325                 :           0 :         StringInfo      state1;
    4326                 :           0 :         StringInfo      state2;
    4327                 :           0 :         MemoryContext agg_context;
    4328                 :             : 
    4329         [ #  # ]:           0 :         if (!AggCheckCallContext(fcinfo, &agg_context))
    4330   [ #  #  #  # ]:           0 :                 elog(ERROR, "aggregate function called in non-aggregate context");
    4331                 :             : 
    4332         [ #  # ]:           0 :         state1 = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
    4333         [ #  # ]:           0 :         state2 = PG_ARGISNULL(1) ? NULL : (StringInfo) PG_GETARG_POINTER(1);
    4334                 :             : 
    4335         [ #  # ]:           0 :         if (state2 == NULL)
    4336                 :             :         {
    4337                 :             :                 /*
    4338                 :             :                  * NULL state2 is easy, just return state1, which we know is already
    4339                 :             :                  * in the agg_context
    4340                 :             :                  */
    4341         [ #  # ]:           0 :                 if (state1 == NULL)
    4342                 :           0 :                         PG_RETURN_NULL();
    4343                 :           0 :                 PG_RETURN_POINTER(state1);
    4344                 :             :         }
    4345                 :             : 
    4346         [ #  # ]:           0 :         if (state1 == NULL)
    4347                 :             :         {
    4348                 :             :                 /* We must copy state2's data into the agg_context */
    4349                 :           0 :                 MemoryContext old_context;
    4350                 :             : 
    4351                 :           0 :                 old_context = MemoryContextSwitchTo(agg_context);
    4352                 :           0 :                 state1 = makeStringAggState(fcinfo);
    4353                 :           0 :                 appendBinaryStringInfo(state1, state2->data, state2->len);
    4354                 :           0 :                 state1->cursor = state2->cursor;
    4355                 :           0 :                 MemoryContextSwitchTo(old_context);
    4356                 :           0 :         }
    4357         [ #  # ]:           0 :         else if (state2->len > 0)
    4358                 :             :         {
    4359                 :             :                 /* Combine ... state1->cursor does not change in this case */
    4360                 :           0 :                 appendBinaryStringInfo(state1, state2->data, state2->len);
    4361                 :           0 :         }
    4362                 :             : 
    4363                 :           0 :         PG_RETURN_POINTER(state1);
    4364                 :           0 : }
    4365                 :             : 
    4366                 :             : /*
    4367                 :             :  * string_agg_serialize
    4368                 :             :  *              Aggregate serialize function for string_agg(text) and string_agg(bytea)
    4369                 :             :  *
    4370                 :             :  * This is strict, so we need not handle NULL input
    4371                 :             :  */
    4372                 :             : Datum
    4373                 :           0 : string_agg_serialize(PG_FUNCTION_ARGS)
    4374                 :             : {
    4375                 :           0 :         StringInfo      state;
    4376                 :           0 :         StringInfoData buf;
    4377                 :           0 :         bytea      *result;
    4378                 :             : 
    4379                 :             :         /* cannot be called directly because of internal-type argument */
    4380         [ #  # ]:           0 :         Assert(AggCheckCallContext(fcinfo, NULL));
    4381                 :             : 
    4382                 :           0 :         state = (StringInfo) PG_GETARG_POINTER(0);
    4383                 :             : 
    4384                 :           0 :         pq_begintypsend(&buf);
    4385                 :             : 
    4386                 :             :         /* cursor */
    4387                 :           0 :         pq_sendint(&buf, state->cursor, 4);
    4388                 :             : 
    4389                 :             :         /* data */
    4390                 :           0 :         pq_sendbytes(&buf, state->data, state->len);
    4391                 :             : 
    4392                 :           0 :         result = pq_endtypsend(&buf);
    4393                 :             : 
    4394                 :           0 :         PG_RETURN_BYTEA_P(result);
    4395                 :           0 : }
    4396                 :             : 
    4397                 :             : /*
    4398                 :             :  * string_agg_deserialize
    4399                 :             :  *              Aggregate deserial function for string_agg(text) and string_agg(bytea)
    4400                 :             :  *
    4401                 :             :  * This is strict, so we need not handle NULL input
    4402                 :             :  */
    4403                 :             : Datum
    4404                 :           0 : string_agg_deserialize(PG_FUNCTION_ARGS)
    4405                 :             : {
    4406                 :           0 :         bytea      *sstate;
    4407                 :           0 :         StringInfo      result;
    4408                 :           0 :         StringInfoData buf;
    4409                 :           0 :         char       *data;
    4410                 :           0 :         int                     datalen;
    4411                 :             : 
    4412                 :             :         /* cannot be called directly because of internal-type argument */
    4413         [ #  # ]:           0 :         Assert(AggCheckCallContext(fcinfo, NULL));
    4414                 :             : 
    4415                 :           0 :         sstate = PG_GETARG_BYTEA_PP(0);
    4416                 :             : 
    4417                 :             :         /*
    4418                 :             :          * Initialize a StringInfo so that we can "receive" it using the standard
    4419                 :             :          * recv-function infrastructure.
    4420                 :             :          */
    4421                 :           0 :         initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
    4422                 :           0 :                                                    VARSIZE_ANY_EXHDR(sstate));
    4423                 :             : 
    4424                 :           0 :         result = makeStringAggState(fcinfo);
    4425                 :             : 
    4426                 :             :         /* cursor */
    4427                 :           0 :         result->cursor = pq_getmsgint(&buf, 4);
    4428                 :             : 
    4429                 :             :         /* data */
    4430                 :           0 :         datalen = VARSIZE_ANY_EXHDR(sstate) - 4;
    4431                 :           0 :         data = (char *) pq_getmsgbytes(&buf, datalen);
    4432                 :           0 :         appendBinaryStringInfo(result, data, datalen);
    4433                 :             : 
    4434                 :           0 :         pq_getmsgend(&buf);
    4435                 :             : 
    4436                 :           0 :         PG_RETURN_POINTER(result);
    4437                 :           0 : }
    4438                 :             : 
    4439                 :             : Datum
    4440                 :           0 : string_agg_finalfn(PG_FUNCTION_ARGS)
    4441                 :             : {
    4442                 :           0 :         StringInfo      state;
    4443                 :             : 
    4444                 :             :         /* cannot be called directly because of internal-type argument */
    4445         [ #  # ]:           0 :         Assert(AggCheckCallContext(fcinfo, NULL));
    4446                 :             : 
    4447         [ #  # ]:           0 :         state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
    4448                 :             : 
    4449         [ #  # ]:           0 :         if (state != NULL)
    4450                 :             :         {
    4451                 :             :                 /* As per comment in transfn, strip data before the cursor position */
    4452                 :           0 :                 PG_RETURN_TEXT_P(cstring_to_text_with_len(&state->data[state->cursor],
    4453                 :             :                                                                                                   state->len - state->cursor));
    4454                 :             :         }
    4455                 :             :         else
    4456                 :           0 :                 PG_RETURN_NULL();
    4457         [ #  # ]:           0 : }
    4458                 :             : 
    4459                 :             : /*
    4460                 :             :  * Prepare cache with fmgr info for the output functions of the datatypes of
    4461                 :             :  * the arguments of a concat-like function, beginning with argument "argidx".
    4462                 :             :  * (Arguments before that will have corresponding slots in the resulting
    4463                 :             :  * FmgrInfo array, but we don't fill those slots.)
    4464                 :             :  */
    4465                 :             : static FmgrInfo *
    4466                 :           0 : build_concat_foutcache(FunctionCallInfo fcinfo, int argidx)
    4467                 :             : {
    4468                 :           0 :         FmgrInfo   *foutcache;
    4469                 :           0 :         int                     i;
    4470                 :             : 
    4471                 :             :         /* We keep the info in fn_mcxt so it survives across calls */
    4472                 :           0 :         foutcache = (FmgrInfo *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    4473                 :           0 :                                                                                                 PG_NARGS() * sizeof(FmgrInfo));
    4474                 :             : 
    4475         [ #  # ]:           0 :         for (i = argidx; i < PG_NARGS(); i++)
    4476                 :             :         {
    4477                 :           0 :                 Oid                     valtype;
    4478                 :           0 :                 Oid                     typOutput;
    4479                 :           0 :                 bool            typIsVarlena;
    4480                 :             : 
    4481                 :           0 :                 valtype = get_fn_expr_argtype(fcinfo->flinfo, i);
    4482         [ #  # ]:           0 :                 if (!OidIsValid(valtype))
    4483   [ #  #  #  # ]:           0 :                         elog(ERROR, "could not determine data type of concat() input");
    4484                 :             : 
    4485                 :           0 :                 getTypeOutputInfo(valtype, &typOutput, &typIsVarlena);
    4486                 :           0 :                 fmgr_info_cxt(typOutput, &foutcache[i], fcinfo->flinfo->fn_mcxt);
    4487                 :           0 :         }
    4488                 :             : 
    4489                 :           0 :         fcinfo->flinfo->fn_extra = foutcache;
    4490                 :             : 
    4491                 :           0 :         return foutcache;
    4492                 :           0 : }
    4493                 :             : 
    4494                 :             : /*
    4495                 :             :  * Implementation of both concat() and concat_ws().
    4496                 :             :  *
    4497                 :             :  * sepstr is the separator string to place between values.
    4498                 :             :  * argidx identifies the first argument to concatenate (counting from zero);
    4499                 :             :  * note that this must be constant across any one series of calls.
    4500                 :             :  *
    4501                 :             :  * Returns NULL if result should be NULL, else text value.
    4502                 :             :  */
    4503                 :             : static text *
    4504                 :           0 : concat_internal(const char *sepstr, int argidx,
    4505                 :             :                                 FunctionCallInfo fcinfo)
    4506                 :             : {
    4507                 :           0 :         text       *result;
    4508                 :           0 :         StringInfoData str;
    4509                 :           0 :         FmgrInfo   *foutcache;
    4510                 :           0 :         bool            first_arg = true;
    4511                 :           0 :         int                     i;
    4512                 :             : 
    4513                 :             :         /*
    4514                 :             :          * concat(VARIADIC some-array) is essentially equivalent to
    4515                 :             :          * array_to_text(), ie concat the array elements with the given separator.
    4516                 :             :          * So we just pass the case off to that code.
    4517                 :             :          */
    4518         [ #  # ]:           0 :         if (get_fn_expr_variadic(fcinfo->flinfo))
    4519                 :             :         {
    4520                 :           0 :                 ArrayType  *arr;
    4521                 :             : 
    4522                 :             :                 /* Should have just the one argument */
    4523         [ #  # ]:           0 :                 Assert(argidx == PG_NARGS() - 1);
    4524                 :             : 
    4525                 :             :                 /* concat(VARIADIC NULL) is defined as NULL */
    4526         [ #  # ]:           0 :                 if (PG_ARGISNULL(argidx))
    4527                 :           0 :                         return NULL;
    4528                 :             : 
    4529                 :             :                 /*
    4530                 :             :                  * Non-null argument had better be an array.  We assume that any call
    4531                 :             :                  * context that could let get_fn_expr_variadic return true will have
    4532                 :             :                  * checked that a VARIADIC-labeled parameter actually is an array.  So
    4533                 :             :                  * it should be okay to just Assert that it's an array rather than
    4534                 :             :                  * doing a full-fledged error check.
    4535                 :             :                  */
    4536         [ #  # ]:           0 :                 Assert(OidIsValid(get_base_element_type(get_fn_expr_argtype(fcinfo->flinfo, argidx))));
    4537                 :             : 
    4538                 :             :                 /* OK, safe to fetch the array value */
    4539                 :           0 :                 arr = PG_GETARG_ARRAYTYPE_P(argidx);
    4540                 :             : 
    4541                 :             :                 /*
    4542                 :             :                  * And serialize the array.  We tell array_to_text to ignore null
    4543                 :             :                  * elements, which matches the behavior of the loop below.
    4544                 :             :                  */
    4545                 :           0 :                 return array_to_text_internal(fcinfo, arr, sepstr, NULL);
    4546                 :           0 :         }
    4547                 :             : 
    4548                 :             :         /* Normal case without explicit VARIADIC marker */
    4549                 :           0 :         initStringInfo(&str);
    4550                 :             : 
    4551                 :             :         /* Get output function info, building it if first time through */
    4552                 :           0 :         foutcache = (FmgrInfo *) fcinfo->flinfo->fn_extra;
    4553         [ #  # ]:           0 :         if (foutcache == NULL)
    4554                 :           0 :                 foutcache = build_concat_foutcache(fcinfo, argidx);
    4555                 :             : 
    4556         [ #  # ]:           0 :         for (i = argidx; i < PG_NARGS(); i++)
    4557                 :             :         {
    4558         [ #  # ]:           0 :                 if (!PG_ARGISNULL(i))
    4559                 :             :                 {
    4560                 :           0 :                         Datum           value = PG_GETARG_DATUM(i);
    4561                 :             : 
    4562                 :             :                         /* add separator if appropriate */
    4563         [ #  # ]:           0 :                         if (first_arg)
    4564                 :           0 :                                 first_arg = false;
    4565                 :             :                         else
    4566                 :           0 :                                 appendStringInfoString(&str, sepstr);
    4567                 :             : 
    4568                 :             :                         /* call the appropriate type output function, append the result */
    4569                 :           0 :                         appendStringInfoString(&str,
    4570                 :           0 :                                                                    OutputFunctionCall(&foutcache[i], value));
    4571                 :           0 :                 }
    4572                 :           0 :         }
    4573                 :             : 
    4574                 :           0 :         result = cstring_to_text_with_len(str.data, str.len);
    4575                 :           0 :         pfree(str.data);
    4576                 :             : 
    4577                 :           0 :         return result;
    4578                 :           0 : }
    4579                 :             : 
    4580                 :             : /*
    4581                 :             :  * Concatenate all arguments. NULL arguments are ignored.
    4582                 :             :  */
    4583                 :             : Datum
    4584                 :           0 : text_concat(PG_FUNCTION_ARGS)
    4585                 :             : {
    4586                 :           0 :         text       *result;
    4587                 :             : 
    4588                 :           0 :         result = concat_internal("", 0, fcinfo);
    4589         [ #  # ]:           0 :         if (result == NULL)
    4590                 :           0 :                 PG_RETURN_NULL();
    4591                 :           0 :         PG_RETURN_TEXT_P(result);
    4592                 :           0 : }
    4593                 :             : 
    4594                 :             : /*
    4595                 :             :  * Concatenate all but first argument value with separators. The first
    4596                 :             :  * parameter is used as the separator. NULL arguments are ignored.
    4597                 :             :  */
    4598                 :             : Datum
    4599                 :           0 : text_concat_ws(PG_FUNCTION_ARGS)
    4600                 :             : {
    4601                 :           0 :         char       *sep;
    4602                 :           0 :         text       *result;
    4603                 :             : 
    4604                 :             :         /* return NULL when separator is NULL */
    4605         [ #  # ]:           0 :         if (PG_ARGISNULL(0))
    4606                 :           0 :                 PG_RETURN_NULL();
    4607                 :           0 :         sep = text_to_cstring(PG_GETARG_TEXT_PP(0));
    4608                 :             : 
    4609                 :           0 :         result = concat_internal(sep, 1, fcinfo);
    4610         [ #  # ]:           0 :         if (result == NULL)
    4611                 :           0 :                 PG_RETURN_NULL();
    4612                 :           0 :         PG_RETURN_TEXT_P(result);
    4613                 :           0 : }
    4614                 :             : 
    4615                 :             : /*
    4616                 :             :  * Return first n characters in the string. When n is negative,
    4617                 :             :  * return all but last |n| characters.
    4618                 :             :  */
    4619                 :             : Datum
    4620                 :           0 : text_left(PG_FUNCTION_ARGS)
    4621                 :             : {
    4622                 :           0 :         int                     n = PG_GETARG_INT32(1);
    4623                 :             : 
    4624         [ #  # ]:           0 :         if (n < 0)
    4625                 :             :         {
    4626                 :           0 :                 text       *str = PG_GETARG_TEXT_PP(0);
    4627                 :           0 :                 const char *p = VARDATA_ANY(str);
    4628                 :           0 :                 int                     len = VARSIZE_ANY_EXHDR(str);
    4629                 :           0 :                 int                     rlen;
    4630                 :             : 
    4631                 :           0 :                 n = pg_mbstrlen_with_len(p, len) + n;
    4632                 :           0 :                 rlen = pg_mbcharcliplen(p, len, n);
    4633                 :           0 :                 PG_RETURN_TEXT_P(cstring_to_text_with_len(p, rlen));
    4634                 :           0 :         }
    4635                 :             :         else
    4636                 :           0 :                 PG_RETURN_TEXT_P(text_substring(PG_GETARG_DATUM(0), 1, n, false));
    4637                 :           0 : }
    4638                 :             : 
    4639                 :             : /*
    4640                 :             :  * Return last n characters in the string. When n is negative,
    4641                 :             :  * return all but first |n| characters.
    4642                 :             :  */
    4643                 :             : Datum
    4644                 :           0 : text_right(PG_FUNCTION_ARGS)
    4645                 :             : {
    4646                 :           0 :         text       *str = PG_GETARG_TEXT_PP(0);
    4647                 :           0 :         const char *p = VARDATA_ANY(str);
    4648                 :           0 :         int                     len = VARSIZE_ANY_EXHDR(str);
    4649                 :           0 :         int                     n = PG_GETARG_INT32(1);
    4650                 :           0 :         int                     off;
    4651                 :             : 
    4652         [ #  # ]:           0 :         if (n < 0)
    4653                 :           0 :                 n = -n;
    4654                 :             :         else
    4655                 :           0 :                 n = pg_mbstrlen_with_len(p, len) - n;
    4656                 :           0 :         off = pg_mbcharcliplen(p, len, n);
    4657                 :             : 
    4658                 :           0 :         PG_RETURN_TEXT_P(cstring_to_text_with_len(p + off, len - off));
    4659                 :           0 : }
    4660                 :             : 
    4661                 :             : /*
    4662                 :             :  * Return reversed string
    4663                 :             :  */
    4664                 :             : Datum
    4665                 :           0 : text_reverse(PG_FUNCTION_ARGS)
    4666                 :             : {
    4667                 :           0 :         text       *str = PG_GETARG_TEXT_PP(0);
    4668                 :           0 :         const char *p = VARDATA_ANY(str);
    4669                 :           0 :         int                     len = VARSIZE_ANY_EXHDR(str);
    4670                 :           0 :         const char *endp = p + len;
    4671                 :           0 :         text       *result;
    4672                 :           0 :         char       *dst;
    4673                 :             : 
    4674                 :           0 :         result = palloc(len + VARHDRSZ);
    4675                 :           0 :         dst = (char *) VARDATA(result) + len;
    4676                 :           0 :         SET_VARSIZE(result, len + VARHDRSZ);
    4677                 :             : 
    4678         [ #  # ]:           0 :         if (pg_database_encoding_max_length() > 1)
    4679                 :             :         {
    4680                 :             :                 /* multibyte version */
    4681         [ #  # ]:           0 :                 while (p < endp)
    4682                 :             :                 {
    4683                 :           0 :                         int                     sz;
    4684                 :             : 
    4685                 :           0 :                         sz = pg_mblen(p);
    4686                 :           0 :                         dst -= sz;
    4687                 :           0 :                         memcpy(dst, p, sz);
    4688                 :           0 :                         p += sz;
    4689                 :           0 :                 }
    4690                 :           0 :         }
    4691                 :             :         else
    4692                 :             :         {
    4693                 :             :                 /* single byte version */
    4694         [ #  # ]:           0 :                 while (p < endp)
    4695                 :           0 :                         *(--dst) = *p++;
    4696                 :             :         }
    4697                 :             : 
    4698                 :           0 :         PG_RETURN_TEXT_P(result);
    4699                 :           0 : }
    4700                 :             : 
    4701                 :             : 
    4702                 :             : /*
    4703                 :             :  * Support macros for text_format()
    4704                 :             :  */
    4705                 :             : #define TEXT_FORMAT_FLAG_MINUS  0x0001  /* is minus flag present? */
    4706                 :             : 
    4707                 :             : #define ADVANCE_PARSE_POINTER(ptr,end_ptr) \
    4708                 :             :         do { \
    4709                 :             :                 if (++(ptr) >= (end_ptr)) \
    4710                 :             :                         ereport(ERROR, \
    4711                 :             :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE), \
    4712                 :             :                                          errmsg("unterminated format() type specifier"), \
    4713                 :             :                                          errhint("For a single \"%%\" use \"%%%%\"."))); \
    4714                 :             :         } while (0)
    4715                 :             : 
    4716                 :             : /*
    4717                 :             :  * Returns a formatted string
    4718                 :             :  */
    4719                 :             : Datum
    4720                 :           0 : text_format(PG_FUNCTION_ARGS)
    4721                 :             : {
    4722                 :           0 :         text       *fmt;
    4723                 :           0 :         StringInfoData str;
    4724                 :           0 :         const char *cp;
    4725                 :           0 :         const char *start_ptr;
    4726                 :           0 :         const char *end_ptr;
    4727                 :           0 :         text       *result;
    4728                 :           0 :         int                     arg;
    4729                 :           0 :         bool            funcvariadic;
    4730                 :           0 :         int                     nargs;
    4731                 :           0 :         Datum      *elements = NULL;
    4732                 :           0 :         bool       *nulls = NULL;
    4733                 :           0 :         Oid                     element_type = InvalidOid;
    4734                 :           0 :         Oid                     prev_type = InvalidOid;
    4735                 :           0 :         Oid                     prev_width_type = InvalidOid;
    4736                 :           0 :         FmgrInfo        typoutputfinfo;
    4737                 :           0 :         FmgrInfo        typoutputinfo_width;
    4738                 :             : 
    4739                 :             :         /* When format string is null, immediately return null */
    4740         [ #  # ]:           0 :         if (PG_ARGISNULL(0))
    4741                 :           0 :                 PG_RETURN_NULL();
    4742                 :             : 
    4743                 :             :         /* If argument is marked VARIADIC, expand array into elements */
    4744         [ #  # ]:           0 :         if (get_fn_expr_variadic(fcinfo->flinfo))
    4745                 :             :         {
    4746                 :           0 :                 ArrayType  *arr;
    4747                 :           0 :                 int16           elmlen;
    4748                 :           0 :                 bool            elmbyval;
    4749                 :           0 :                 char            elmalign;
    4750                 :           0 :                 int                     nitems;
    4751                 :             : 
    4752                 :             :                 /* Should have just the one argument */
    4753         [ #  # ]:           0 :                 Assert(PG_NARGS() == 2);
    4754                 :             : 
    4755                 :             :                 /* If argument is NULL, we treat it as zero-length array */
    4756         [ #  # ]:           0 :                 if (PG_ARGISNULL(1))
    4757                 :           0 :                         nitems = 0;
    4758                 :             :                 else
    4759                 :             :                 {
    4760                 :             :                         /*
    4761                 :             :                          * Non-null argument had better be an array.  We assume that any
    4762                 :             :                          * call context that could let get_fn_expr_variadic return true
    4763                 :             :                          * will have checked that a VARIADIC-labeled parameter actually is
    4764                 :             :                          * an array.  So it should be okay to just Assert that it's an
    4765                 :             :                          * array rather than doing a full-fledged error check.
    4766                 :             :                          */
    4767         [ #  # ]:           0 :                         Assert(OidIsValid(get_base_element_type(get_fn_expr_argtype(fcinfo->flinfo, 1))));
    4768                 :             : 
    4769                 :             :                         /* OK, safe to fetch the array value */
    4770                 :           0 :                         arr = PG_GETARG_ARRAYTYPE_P(1);
    4771                 :             : 
    4772                 :             :                         /* Get info about array element type */
    4773                 :           0 :                         element_type = ARR_ELEMTYPE(arr);
    4774                 :           0 :                         get_typlenbyvalalign(element_type,
    4775                 :             :                                                                  &elmlen, &elmbyval, &elmalign);
    4776                 :             : 
    4777                 :             :                         /* Extract all array elements */
    4778                 :           0 :                         deconstruct_array(arr, element_type, elmlen, elmbyval, elmalign,
    4779                 :             :                                                           &elements, &nulls, &nitems);
    4780                 :             :                 }
    4781                 :             : 
    4782                 :           0 :                 nargs = nitems + 1;
    4783                 :           0 :                 funcvariadic = true;
    4784                 :           0 :         }
    4785                 :             :         else
    4786                 :             :         {
    4787                 :             :                 /* Non-variadic case, we'll process the arguments individually */
    4788                 :           0 :                 nargs = PG_NARGS();
    4789                 :           0 :                 funcvariadic = false;
    4790                 :             :         }
    4791                 :             : 
    4792                 :             :         /* Setup for main loop. */
    4793                 :           0 :         fmt = PG_GETARG_TEXT_PP(0);
    4794                 :           0 :         start_ptr = VARDATA_ANY(fmt);
    4795                 :           0 :         end_ptr = start_ptr + VARSIZE_ANY_EXHDR(fmt);
    4796                 :           0 :         initStringInfo(&str);
    4797                 :           0 :         arg = 1;                                        /* next argument position to print */
    4798                 :             : 
    4799                 :             :         /* Scan format string, looking for conversion specifiers. */
    4800         [ #  # ]:           0 :         for (cp = start_ptr; cp < end_ptr; cp++)
    4801                 :             :         {
    4802                 :           0 :                 int                     argpos;
    4803                 :           0 :                 int                     widthpos;
    4804                 :           0 :                 int                     flags;
    4805                 :           0 :                 int                     width;
    4806                 :           0 :                 Datum           value;
    4807                 :           0 :                 bool            isNull;
    4808                 :           0 :                 Oid                     typid;
    4809                 :             : 
    4810                 :             :                 /*
    4811                 :             :                  * If it's not the start of a conversion specifier, just copy it to
    4812                 :             :                  * the output buffer.
    4813                 :             :                  */
    4814         [ #  # ]:           0 :                 if (*cp != '%')
    4815                 :             :                 {
    4816         [ #  # ]:           0 :                         appendStringInfoCharMacro(&str, *cp);
    4817                 :           0 :                         continue;
    4818                 :             :                 }
    4819                 :             : 
    4820   [ #  #  #  #  :           0 :                 ADVANCE_PARSE_POINTER(cp, end_ptr);
                   #  # ]
    4821                 :             : 
    4822                 :             :                 /* Easy case: %% outputs a single % */
    4823         [ #  # ]:           0 :                 if (*cp == '%')
    4824                 :             :                 {
    4825         [ #  # ]:           0 :                         appendStringInfoCharMacro(&str, *cp);
    4826                 :           0 :                         continue;
    4827                 :             :                 }
    4828                 :             : 
    4829                 :             :                 /* Parse the optional portions of the format specifier */
    4830                 :           0 :                 cp = text_format_parse_format(cp, end_ptr,
    4831                 :             :                                                                           &argpos, &widthpos,
    4832                 :             :                                                                           &flags, &width);
    4833                 :             : 
    4834                 :             :                 /*
    4835                 :             :                  * Next we should see the main conversion specifier.  Whether or not
    4836                 :             :                  * an argument position was present, it's known that at least one
    4837                 :             :                  * character remains in the string at this point.  Experience suggests
    4838                 :             :                  * that it's worth checking that that character is one of the expected
    4839                 :             :                  * ones before we try to fetch arguments, so as to produce the least
    4840                 :             :                  * confusing response to a mis-formatted specifier.
    4841                 :             :                  */
    4842         [ #  # ]:           0 :                 if (strchr("sIL", *cp) == NULL)
    4843   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    4844                 :             :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    4845                 :             :                                          errmsg("unrecognized format() type specifier \"%.*s\"",
    4846                 :             :                                                         pg_mblen(cp), cp),
    4847                 :             :                                          errhint("For a single \"%%\" use \"%%%%\".")));
    4848                 :             : 
    4849                 :             :                 /* If indirect width was specified, get its value */
    4850         [ #  # ]:           0 :                 if (widthpos >= 0)
    4851                 :             :                 {
    4852                 :             :                         /* Collect the specified or next argument position */
    4853         [ #  # ]:           0 :                         if (widthpos > 0)
    4854                 :           0 :                                 arg = widthpos;
    4855         [ #  # ]:           0 :                         if (arg >= nargs)
    4856   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    4857                 :             :                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    4858                 :             :                                                  errmsg("too few arguments for format()")));
    4859                 :             : 
    4860                 :             :                         /* Get the value and type of the selected argument */
    4861         [ #  # ]:           0 :                         if (!funcvariadic)
    4862                 :             :                         {
    4863                 :           0 :                                 value = PG_GETARG_DATUM(arg);
    4864                 :           0 :                                 isNull = PG_ARGISNULL(arg);
    4865                 :           0 :                                 typid = get_fn_expr_argtype(fcinfo->flinfo, arg);
    4866                 :           0 :                         }
    4867                 :             :                         else
    4868                 :             :                         {
    4869                 :           0 :                                 value = elements[arg - 1];
    4870                 :           0 :                                 isNull = nulls[arg - 1];
    4871                 :           0 :                                 typid = element_type;
    4872                 :             :                         }
    4873         [ #  # ]:           0 :                         if (!OidIsValid(typid))
    4874   [ #  #  #  # ]:           0 :                                 elog(ERROR, "could not determine data type of format() input");
    4875                 :             : 
    4876                 :           0 :                         arg++;
    4877                 :             : 
    4878                 :             :                         /* We can treat NULL width the same as zero */
    4879         [ #  # ]:           0 :                         if (isNull)
    4880                 :           0 :                                 width = 0;
    4881         [ #  # ]:           0 :                         else if (typid == INT4OID)
    4882                 :           0 :                                 width = DatumGetInt32(value);
    4883         [ #  # ]:           0 :                         else if (typid == INT2OID)
    4884                 :           0 :                                 width = DatumGetInt16(value);
    4885                 :             :                         else
    4886                 :             :                         {
    4887                 :             :                                 /* For less-usual datatypes, convert to text then to int */
    4888                 :           0 :                                 char       *str;
    4889                 :             : 
    4890         [ #  # ]:           0 :                                 if (typid != prev_width_type)
    4891                 :             :                                 {
    4892                 :           0 :                                         Oid                     typoutputfunc;
    4893                 :           0 :                                         bool            typIsVarlena;
    4894                 :             : 
    4895                 :           0 :                                         getTypeOutputInfo(typid, &typoutputfunc, &typIsVarlena);
    4896                 :           0 :                                         fmgr_info(typoutputfunc, &typoutputinfo_width);
    4897                 :           0 :                                         prev_width_type = typid;
    4898                 :           0 :                                 }
    4899                 :             : 
    4900                 :           0 :                                 str = OutputFunctionCall(&typoutputinfo_width, value);
    4901                 :             : 
    4902                 :             :                                 /* pg_strtoint32 will complain about bad data or overflow */
    4903                 :           0 :                                 width = pg_strtoint32(str);
    4904                 :             : 
    4905                 :           0 :                                 pfree(str);
    4906                 :           0 :                         }
    4907                 :           0 :                 }
    4908                 :             : 
    4909                 :             :                 /* Collect the specified or next argument position */
    4910         [ #  # ]:           0 :                 if (argpos > 0)
    4911                 :           0 :                         arg = argpos;
    4912         [ #  # ]:           0 :                 if (arg >= nargs)
    4913   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    4914                 :             :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    4915                 :             :                                          errmsg("too few arguments for format()")));
    4916                 :             : 
    4917                 :             :                 /* Get the value and type of the selected argument */
    4918         [ #  # ]:           0 :                 if (!funcvariadic)
    4919                 :             :                 {
    4920                 :           0 :                         value = PG_GETARG_DATUM(arg);
    4921                 :           0 :                         isNull = PG_ARGISNULL(arg);
    4922                 :           0 :                         typid = get_fn_expr_argtype(fcinfo->flinfo, arg);
    4923                 :           0 :                 }
    4924                 :             :                 else
    4925                 :             :                 {
    4926                 :           0 :                         value = elements[arg - 1];
    4927                 :           0 :                         isNull = nulls[arg - 1];
    4928                 :           0 :                         typid = element_type;
    4929                 :             :                 }
    4930         [ #  # ]:           0 :                 if (!OidIsValid(typid))
    4931   [ #  #  #  # ]:           0 :                         elog(ERROR, "could not determine data type of format() input");
    4932                 :             : 
    4933                 :           0 :                 arg++;
    4934                 :             : 
    4935                 :             :                 /*
    4936                 :             :                  * Get the appropriate typOutput function, reusing previous one if
    4937                 :             :                  * same type as previous argument.  That's particularly useful in the
    4938                 :             :                  * variadic-array case, but often saves work even for ordinary calls.
    4939                 :             :                  */
    4940         [ #  # ]:           0 :                 if (typid != prev_type)
    4941                 :             :                 {
    4942                 :           0 :                         Oid                     typoutputfunc;
    4943                 :           0 :                         bool            typIsVarlena;
    4944                 :             : 
    4945                 :           0 :                         getTypeOutputInfo(typid, &typoutputfunc, &typIsVarlena);
    4946                 :           0 :                         fmgr_info(typoutputfunc, &typoutputfinfo);
    4947                 :           0 :                         prev_type = typid;
    4948                 :           0 :                 }
    4949                 :             : 
    4950                 :             :                 /*
    4951                 :             :                  * And now we can format the value.
    4952                 :             :                  */
    4953         [ #  # ]:           0 :                 switch (*cp)
    4954                 :             :                 {
    4955                 :             :                         case 's':
    4956                 :             :                         case 'I':
    4957                 :             :                         case 'L':
    4958                 :           0 :                                 text_format_string_conversion(&str, *cp, &typoutputfinfo,
    4959                 :           0 :                                                                                           value, isNull,
    4960                 :           0 :                                                                                           flags, width);
    4961                 :           0 :                                 break;
    4962                 :             :                         default:
    4963                 :             :                                 /* should not get here, because of previous check */
    4964   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    4965                 :             :                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    4966                 :             :                                                  errmsg("unrecognized format() type specifier \"%.*s\"",
    4967                 :             :                                                                 pg_mblen(cp), cp),
    4968                 :             :                                                  errhint("For a single \"%%\" use \"%%%%\".")));
    4969                 :           0 :                                 break;
    4970                 :             :                 }
    4971      [ #  #  # ]:           0 :         }
    4972                 :             : 
    4973                 :             :         /* Don't need deconstruct_array results anymore. */
    4974         [ #  # ]:           0 :         if (elements != NULL)
    4975                 :           0 :                 pfree(elements);
    4976         [ #  # ]:           0 :         if (nulls != NULL)
    4977                 :           0 :                 pfree(nulls);
    4978                 :             : 
    4979                 :             :         /* Generate results. */
    4980                 :           0 :         result = cstring_to_text_with_len(str.data, str.len);
    4981                 :           0 :         pfree(str.data);
    4982                 :             : 
    4983                 :           0 :         PG_RETURN_TEXT_P(result);
    4984                 :           0 : }
    4985                 :             : 
    4986                 :             : /*
    4987                 :             :  * Parse contiguous digits as a decimal number.
    4988                 :             :  *
    4989                 :             :  * Returns true if some digits could be parsed.
    4990                 :             :  * The value is returned into *value, and *ptr is advanced to the next
    4991                 :             :  * character to be parsed.
    4992                 :             :  *
    4993                 :             :  * Note parsing invariant: at least one character is known available before
    4994                 :             :  * string end (end_ptr) at entry, and this is still true at exit.
    4995                 :             :  */
    4996                 :             : static bool
    4997                 :           0 : text_format_parse_digits(const char **ptr, const char *end_ptr, int *value)
    4998                 :             : {
    4999                 :           0 :         bool            found = false;
    5000                 :           0 :         const char *cp = *ptr;
    5001                 :           0 :         int                     val = 0;
    5002                 :             : 
    5003   [ #  #  #  # ]:           0 :         while (*cp >= '0' && *cp <= '9')
    5004                 :             :         {
    5005                 :           0 :                 int8            digit = (*cp - '0');
    5006                 :             : 
    5007         [ #  # ]:           0 :                 if (unlikely(pg_mul_s32_overflow(val, 10, &val)) ||
    5008                 :           0 :                         unlikely(pg_add_s32_overflow(val, digit, &val)))
    5009   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    5010                 :             :                                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
    5011                 :             :                                          errmsg("number is out of range")));
    5012   [ #  #  #  #  :           0 :                 ADVANCE_PARSE_POINTER(cp, end_ptr);
                   #  # ]
    5013                 :           0 :                 found = true;
    5014                 :           0 :         }
    5015                 :             : 
    5016                 :           0 :         *ptr = cp;
    5017                 :           0 :         *value = val;
    5018                 :             : 
    5019                 :           0 :         return found;
    5020                 :           0 : }
    5021                 :             : 
    5022                 :             : /*
    5023                 :             :  * Parse a format specifier (generally following the SUS printf spec).
    5024                 :             :  *
    5025                 :             :  * We have already advanced over the initial '%', and we are looking for
    5026                 :             :  * [argpos][flags][width]type (but the type character is not consumed here).
    5027                 :             :  *
    5028                 :             :  * Inputs are start_ptr (the position after '%') and end_ptr (string end + 1).
    5029                 :             :  * Output parameters:
    5030                 :             :  *      argpos: argument position for value to be printed.  -1 means unspecified.
    5031                 :             :  *      widthpos: argument position for width.  Zero means the argument position
    5032                 :             :  *                      was unspecified (ie, take the next arg) and -1 means no width
    5033                 :             :  *                      argument (width was omitted or specified as a constant).
    5034                 :             :  *      flags: bitmask of flags.
    5035                 :             :  *      width: directly-specified width value.  Zero means the width was omitted
    5036                 :             :  *                      (note it's not necessary to distinguish this case from an explicit
    5037                 :             :  *                      zero width value).
    5038                 :             :  *
    5039                 :             :  * The function result is the next character position to be parsed, ie, the
    5040                 :             :  * location where the type character is/should be.
    5041                 :             :  *
    5042                 :             :  * Note parsing invariant: at least one character is known available before
    5043                 :             :  * string end (end_ptr) at entry, and this is still true at exit.
    5044                 :             :  */
    5045                 :             : static const char *
    5046                 :           0 : text_format_parse_format(const char *start_ptr, const char *end_ptr,
    5047                 :             :                                                  int *argpos, int *widthpos,
    5048                 :             :                                                  int *flags, int *width)
    5049                 :             : {
    5050                 :           0 :         const char *cp = start_ptr;
    5051                 :           0 :         int                     n;
    5052                 :             : 
    5053                 :             :         /* set defaults for output parameters */
    5054                 :           0 :         *argpos = -1;
    5055                 :           0 :         *widthpos = -1;
    5056                 :           0 :         *flags = 0;
    5057                 :           0 :         *width = 0;
    5058                 :             : 
    5059                 :             :         /* try to identify first number */
    5060         [ #  # ]:           0 :         if (text_format_parse_digits(&cp, end_ptr, &n))
    5061                 :             :         {
    5062         [ #  # ]:           0 :                 if (*cp != '$')
    5063                 :             :                 {
    5064                 :             :                         /* Must be just a width and a type, so we're done */
    5065                 :           0 :                         *width = n;
    5066                 :           0 :                         return cp;
    5067                 :             :                 }
    5068                 :             :                 /* The number was argument position */
    5069                 :           0 :                 *argpos = n;
    5070                 :             :                 /* Explicit 0 for argument index is immediately refused */
    5071         [ #  # ]:           0 :                 if (n == 0)
    5072   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    5073                 :             :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    5074                 :             :                                          errmsg("format specifies argument 0, but arguments are numbered from 1")));
    5075   [ #  #  #  #  :           0 :                 ADVANCE_PARSE_POINTER(cp, end_ptr);
                   #  # ]
    5076                 :           0 :         }
    5077                 :             : 
    5078                 :             :         /* Handle flags (only minus is supported now) */
    5079         [ #  # ]:           0 :         while (*cp == '-')
    5080                 :             :         {
    5081                 :           0 :                 *flags |= TEXT_FORMAT_FLAG_MINUS;
    5082   [ #  #  #  #  :           0 :                 ADVANCE_PARSE_POINTER(cp, end_ptr);
                   #  # ]
    5083                 :             :         }
    5084                 :             : 
    5085         [ #  # ]:           0 :         if (*cp == '*')
    5086                 :             :         {
    5087                 :             :                 /* Handle indirect width */
    5088   [ #  #  #  #  :           0 :                 ADVANCE_PARSE_POINTER(cp, end_ptr);
                   #  # ]
    5089         [ #  # ]:           0 :                 if (text_format_parse_digits(&cp, end_ptr, &n))
    5090                 :             :                 {
    5091                 :             :                         /* number in this position must be closed by $ */
    5092         [ #  # ]:           0 :                         if (*cp != '$')
    5093   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    5094                 :             :                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    5095                 :             :                                                  errmsg("width argument position must be ended by \"$\"")));
    5096                 :             :                         /* The number was width argument position */
    5097                 :           0 :                         *widthpos = n;
    5098                 :             :                         /* Explicit 0 for argument index is immediately refused */
    5099         [ #  # ]:           0 :                         if (n == 0)
    5100   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    5101                 :             :                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    5102                 :             :                                                  errmsg("format specifies argument 0, but arguments are numbered from 1")));
    5103   [ #  #  #  #  :           0 :                         ADVANCE_PARSE_POINTER(cp, end_ptr);
                   #  # ]
    5104                 :           0 :                 }
    5105                 :             :                 else
    5106                 :           0 :                         *widthpos = 0;          /* width's argument position is unspecified */
    5107                 :           0 :         }
    5108                 :             :         else
    5109                 :             :         {
    5110                 :             :                 /* Check for direct width specification */
    5111         [ #  # ]:           0 :                 if (text_format_parse_digits(&cp, end_ptr, &n))
    5112                 :           0 :                         *width = n;
    5113                 :             :         }
    5114                 :             : 
    5115                 :             :         /* cp should now be pointing at type character */
    5116                 :           0 :         return cp;
    5117                 :           0 : }
    5118                 :             : 
    5119                 :             : /*
    5120                 :             :  * Format a %s, %I, or %L conversion
    5121                 :             :  */
    5122                 :             : static void
    5123                 :           0 : text_format_string_conversion(StringInfo buf, char conversion,
    5124                 :             :                                                           FmgrInfo *typOutputInfo,
    5125                 :             :                                                           Datum value, bool isNull,
    5126                 :             :                                                           int flags, int width)
    5127                 :             : {
    5128                 :           0 :         char       *str;
    5129                 :             : 
    5130                 :             :         /* Handle NULL arguments before trying to stringify the value. */
    5131         [ #  # ]:           0 :         if (isNull)
    5132                 :             :         {
    5133         [ #  # ]:           0 :                 if (conversion == 's')
    5134                 :           0 :                         text_format_append_string(buf, "", flags, width);
    5135         [ #  # ]:           0 :                 else if (conversion == 'L')
    5136                 :           0 :                         text_format_append_string(buf, "NULL", flags, width);
    5137         [ #  # ]:           0 :                 else if (conversion == 'I')
    5138   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    5139                 :             :                                         (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    5140                 :             :                                          errmsg("null values cannot be formatted as an SQL identifier")));
    5141                 :           0 :                 return;
    5142                 :             :         }
    5143                 :             : 
    5144                 :             :         /* Stringify. */
    5145                 :           0 :         str = OutputFunctionCall(typOutputInfo, value);
    5146                 :             : 
    5147                 :             :         /* Escape. */
    5148         [ #  # ]:           0 :         if (conversion == 'I')
    5149                 :             :         {
    5150                 :             :                 /* quote_identifier may or may not allocate a new string. */
    5151                 :           0 :                 text_format_append_string(buf, quote_identifier(str), flags, width);
    5152                 :           0 :         }
    5153         [ #  # ]:           0 :         else if (conversion == 'L')
    5154                 :             :         {
    5155                 :           0 :                 char       *qstr = quote_literal_cstr(str);
    5156                 :             : 
    5157                 :           0 :                 text_format_append_string(buf, qstr, flags, width);
    5158                 :             :                 /* quote_literal_cstr() always allocates a new string */
    5159                 :           0 :                 pfree(qstr);
    5160                 :           0 :         }
    5161                 :             :         else
    5162                 :           0 :                 text_format_append_string(buf, str, flags, width);
    5163                 :             : 
    5164                 :             :         /* Cleanup. */
    5165                 :           0 :         pfree(str);
    5166         [ #  # ]:           0 : }
    5167                 :             : 
    5168                 :             : /*
    5169                 :             :  * Append str to buf, padding as directed by flags/width
    5170                 :             :  */
    5171                 :             : static void
    5172                 :           0 : text_format_append_string(StringInfo buf, const char *str,
    5173                 :             :                                                   int flags, int width)
    5174                 :             : {
    5175                 :           0 :         bool            align_to_left = false;
    5176                 :           0 :         int                     len;
    5177                 :             : 
    5178                 :             :         /* fast path for typical easy case */
    5179         [ #  # ]:           0 :         if (width == 0)
    5180                 :             :         {
    5181                 :           0 :                 appendStringInfoString(buf, str);
    5182                 :           0 :                 return;
    5183                 :             :         }
    5184                 :             : 
    5185         [ #  # ]:           0 :         if (width < 0)
    5186                 :             :         {
    5187                 :             :                 /* Negative width: implicit '-' flag, then take absolute value */
    5188                 :           0 :                 align_to_left = true;
    5189                 :             :                 /* -INT_MIN is undefined */
    5190         [ #  # ]:           0 :                 if (width <= INT_MIN)
    5191   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    5192                 :             :                                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
    5193                 :             :                                          errmsg("number is out of range")));
    5194                 :           0 :                 width = -width;
    5195                 :           0 :         }
    5196         [ #  # ]:           0 :         else if (flags & TEXT_FORMAT_FLAG_MINUS)
    5197                 :           0 :                 align_to_left = true;
    5198                 :             : 
    5199                 :           0 :         len = pg_mbstrlen(str);
    5200         [ #  # ]:           0 :         if (align_to_left)
    5201                 :             :         {
    5202                 :             :                 /* left justify */
    5203                 :           0 :                 appendStringInfoString(buf, str);
    5204         [ #  # ]:           0 :                 if (len < width)
    5205                 :           0 :                         appendStringInfoSpaces(buf, width - len);
    5206                 :           0 :         }
    5207                 :             :         else
    5208                 :             :         {
    5209                 :             :                 /* right justify */
    5210         [ #  # ]:           0 :                 if (len < width)
    5211                 :           0 :                         appendStringInfoSpaces(buf, width - len);
    5212                 :           0 :                 appendStringInfoString(buf, str);
    5213                 :             :         }
    5214         [ #  # ]:           0 : }
    5215                 :             : 
    5216                 :             : /*
    5217                 :             :  * text_format_nv - nonvariadic wrapper for text_format function.
    5218                 :             :  *
    5219                 :             :  * note: this wrapper is necessary to pass the sanity check in opr_sanity,
    5220                 :             :  * which checks that all built-in functions that share the implementing C
    5221                 :             :  * function take the same number of arguments.
    5222                 :             :  */
    5223                 :             : Datum
    5224                 :           0 : text_format_nv(PG_FUNCTION_ARGS)
    5225                 :             : {
    5226                 :           0 :         return text_format(fcinfo);
    5227                 :             : }
    5228                 :             : 
    5229                 :             : /*
    5230                 :             :  * Helper function for Levenshtein distance functions. Faster than memcmp(),
    5231                 :             :  * for this use case.
    5232                 :             :  */
    5233                 :             : static inline bool
    5234                 :           0 : rest_of_char_same(const char *s1, const char *s2, int len)
    5235                 :             : {
    5236         [ #  # ]:           0 :         while (len > 0)
    5237                 :             :         {
    5238                 :           0 :                 len--;
    5239         [ #  # ]:           0 :                 if (s1[len] != s2[len])
    5240                 :           0 :                         return false;
    5241                 :             :         }
    5242                 :           0 :         return true;
    5243                 :           0 : }
    5244                 :             : 
    5245                 :             : /* Expand each Levenshtein distance variant */
    5246                 :             : #include "levenshtein.c"
    5247                 :             : #define LEVENSHTEIN_LESS_EQUAL
    5248                 :             : #include "levenshtein.c"
    5249                 :             : 
    5250                 :             : 
    5251                 :             : /*
    5252                 :             :  * The following *ClosestMatch() functions can be used to determine whether a
    5253                 :             :  * user-provided string resembles any known valid values, which is useful for
    5254                 :             :  * providing hints in log messages, among other things.  Use these functions
    5255                 :             :  * like so:
    5256                 :             :  *
    5257                 :             :  *              initClosestMatch(&state, source_string, max_distance);
    5258                 :             :  *
    5259                 :             :  *              for (int i = 0; i < num_valid_strings; i++)
    5260                 :             :  *                      updateClosestMatch(&state, valid_strings[i]);
    5261                 :             :  *
    5262                 :             :  *              closestMatch = getClosestMatch(&state);
    5263                 :             :  */
    5264                 :             : 
    5265                 :             : /*
    5266                 :             :  * Initialize the given state with the source string and maximum Levenshtein
    5267                 :             :  * distance to consider.
    5268                 :             :  */
    5269                 :             : void
    5270                 :           0 : initClosestMatch(ClosestMatchState *state, const char *source, int max_d)
    5271                 :             : {
    5272         [ #  # ]:           0 :         Assert(state);
    5273         [ #  # ]:           0 :         Assert(max_d >= 0);
    5274                 :             : 
    5275                 :           0 :         state->source = source;
    5276                 :           0 :         state->min_d = -1;
    5277                 :           0 :         state->max_d = max_d;
    5278                 :           0 :         state->match = NULL;
    5279                 :           0 : }
    5280                 :             : 
    5281                 :             : /*
    5282                 :             :  * If the candidate string is a closer match than the current one saved (or
    5283                 :             :  * there is no match saved), save it as the closest match.
    5284                 :             :  *
    5285                 :             :  * If the source or candidate string is NULL, empty, or too long, this function
    5286                 :             :  * takes no action.  Likewise, if the Levenshtein distance exceeds the maximum
    5287                 :             :  * allowed or more than half the characters are different, no action is taken.
    5288                 :             :  */
    5289                 :             : void
    5290                 :           0 : updateClosestMatch(ClosestMatchState *state, const char *candidate)
    5291                 :             : {
    5292                 :           0 :         int                     dist;
    5293                 :             : 
    5294         [ #  # ]:           0 :         Assert(state);
    5295                 :             : 
    5296   [ #  #  #  # ]:           0 :         if (state->source == NULL || state->source[0] == '\0' ||
    5297   [ #  #  #  # ]:           0 :                 candidate == NULL || candidate[0] == '\0')
    5298                 :           0 :                 return;
    5299                 :             : 
    5300                 :             :         /*
    5301                 :             :          * To avoid ERROR-ing, we check the lengths here instead of setting
    5302                 :             :          * 'trusted' to false in the call to varstr_levenshtein_less_equal().
    5303                 :             :          */
    5304   [ #  #  #  # ]:           0 :         if (strlen(state->source) > MAX_LEVENSHTEIN_STRLEN ||
    5305                 :           0 :                 strlen(candidate) > MAX_LEVENSHTEIN_STRLEN)
    5306                 :           0 :                 return;
    5307                 :             : 
    5308                 :           0 :         dist = varstr_levenshtein_less_equal(state->source, strlen(state->source),
    5309                 :           0 :                                                                                  candidate, strlen(candidate), 1, 1, 1,
    5310                 :           0 :                                                                                  state->max_d, true);
    5311         [ #  # ]:           0 :         if (dist <= state->max_d &&
    5312   [ #  #  #  # ]:           0 :                 dist <= strlen(state->source) / 2 &&
    5313         [ #  # ]:           0 :                 (state->min_d == -1 || dist < state->min_d))
    5314                 :             :         {
    5315                 :           0 :                 state->min_d = dist;
    5316                 :           0 :                 state->match = candidate;
    5317                 :           0 :         }
    5318         [ #  # ]:           0 : }
    5319                 :             : 
    5320                 :             : /*
    5321                 :             :  * Return the closest match.  If no suitable candidates were provided via
    5322                 :             :  * updateClosestMatch(), return NULL.
    5323                 :             :  */
    5324                 :             : const char *
    5325                 :           0 : getClosestMatch(ClosestMatchState *state)
    5326                 :             : {
    5327         [ #  # ]:           0 :         Assert(state);
    5328                 :             : 
    5329                 :           0 :         return state->match;
    5330                 :             : }
    5331                 :             : 
    5332                 :             : 
    5333                 :             : /*
    5334                 :             :  * Unicode support
    5335                 :             :  */
    5336                 :             : 
    5337                 :             : static UnicodeNormalizationForm
    5338                 :           0 : unicode_norm_form_from_string(const char *formstr)
    5339                 :             : {
    5340                 :           0 :         UnicodeNormalizationForm form = -1;
    5341                 :             : 
    5342                 :             :         /*
    5343                 :             :          * Might as well check this while we're here.
    5344                 :             :          */
    5345         [ #  # ]:           0 :         if (GetDatabaseEncoding() != PG_UTF8)
    5346   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    5347                 :             :                                 (errcode(ERRCODE_SYNTAX_ERROR),
    5348                 :             :                                  errmsg("Unicode normalization can only be performed if server encoding is UTF8")));
    5349                 :             : 
    5350         [ #  # ]:           0 :         if (pg_strcasecmp(formstr, "NFC") == 0)
    5351                 :           0 :                 form = UNICODE_NFC;
    5352         [ #  # ]:           0 :         else if (pg_strcasecmp(formstr, "NFD") == 0)
    5353                 :           0 :                 form = UNICODE_NFD;
    5354         [ #  # ]:           0 :         else if (pg_strcasecmp(formstr, "NFKC") == 0)
    5355                 :           0 :                 form = UNICODE_NFKC;
    5356         [ #  # ]:           0 :         else if (pg_strcasecmp(formstr, "NFKD") == 0)
    5357                 :           0 :                 form = UNICODE_NFKD;
    5358                 :             :         else
    5359   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    5360                 :             :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    5361                 :             :                                  errmsg("invalid normalization form: %s", formstr)));
    5362                 :             : 
    5363                 :           0 :         return form;
    5364                 :           0 : }
    5365                 :             : 
    5366                 :             : /*
    5367                 :             :  * Returns version of Unicode used by Postgres in "major.minor" format (the
    5368                 :             :  * same format as the Unicode version reported by ICU). The third component
    5369                 :             :  * ("update version") never involves additions to the character repertoire and
    5370                 :             :  * is unimportant for most purposes.
    5371                 :             :  *
    5372                 :             :  * See: https://unicode.org/versions/
    5373                 :             :  */
    5374                 :             : Datum
    5375                 :           0 : unicode_version(PG_FUNCTION_ARGS)
    5376                 :             : {
    5377                 :           0 :         PG_RETURN_TEXT_P(cstring_to_text(PG_UNICODE_VERSION));
    5378                 :             : }
    5379                 :             : 
    5380                 :             : /*
    5381                 :             :  * Returns version of Unicode used by ICU, if enabled; otherwise NULL.
    5382                 :             :  */
    5383                 :             : Datum
    5384                 :           0 : icu_unicode_version(PG_FUNCTION_ARGS)
    5385                 :             : {
    5386                 :           0 :         const char *version = pg_icu_unicode_version();
    5387                 :             : 
    5388         [ #  # ]:           0 :         if (version)
    5389                 :           0 :                 PG_RETURN_TEXT_P(cstring_to_text(version));
    5390                 :             :         else
    5391                 :           0 :                 PG_RETURN_NULL();
    5392         [ #  # ]:           0 : }
    5393                 :             : 
    5394                 :             : /*
    5395                 :             :  * Check whether the string contains only assigned Unicode code
    5396                 :             :  * points. Requires that the database encoding is UTF-8.
    5397                 :             :  */
    5398                 :             : Datum
    5399                 :           0 : unicode_assigned(PG_FUNCTION_ARGS)
    5400                 :             : {
    5401                 :           0 :         text       *input = PG_GETARG_TEXT_PP(0);
    5402                 :           0 :         unsigned char *p;
    5403                 :           0 :         int                     size;
    5404                 :             : 
    5405         [ #  # ]:           0 :         if (GetDatabaseEncoding() != PG_UTF8)
    5406   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    5407                 :             :                                 (errmsg("Unicode categorization can only be performed if server encoding is UTF8")));
    5408                 :             : 
    5409                 :             :         /* convert to char32_t */
    5410                 :           0 :         size = pg_mbstrlen_with_len(VARDATA_ANY(input), VARSIZE_ANY_EXHDR(input));
    5411                 :           0 :         p = (unsigned char *) VARDATA_ANY(input);
    5412   [ #  #  #  # ]:           0 :         for (int i = 0; i < size; i++)
    5413                 :             :         {
    5414                 :           0 :                 char32_t        uchar = utf8_to_unicode(p);
    5415                 :           0 :                 int                     category = unicode_category(uchar);
    5416                 :             : 
    5417         [ #  # ]:           0 :                 if (category == PG_U_UNASSIGNED)
    5418                 :           0 :                         PG_RETURN_BOOL(false);
    5419                 :             : 
    5420                 :           0 :                 p += pg_utf_mblen(p);
    5421         [ #  # ]:           0 :         }
    5422                 :             : 
    5423                 :           0 :         PG_RETURN_BOOL(true);
    5424                 :           0 : }
    5425                 :             : 
    5426                 :             : Datum
    5427                 :           0 : unicode_normalize_func(PG_FUNCTION_ARGS)
    5428                 :             : {
    5429                 :           0 :         text       *input = PG_GETARG_TEXT_PP(0);
    5430                 :           0 :         char       *formstr = text_to_cstring(PG_GETARG_TEXT_PP(1));
    5431                 :           0 :         UnicodeNormalizationForm form;
    5432                 :           0 :         int                     size;
    5433                 :           0 :         char32_t   *input_chars;
    5434                 :           0 :         char32_t   *output_chars;
    5435                 :           0 :         unsigned char *p;
    5436                 :           0 :         text       *result;
    5437                 :           0 :         int                     i;
    5438                 :             : 
    5439                 :           0 :         form = unicode_norm_form_from_string(formstr);
    5440                 :             : 
    5441                 :             :         /* convert to char32_t */
    5442                 :           0 :         size = pg_mbstrlen_with_len(VARDATA_ANY(input), VARSIZE_ANY_EXHDR(input));
    5443                 :           0 :         input_chars = palloc((size + 1) * sizeof(char32_t));
    5444                 :           0 :         p = (unsigned char *) VARDATA_ANY(input);
    5445         [ #  # ]:           0 :         for (i = 0; i < size; i++)
    5446                 :             :         {
    5447                 :           0 :                 input_chars[i] = utf8_to_unicode(p);
    5448                 :           0 :                 p += pg_utf_mblen(p);
    5449                 :           0 :         }
    5450                 :           0 :         input_chars[i] = (char32_t) '\0';
    5451         [ #  # ]:           0 :         Assert((char *) p == VARDATA_ANY(input) + VARSIZE_ANY_EXHDR(input));
    5452                 :             : 
    5453                 :             :         /* action */
    5454                 :           0 :         output_chars = unicode_normalize(form, input_chars);
    5455                 :             : 
    5456                 :             :         /* convert back to UTF-8 string */
    5457                 :           0 :         size = 0;
    5458         [ #  # ]:           0 :         for (char32_t *wp = output_chars; *wp; wp++)
    5459                 :             :         {
    5460                 :           0 :                 unsigned char buf[4];
    5461                 :             : 
    5462                 :           0 :                 unicode_to_utf8(*wp, buf);
    5463                 :           0 :                 size += pg_utf_mblen(buf);
    5464                 :           0 :         }
    5465                 :             : 
    5466                 :           0 :         result = palloc(size + VARHDRSZ);
    5467                 :           0 :         SET_VARSIZE(result, size + VARHDRSZ);
    5468                 :             : 
    5469                 :           0 :         p = (unsigned char *) VARDATA_ANY(result);
    5470         [ #  # ]:           0 :         for (char32_t *wp = output_chars; *wp; wp++)
    5471                 :             :         {
    5472                 :           0 :                 unicode_to_utf8(*wp, p);
    5473                 :           0 :                 p += pg_utf_mblen(p);
    5474                 :           0 :         }
    5475         [ #  # ]:           0 :         Assert((char *) p == (char *) result + size + VARHDRSZ);
    5476                 :             : 
    5477                 :           0 :         PG_RETURN_TEXT_P(result);
    5478                 :           0 : }
    5479                 :             : 
    5480                 :             : /*
    5481                 :             :  * Check whether the string is in the specified Unicode normalization form.
    5482                 :             :  *
    5483                 :             :  * This is done by converting the string to the specified normal form and then
    5484                 :             :  * comparing that to the original string.  To speed that up, we also apply the
    5485                 :             :  * "quick check" algorithm specified in UAX #15, which can give a yes or no
    5486                 :             :  * answer for many strings by just scanning the string once.
    5487                 :             :  *
    5488                 :             :  * This function should generally be optimized for the case where the string
    5489                 :             :  * is in fact normalized.  In that case, we'll end up looking at the entire
    5490                 :             :  * string, so it's probably not worth doing any incremental conversion etc.
    5491                 :             :  */
    5492                 :             : Datum
    5493                 :           0 : unicode_is_normalized(PG_FUNCTION_ARGS)
    5494                 :             : {
    5495                 :           0 :         text       *input = PG_GETARG_TEXT_PP(0);
    5496                 :           0 :         char       *formstr = text_to_cstring(PG_GETARG_TEXT_PP(1));
    5497                 :           0 :         UnicodeNormalizationForm form;
    5498                 :           0 :         int                     size;
    5499                 :           0 :         char32_t   *input_chars;
    5500                 :           0 :         char32_t   *output_chars;
    5501                 :           0 :         unsigned char *p;
    5502                 :           0 :         int                     i;
    5503                 :           0 :         UnicodeNormalizationQC quickcheck;
    5504                 :           0 :         int                     output_size;
    5505                 :           0 :         bool            result;
    5506                 :             : 
    5507                 :           0 :         form = unicode_norm_form_from_string(formstr);
    5508                 :             : 
    5509                 :             :         /* convert to char32_t */
    5510                 :           0 :         size = pg_mbstrlen_with_len(VARDATA_ANY(input), VARSIZE_ANY_EXHDR(input));
    5511                 :           0 :         input_chars = palloc((size + 1) * sizeof(char32_t));
    5512                 :           0 :         p = (unsigned char *) VARDATA_ANY(input);
    5513         [ #  # ]:           0 :         for (i = 0; i < size; i++)
    5514                 :             :         {
    5515                 :           0 :                 input_chars[i] = utf8_to_unicode(p);
    5516                 :           0 :                 p += pg_utf_mblen(p);
    5517                 :           0 :         }
    5518                 :           0 :         input_chars[i] = (char32_t) '\0';
    5519         [ #  # ]:           0 :         Assert((char *) p == VARDATA_ANY(input) + VARSIZE_ANY_EXHDR(input));
    5520                 :             : 
    5521                 :             :         /* quick check (see UAX #15) */
    5522                 :           0 :         quickcheck = unicode_is_normalized_quickcheck(form, input_chars);
    5523         [ #  # ]:           0 :         if (quickcheck == UNICODE_NORM_QC_YES)
    5524                 :           0 :                 PG_RETURN_BOOL(true);
    5525         [ #  # ]:           0 :         else if (quickcheck == UNICODE_NORM_QC_NO)
    5526                 :           0 :                 PG_RETURN_BOOL(false);
    5527                 :             : 
    5528                 :             :         /* normalize and compare with original */
    5529                 :           0 :         output_chars = unicode_normalize(form, input_chars);
    5530                 :             : 
    5531                 :           0 :         output_size = 0;
    5532         [ #  # ]:           0 :         for (char32_t *wp = output_chars; *wp; wp++)
    5533                 :           0 :                 output_size++;
    5534                 :             : 
    5535         [ #  # ]:           0 :         result = (size == output_size) &&
    5536                 :           0 :                 (memcmp(input_chars, output_chars, size * sizeof(char32_t)) == 0);
    5537                 :             : 
    5538                 :           0 :         PG_RETURN_BOOL(result);
    5539                 :           0 : }
    5540                 :             : 
    5541                 :             : /*
    5542                 :             :  * Check if first n chars are hexadecimal digits
    5543                 :             :  */
    5544                 :             : static bool
    5545                 :           0 : isxdigits_n(const char *instr, size_t n)
    5546                 :             : {
    5547   [ #  #  #  #  :           0 :         for (size_t i = 0; i < n; i++)
                      # ]
    5548         [ #  # ]:           0 :                 if (!isxdigit((unsigned char) instr[i]))
    5549                 :           0 :                         return false;
    5550                 :             : 
    5551                 :           0 :         return true;
    5552                 :           0 : }
    5553                 :             : 
    5554                 :             : static unsigned int
    5555                 :           0 : hexval(unsigned char c)
    5556                 :             : {
    5557   [ #  #  #  # ]:           0 :         if (c >= '0' && c <= '9')
    5558                 :           0 :                 return c - '0';
    5559   [ #  #  #  # ]:           0 :         if (c >= 'a' && c <= 'f')
    5560                 :           0 :                 return c - 'a' + 0xA;
    5561         [ #  # ]:           0 :         if (c >= 'A' && c <= 'F')
    5562                 :           0 :                 return c - 'A' + 0xA;
    5563   [ #  #  #  # ]:           0 :         elog(ERROR, "invalid hexadecimal digit");
    5564                 :           0 :         return 0;                                       /* not reached */
    5565                 :           0 : }
    5566                 :             : 
    5567                 :             : /*
    5568                 :             :  * Translate string with hexadecimal digits to number
    5569                 :             :  */
    5570                 :             : static unsigned int
    5571                 :           0 : hexval_n(const char *instr, size_t n)
    5572                 :             : {
    5573                 :           0 :         unsigned int result = 0;
    5574                 :             : 
    5575         [ #  # ]:           0 :         for (size_t i = 0; i < n; i++)
    5576                 :           0 :                 result += hexval(instr[i]) << (4 * (n - i - 1));
    5577                 :             : 
    5578                 :           0 :         return result;
    5579                 :           0 : }
    5580                 :             : 
    5581                 :             : /*
    5582                 :             :  * Replaces Unicode escape sequences by Unicode characters
    5583                 :             :  */
    5584                 :             : Datum
    5585                 :           0 : unistr(PG_FUNCTION_ARGS)
    5586                 :             : {
    5587                 :           0 :         text       *input_text = PG_GETARG_TEXT_PP(0);
    5588                 :           0 :         char       *instr;
    5589                 :           0 :         int                     len;
    5590                 :           0 :         StringInfoData str;
    5591                 :           0 :         text       *result;
    5592                 :           0 :         char16_t        pair_first = 0;
    5593                 :           0 :         char            cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
    5594                 :             : 
    5595                 :           0 :         instr = VARDATA_ANY(input_text);
    5596                 :           0 :         len = VARSIZE_ANY_EXHDR(input_text);
    5597                 :             : 
    5598                 :           0 :         initStringInfo(&str);
    5599                 :             : 
    5600         [ #  # ]:           0 :         while (len > 0)
    5601                 :             :         {
    5602         [ #  # ]:           0 :                 if (instr[0] == '\\')
    5603                 :             :                 {
    5604   [ #  #  #  # ]:           0 :                         if (len >= 2 &&
    5605                 :           0 :                                 instr[1] == '\\')
    5606                 :             :                         {
    5607         [ #  # ]:           0 :                                 if (pair_first)
    5608                 :           0 :                                         goto invalid_pair;
    5609                 :           0 :                                 appendStringInfoChar(&str, '\\');
    5610                 :           0 :                                 instr += 2;
    5611                 :           0 :                                 len -= 2;
    5612                 :           0 :                         }
    5613   [ #  #  #  # ]:           0 :                         else if ((len >= 5 && isxdigits_n(instr + 1, 4)) ||
    5614   [ #  #  #  # ]:           0 :                                          (len >= 6 && instr[1] == 'u' && isxdigits_n(instr + 2, 4)))
    5615                 :             :                         {
    5616                 :           0 :                                 char32_t        unicode;
    5617                 :           0 :                                 int                     offset = instr[1] == 'u' ? 2 : 1;
    5618                 :             : 
    5619                 :           0 :                                 unicode = hexval_n(instr + offset, 4);
    5620                 :             : 
    5621         [ #  # ]:           0 :                                 if (!is_valid_unicode_codepoint(unicode))
    5622   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
    5623                 :             :                                                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    5624                 :             :                                                         errmsg("invalid Unicode code point: %04X", unicode));
    5625                 :             : 
    5626         [ #  # ]:           0 :                                 if (pair_first)
    5627                 :             :                                 {
    5628         [ #  # ]:           0 :                                         if (is_utf16_surrogate_second(unicode))
    5629                 :             :                                         {
    5630                 :           0 :                                                 unicode = surrogate_pair_to_codepoint(pair_first, unicode);
    5631                 :           0 :                                                 pair_first = 0;
    5632                 :           0 :                                         }
    5633                 :             :                                         else
    5634                 :           0 :                                                 goto invalid_pair;
    5635                 :           0 :                                 }
    5636         [ #  # ]:           0 :                                 else if (is_utf16_surrogate_second(unicode))
    5637                 :           0 :                                         goto invalid_pair;
    5638                 :             : 
    5639         [ #  # ]:           0 :                                 if (is_utf16_surrogate_first(unicode))
    5640                 :           0 :                                         pair_first = unicode;
    5641                 :             :                                 else
    5642                 :             :                                 {
    5643                 :           0 :                                         pg_unicode_to_server(unicode, (unsigned char *) cbuf);
    5644                 :           0 :                                         appendStringInfoString(&str, cbuf);
    5645                 :             :                                 }
    5646                 :             : 
    5647                 :           0 :                                 instr += 4 + offset;
    5648                 :           0 :                                 len -= 4 + offset;
    5649         [ #  # ]:           0 :                         }
    5650   [ #  #  #  #  :           0 :                         else if (len >= 8 && instr[1] == '+' && isxdigits_n(instr + 2, 6))
                   #  # ]
    5651                 :             :                         {
    5652                 :           0 :                                 char32_t        unicode;
    5653                 :             : 
    5654                 :           0 :                                 unicode = hexval_n(instr + 2, 6);
    5655                 :             : 
    5656         [ #  # ]:           0 :                                 if (!is_valid_unicode_codepoint(unicode))
    5657   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
    5658                 :             :                                                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    5659                 :             :                                                         errmsg("invalid Unicode code point: %04X", unicode));
    5660                 :             : 
    5661         [ #  # ]:           0 :                                 if (pair_first)
    5662                 :             :                                 {
    5663         [ #  # ]:           0 :                                         if (is_utf16_surrogate_second(unicode))
    5664                 :             :                                         {
    5665                 :           0 :                                                 unicode = surrogate_pair_to_codepoint(pair_first, unicode);
    5666                 :           0 :                                                 pair_first = 0;
    5667                 :           0 :                                         }
    5668                 :             :                                         else
    5669                 :           0 :                                                 goto invalid_pair;
    5670                 :           0 :                                 }
    5671         [ #  # ]:           0 :                                 else if (is_utf16_surrogate_second(unicode))
    5672                 :           0 :                                         goto invalid_pair;
    5673                 :             : 
    5674         [ #  # ]:           0 :                                 if (is_utf16_surrogate_first(unicode))
    5675                 :           0 :                                         pair_first = unicode;
    5676                 :             :                                 else
    5677                 :             :                                 {
    5678                 :           0 :                                         pg_unicode_to_server(unicode, (unsigned char *) cbuf);
    5679                 :           0 :                                         appendStringInfoString(&str, cbuf);
    5680                 :             :                                 }
    5681                 :             : 
    5682                 :           0 :                                 instr += 8;
    5683                 :           0 :                                 len -= 8;
    5684         [ #  # ]:           0 :                         }
    5685         [ #  # ]:           0 :                         else if (len >= 10 && instr[1] == 'U' && isxdigits_n(instr + 2, 8))
    5686                 :             :                         {
    5687                 :           0 :                                 char32_t        unicode;
    5688                 :             : 
    5689                 :           0 :                                 unicode = hexval_n(instr + 2, 8);
    5690                 :             : 
    5691         [ #  # ]:           0 :                                 if (!is_valid_unicode_codepoint(unicode))
    5692   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
    5693                 :             :                                                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    5694                 :             :                                                         errmsg("invalid Unicode code point: %04X", unicode));
    5695                 :             : 
    5696         [ #  # ]:           0 :                                 if (pair_first)
    5697                 :             :                                 {
    5698         [ #  # ]:           0 :                                         if (is_utf16_surrogate_second(unicode))
    5699                 :             :                                         {
    5700                 :           0 :                                                 unicode = surrogate_pair_to_codepoint(pair_first, unicode);
    5701                 :           0 :                                                 pair_first = 0;
    5702                 :           0 :                                         }
    5703                 :             :                                         else
    5704                 :           0 :                                                 goto invalid_pair;
    5705                 :           0 :                                 }
    5706         [ #  # ]:           0 :                                 else if (is_utf16_surrogate_second(unicode))
    5707                 :           0 :                                         goto invalid_pair;
    5708                 :             : 
    5709         [ #  # ]:           0 :                                 if (is_utf16_surrogate_first(unicode))
    5710                 :           0 :                                         pair_first = unicode;
    5711                 :             :                                 else
    5712                 :             :                                 {
    5713                 :           0 :                                         pg_unicode_to_server(unicode, (unsigned char *) cbuf);
    5714                 :           0 :                                         appendStringInfoString(&str, cbuf);
    5715                 :             :                                 }
    5716                 :             : 
    5717                 :           0 :                                 instr += 10;
    5718                 :           0 :                                 len -= 10;
    5719      [ #  #  # ]:           0 :                         }
    5720                 :             :                         else
    5721   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    5722                 :             :                                                 (errcode(ERRCODE_SYNTAX_ERROR),
    5723                 :             :                                                  errmsg("invalid Unicode escape"),
    5724                 :             :                                                  errhint("Unicode escapes must be \\XXXX, \\+XXXXXX, \\uXXXX, or \\UXXXXXXXX.")));
    5725                 :           0 :                 }
    5726                 :             :                 else
    5727                 :             :                 {
    5728         [ #  # ]:           0 :                         if (pair_first)
    5729                 :           0 :                                 goto invalid_pair;
    5730                 :             : 
    5731                 :           0 :                         appendStringInfoChar(&str, *instr++);
    5732                 :           0 :                         len--;
    5733                 :             :                 }
    5734                 :             :         }
    5735                 :             : 
    5736                 :             :         /* unfinished surrogate pair? */
    5737         [ #  # ]:           0 :         if (pair_first)
    5738                 :           0 :                 goto invalid_pair;
    5739                 :             : 
    5740                 :           0 :         result = cstring_to_text_with_len(str.data, str.len);
    5741                 :           0 :         pfree(str.data);
    5742                 :             : 
    5743                 :           0 :         PG_RETURN_TEXT_P(result);
    5744                 :             : 
    5745                 :             : invalid_pair:
    5746   [ #  #  #  # ]:           0 :         ereport(ERROR,
    5747                 :             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    5748                 :             :                          errmsg("invalid Unicode surrogate pair")));
    5749                 :           0 :         PG_RETURN_NULL();                       /* keep compiler quiet */
    5750         [ #  # ]:           0 : }
        

Generated by: LCOV version 2.3.2-1