LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - varlena.c (source / functions) Coverage Total Hit UNC LBC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 90.3 % 1912 1727 2 4 179 8 25 1694 1 22 398
Current Date: 2025-09-06 07:49:51 +0900 Functions: 92.3 % 143 132 11 1 7 124 41
Baseline: lcov-20250906-005545-baseline Branches: 71.5 % 1185 847 7 8 323 5 2 9 831 471 245
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 100.0 % 4 4 4
(30,360] days: 95.8 % 71 68 2 1 21 47
(360..) days: 90.1 % 1837 1655 4 178 8 1647 1
Function coverage date bins:
(30,360] days: 100.0 % 4 4 3 1
(360..) days: 92.1 % 139 128 11 1 4 123
Branch coverage date bins:
(30,360] days: 58.0 % 88 51 7 10 1 9 41 14 6
(360..) days: 43.9 % 1813 796 8 313 4 2 790 457 239

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

Generated by: LCOV version 2.4-beta