LCOV - code coverage report
Current view: top level - contrib/citext - citext.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 100.0 % 156 156
Test Date: 2026-03-01 00:15:48 Functions: 100.0 % 35 35
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * contrib/citext/citext.c
       3              :  */
       4              : #include "postgres.h"
       5              : 
       6              : #include "catalog/pg_collation.h"
       7              : #include "common/hashfn.h"
       8              : #include "fmgr.h"
       9              : #include "utils/formatting.h"
      10              : #include "utils/varlena.h"
      11              : #include "varatt.h"
      12              : 
      13            3 : PG_MODULE_MAGIC_EXT(
      14              :                     .name = "citext",
      15              :                     .version = PG_VERSION
      16              : );
      17              : 
      18              : /*
      19              :  *      ====================
      20              :  *      FORWARD DECLARATIONS
      21              :  *      ====================
      22              :  */
      23              : 
      24              : static int32 citextcmp(text *left, text *right, Oid collid);
      25              : static int32 internal_citext_pattern_cmp(text *left, text *right, Oid collid);
      26              : 
      27              : /*
      28              :  *      =================
      29              :  *      UTILITY FUNCTIONS
      30              :  *      =================
      31              :  */
      32              : 
      33              : /*
      34              :  * citextcmp()
      35              :  * Internal comparison function for citext strings.
      36              :  * Returns int32 negative, zero, or positive.
      37              :  */
      38              : static int32
      39          150 : citextcmp(text *left, text *right, Oid collid)
      40              : {
      41              :     char       *lcstr,
      42              :                *rcstr;
      43              :     int32       result;
      44              : 
      45              :     /*
      46              :      * We must do our str_tolower calls with DEFAULT_COLLATION_OID, not the
      47              :      * input collation as you might expect.  This is so that the behavior of
      48              :      * citext's equality and hashing functions is not collation-dependent.  We
      49              :      * should change this once the core infrastructure is able to cope with
      50              :      * collation-dependent equality and hashing functions.
      51              :      */
      52              : 
      53          150 :     lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
      54          150 :     rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
      55              : 
      56          150 :     result = varstr_cmp(lcstr, strlen(lcstr),
      57          150 :                         rcstr, strlen(rcstr),
      58              :                         collid);
      59              : 
      60          150 :     pfree(lcstr);
      61          150 :     pfree(rcstr);
      62              : 
      63          150 :     return result;
      64              : }
      65              : 
      66              : /*
      67              :  * citext_pattern_cmp()
      68              :  * Internal character-by-character comparison function for citext strings.
      69              :  * Returns int32 negative, zero, or positive.
      70              :  */
      71              : static int32
      72           54 : internal_citext_pattern_cmp(text *left, text *right, Oid collid)
      73              : {
      74              :     char       *lcstr,
      75              :                *rcstr;
      76              :     int         llen,
      77              :                 rlen;
      78              :     int32       result;
      79              : 
      80           54 :     lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
      81           54 :     rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
      82              : 
      83           54 :     llen = strlen(lcstr);
      84           54 :     rlen = strlen(rcstr);
      85              : 
      86           54 :     result = memcmp(lcstr, rcstr, Min(llen, rlen));
      87           54 :     if (result == 0)
      88              :     {
      89           20 :         if (llen < rlen)
      90            1 :             result = -1;
      91           19 :         else if (llen > rlen)
      92            1 :             result = 1;
      93              :     }
      94              : 
      95           54 :     pfree(lcstr);
      96           54 :     pfree(rcstr);
      97              : 
      98           54 :     return result;
      99              : }
     100              : 
     101              : /*
     102              :  *      ==================
     103              :  *      INDEXING FUNCTIONS
     104              :  *      ==================
     105              :  */
     106              : 
     107            5 : PG_FUNCTION_INFO_V1(citext_cmp);
     108              : 
     109              : Datum
     110          131 : citext_cmp(PG_FUNCTION_ARGS)
     111              : {
     112          131 :     text       *left = PG_GETARG_TEXT_PP(0);
     113          131 :     text       *right = PG_GETARG_TEXT_PP(1);
     114              :     int32       result;
     115              : 
     116          131 :     result = citextcmp(left, right, PG_GET_COLLATION());
     117              : 
     118          131 :     PG_FREE_IF_COPY(left, 0);
     119          131 :     PG_FREE_IF_COPY(right, 1);
     120              : 
     121          131 :     PG_RETURN_INT32(result);
     122              : }
     123              : 
     124            4 : PG_FUNCTION_INFO_V1(citext_pattern_cmp);
     125              : 
     126              : Datum
     127           10 : citext_pattern_cmp(PG_FUNCTION_ARGS)
     128              : {
     129           10 :     text       *left = PG_GETARG_TEXT_PP(0);
     130           10 :     text       *right = PG_GETARG_TEXT_PP(1);
     131              :     int32       result;
     132              : 
     133           10 :     result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION());
     134              : 
     135           10 :     PG_FREE_IF_COPY(left, 0);
     136           10 :     PG_FREE_IF_COPY(right, 1);
     137              : 
     138           10 :     PG_RETURN_INT32(result);
     139              : }
     140              : 
     141            3 : PG_FUNCTION_INFO_V1(citext_hash);
     142              : 
     143              : Datum
     144           10 : citext_hash(PG_FUNCTION_ARGS)
     145              : {
     146           10 :     text       *txt = PG_GETARG_TEXT_PP(0);
     147              :     char       *str;
     148              :     Datum       result;
     149              : 
     150           10 :     str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
     151           10 :     result = hash_any((unsigned char *) str, strlen(str));
     152           10 :     pfree(str);
     153              : 
     154              :     /* Avoid leaking memory for toasted inputs */
     155           10 :     PG_FREE_IF_COPY(txt, 0);
     156              : 
     157           10 :     PG_RETURN_DATUM(result);
     158              : }
     159              : 
     160            3 : PG_FUNCTION_INFO_V1(citext_hash_extended);
     161              : 
     162              : Datum
     163           10 : citext_hash_extended(PG_FUNCTION_ARGS)
     164              : {
     165           10 :     text       *txt = PG_GETARG_TEXT_PP(0);
     166           10 :     uint64      seed = PG_GETARG_INT64(1);
     167              :     char       *str;
     168              :     Datum       result;
     169              : 
     170           10 :     str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
     171           10 :     result = hash_any_extended((unsigned char *) str, strlen(str), seed);
     172           10 :     pfree(str);
     173              : 
     174              :     /* Avoid leaking memory for toasted inputs */
     175           10 :     PG_FREE_IF_COPY(txt, 0);
     176              : 
     177           10 :     PG_RETURN_DATUM(result);
     178              : }
     179              : 
     180              : /*
     181              :  *      ==================
     182              :  *      OPERATOR FUNCTIONS
     183              :  *      ==================
     184              :  */
     185              : 
     186            5 : PG_FUNCTION_INFO_V1(citext_eq);
     187              : 
     188              : Datum
     189           85 : citext_eq(PG_FUNCTION_ARGS)
     190              : {
     191           85 :     text       *left = PG_GETARG_TEXT_PP(0);
     192           85 :     text       *right = PG_GETARG_TEXT_PP(1);
     193              :     char       *lcstr,
     194              :                *rcstr;
     195              :     bool        result;
     196              : 
     197              :     /* We can't compare lengths in advance of downcasing ... */
     198              : 
     199           85 :     lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
     200           85 :     rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
     201              : 
     202              :     /*
     203              :      * Since we only care about equality or not-equality, we can avoid all the
     204              :      * expense of strcoll() here, and just do bitwise comparison.
     205              :      */
     206           85 :     result = (strcmp(lcstr, rcstr) == 0);
     207              : 
     208           85 :     pfree(lcstr);
     209           85 :     pfree(rcstr);
     210           85 :     PG_FREE_IF_COPY(left, 0);
     211           85 :     PG_FREE_IF_COPY(right, 1);
     212              : 
     213           85 :     PG_RETURN_BOOL(result);
     214              : }
     215              : 
     216            4 : PG_FUNCTION_INFO_V1(citext_ne);
     217              : 
     218              : Datum
     219           22 : citext_ne(PG_FUNCTION_ARGS)
     220              : {
     221           22 :     text       *left = PG_GETARG_TEXT_PP(0);
     222           22 :     text       *right = PG_GETARG_TEXT_PP(1);
     223              :     char       *lcstr,
     224              :                *rcstr;
     225              :     bool        result;
     226              : 
     227              :     /* We can't compare lengths in advance of downcasing ... */
     228              : 
     229           22 :     lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
     230           22 :     rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
     231              : 
     232              :     /*
     233              :      * Since we only care about equality or not-equality, we can avoid all the
     234              :      * expense of strcoll() here, and just do bitwise comparison.
     235              :      */
     236           22 :     result = (strcmp(lcstr, rcstr) != 0);
     237              : 
     238           22 :     pfree(lcstr);
     239           22 :     pfree(rcstr);
     240           22 :     PG_FREE_IF_COPY(left, 0);
     241           22 :     PG_FREE_IF_COPY(right, 1);
     242              : 
     243           22 :     PG_RETURN_BOOL(result);
     244              : }
     245              : 
     246            3 : PG_FUNCTION_INFO_V1(citext_lt);
     247              : 
     248              : Datum
     249            1 : citext_lt(PG_FUNCTION_ARGS)
     250              : {
     251            1 :     text       *left = PG_GETARG_TEXT_PP(0);
     252            1 :     text       *right = PG_GETARG_TEXT_PP(1);
     253              :     bool        result;
     254              : 
     255            1 :     result = citextcmp(left, right, PG_GET_COLLATION()) < 0;
     256              : 
     257            1 :     PG_FREE_IF_COPY(left, 0);
     258            1 :     PG_FREE_IF_COPY(right, 1);
     259              : 
     260            1 :     PG_RETURN_BOOL(result);
     261              : }
     262              : 
     263            3 : PG_FUNCTION_INFO_V1(citext_le);
     264              : 
     265              : Datum
     266            1 : citext_le(PG_FUNCTION_ARGS)
     267              : {
     268            1 :     text       *left = PG_GETARG_TEXT_PP(0);
     269            1 :     text       *right = PG_GETARG_TEXT_PP(1);
     270              :     bool        result;
     271              : 
     272            1 :     result = citextcmp(left, right, PG_GET_COLLATION()) <= 0;
     273              : 
     274            1 :     PG_FREE_IF_COPY(left, 0);
     275            1 :     PG_FREE_IF_COPY(right, 1);
     276              : 
     277            1 :     PG_RETURN_BOOL(result);
     278              : }
     279              : 
     280            3 : PG_FUNCTION_INFO_V1(citext_gt);
     281              : 
     282              : Datum
     283            3 : citext_gt(PG_FUNCTION_ARGS)
     284              : {
     285            3 :     text       *left = PG_GETARG_TEXT_PP(0);
     286            3 :     text       *right = PG_GETARG_TEXT_PP(1);
     287              :     bool        result;
     288              : 
     289            3 :     result = citextcmp(left, right, PG_GET_COLLATION()) > 0;
     290              : 
     291            3 :     PG_FREE_IF_COPY(left, 0);
     292            3 :     PG_FREE_IF_COPY(right, 1);
     293              : 
     294            3 :     PG_RETURN_BOOL(result);
     295              : }
     296              : 
     297            3 : PG_FUNCTION_INFO_V1(citext_ge);
     298              : 
     299              : Datum
     300            1 : citext_ge(PG_FUNCTION_ARGS)
     301              : {
     302            1 :     text       *left = PG_GETARG_TEXT_PP(0);
     303            1 :     text       *right = PG_GETARG_TEXT_PP(1);
     304              :     bool        result;
     305              : 
     306            1 :     result = citextcmp(left, right, PG_GET_COLLATION()) >= 0;
     307              : 
     308            1 :     PG_FREE_IF_COPY(left, 0);
     309            1 :     PG_FREE_IF_COPY(right, 1);
     310              : 
     311            1 :     PG_RETURN_BOOL(result);
     312              : }
     313              : 
     314            4 : PG_FUNCTION_INFO_V1(citext_pattern_lt);
     315              : 
     316              : Datum
     317            9 : citext_pattern_lt(PG_FUNCTION_ARGS)
     318              : {
     319            9 :     text       *left = PG_GETARG_TEXT_PP(0);
     320            9 :     text       *right = PG_GETARG_TEXT_PP(1);
     321              :     bool        result;
     322              : 
     323            9 :     result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) < 0;
     324              : 
     325            9 :     PG_FREE_IF_COPY(left, 0);
     326            9 :     PG_FREE_IF_COPY(right, 1);
     327              : 
     328            9 :     PG_RETURN_BOOL(result);
     329              : }
     330              : 
     331            4 : PG_FUNCTION_INFO_V1(citext_pattern_le);
     332              : 
     333              : Datum
     334           13 : citext_pattern_le(PG_FUNCTION_ARGS)
     335              : {
     336           13 :     text       *left = PG_GETARG_TEXT_PP(0);
     337           13 :     text       *right = PG_GETARG_TEXT_PP(1);
     338              :     bool        result;
     339              : 
     340           13 :     result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) <= 0;
     341              : 
     342           13 :     PG_FREE_IF_COPY(left, 0);
     343           13 :     PG_FREE_IF_COPY(right, 1);
     344              : 
     345           13 :     PG_RETURN_BOOL(result);
     346              : }
     347              : 
     348            4 : PG_FUNCTION_INFO_V1(citext_pattern_gt);
     349              : 
     350              : Datum
     351           10 : citext_pattern_gt(PG_FUNCTION_ARGS)
     352              : {
     353           10 :     text       *left = PG_GETARG_TEXT_PP(0);
     354           10 :     text       *right = PG_GETARG_TEXT_PP(1);
     355              :     bool        result;
     356              : 
     357           10 :     result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) > 0;
     358              : 
     359           10 :     PG_FREE_IF_COPY(left, 0);
     360           10 :     PG_FREE_IF_COPY(right, 1);
     361              : 
     362           10 :     PG_RETURN_BOOL(result);
     363              : }
     364              : 
     365            4 : PG_FUNCTION_INFO_V1(citext_pattern_ge);
     366              : 
     367              : Datum
     368           12 : citext_pattern_ge(PG_FUNCTION_ARGS)
     369              : {
     370           12 :     text       *left = PG_GETARG_TEXT_PP(0);
     371           12 :     text       *right = PG_GETARG_TEXT_PP(1);
     372              :     bool        result;
     373              : 
     374           12 :     result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) >= 0;
     375              : 
     376           12 :     PG_FREE_IF_COPY(left, 0);
     377           12 :     PG_FREE_IF_COPY(right, 1);
     378              : 
     379           12 :     PG_RETURN_BOOL(result);
     380              : }
     381              : 
     382              : /*
     383              :  *      ===================
     384              :  *      AGGREGATE FUNCTIONS
     385              :  *      ===================
     386              :  */
     387              : 
     388            3 : PG_FUNCTION_INFO_V1(citext_smaller);
     389              : 
     390              : Datum
     391            7 : citext_smaller(PG_FUNCTION_ARGS)
     392              : {
     393            7 :     text       *left = PG_GETARG_TEXT_PP(0);
     394            7 :     text       *right = PG_GETARG_TEXT_PP(1);
     395              :     text       *result;
     396              : 
     397            7 :     result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right;
     398            7 :     PG_RETURN_TEXT_P(result);
     399              : }
     400              : 
     401            3 : PG_FUNCTION_INFO_V1(citext_larger);
     402              : 
     403              : Datum
     404            6 : citext_larger(PG_FUNCTION_ARGS)
     405              : {
     406            6 :     text       *left = PG_GETARG_TEXT_PP(0);
     407            6 :     text       *right = PG_GETARG_TEXT_PP(1);
     408              :     text       *result;
     409              : 
     410            6 :     result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right;
     411            6 :     PG_RETURN_TEXT_P(result);
     412              : }
        

Generated by: LCOV version 2.0-1