LCOV - code coverage report
Current view: top level - contrib/citext - citext.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 156 156 100.0 %
Date: 2025-04-01 16:15:31 Functions: 35 35 100.0 %
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           6 : 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         300 : 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         300 :     lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
      54         300 :     rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
      55             : 
      56         300 :     result = varstr_cmp(lcstr, strlen(lcstr),
      57         300 :                         rcstr, strlen(rcstr),
      58             :                         collid);
      59             : 
      60         300 :     pfree(lcstr);
      61         300 :     pfree(rcstr);
      62             : 
      63         300 :     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         108 : 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         108 :     lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
      81         108 :     rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
      82             : 
      83         108 :     llen = strlen(lcstr);
      84         108 :     rlen = strlen(rcstr);
      85             : 
      86         108 :     result = memcmp(lcstr, rcstr, Min(llen, rlen));
      87         108 :     if (result == 0)
      88             :     {
      89          40 :         if (llen < rlen)
      90           2 :             result = -1;
      91          38 :         else if (llen > rlen)
      92           2 :             result = 1;
      93             :     }
      94             : 
      95         108 :     pfree(lcstr);
      96         108 :     pfree(rcstr);
      97             : 
      98         108 :     return result;
      99             : }
     100             : 
     101             : /*
     102             :  *      ==================
     103             :  *      INDEXING FUNCTIONS
     104             :  *      ==================
     105             :  */
     106             : 
     107          10 : PG_FUNCTION_INFO_V1(citext_cmp);
     108             : 
     109             : Datum
     110         262 : citext_cmp(PG_FUNCTION_ARGS)
     111             : {
     112         262 :     text       *left = PG_GETARG_TEXT_PP(0);
     113         262 :     text       *right = PG_GETARG_TEXT_PP(1);
     114             :     int32       result;
     115             : 
     116         262 :     result = citextcmp(left, right, PG_GET_COLLATION());
     117             : 
     118         262 :     PG_FREE_IF_COPY(left, 0);
     119         262 :     PG_FREE_IF_COPY(right, 1);
     120             : 
     121         262 :     PG_RETURN_INT32(result);
     122             : }
     123             : 
     124           8 : PG_FUNCTION_INFO_V1(citext_pattern_cmp);
     125             : 
     126             : Datum
     127          20 : citext_pattern_cmp(PG_FUNCTION_ARGS)
     128             : {
     129          20 :     text       *left = PG_GETARG_TEXT_PP(0);
     130          20 :     text       *right = PG_GETARG_TEXT_PP(1);
     131             :     int32       result;
     132             : 
     133          20 :     result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION());
     134             : 
     135          20 :     PG_FREE_IF_COPY(left, 0);
     136          20 :     PG_FREE_IF_COPY(right, 1);
     137             : 
     138          20 :     PG_RETURN_INT32(result);
     139             : }
     140             : 
     141           6 : PG_FUNCTION_INFO_V1(citext_hash);
     142             : 
     143             : Datum
     144          20 : citext_hash(PG_FUNCTION_ARGS)
     145             : {
     146          20 :     text       *txt = PG_GETARG_TEXT_PP(0);
     147             :     char       *str;
     148             :     Datum       result;
     149             : 
     150          20 :     str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
     151          20 :     result = hash_any((unsigned char *) str, strlen(str));
     152          20 :     pfree(str);
     153             : 
     154             :     /* Avoid leaking memory for toasted inputs */
     155          20 :     PG_FREE_IF_COPY(txt, 0);
     156             : 
     157          20 :     PG_RETURN_DATUM(result);
     158             : }
     159             : 
     160           6 : PG_FUNCTION_INFO_V1(citext_hash_extended);
     161             : 
     162             : Datum
     163          20 : citext_hash_extended(PG_FUNCTION_ARGS)
     164             : {
     165          20 :     text       *txt = PG_GETARG_TEXT_PP(0);
     166          20 :     uint64      seed = PG_GETARG_INT64(1);
     167             :     char       *str;
     168             :     Datum       result;
     169             : 
     170          20 :     str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
     171          20 :     result = hash_any_extended((unsigned char *) str, strlen(str), seed);
     172          20 :     pfree(str);
     173             : 
     174             :     /* Avoid leaking memory for toasted inputs */
     175          20 :     PG_FREE_IF_COPY(txt, 0);
     176             : 
     177          20 :     PG_RETURN_DATUM(result);
     178             : }
     179             : 
     180             : /*
     181             :  *      ==================
     182             :  *      OPERATOR FUNCTIONS
     183             :  *      ==================
     184             :  */
     185             : 
     186          10 : PG_FUNCTION_INFO_V1(citext_eq);
     187             : 
     188             : Datum
     189         170 : citext_eq(PG_FUNCTION_ARGS)
     190             : {
     191         170 :     text       *left = PG_GETARG_TEXT_PP(0);
     192         170 :     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         170 :     lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
     200         170 :     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         170 :     result = (strcmp(lcstr, rcstr) == 0);
     207             : 
     208         170 :     pfree(lcstr);
     209         170 :     pfree(rcstr);
     210         170 :     PG_FREE_IF_COPY(left, 0);
     211         170 :     PG_FREE_IF_COPY(right, 1);
     212             : 
     213         170 :     PG_RETURN_BOOL(result);
     214             : }
     215             : 
     216           8 : PG_FUNCTION_INFO_V1(citext_ne);
     217             : 
     218             : Datum
     219          44 : citext_ne(PG_FUNCTION_ARGS)
     220             : {
     221          44 :     text       *left = PG_GETARG_TEXT_PP(0);
     222          44 :     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          44 :     lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
     230          44 :     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          44 :     result = (strcmp(lcstr, rcstr) != 0);
     237             : 
     238          44 :     pfree(lcstr);
     239          44 :     pfree(rcstr);
     240          44 :     PG_FREE_IF_COPY(left, 0);
     241          44 :     PG_FREE_IF_COPY(right, 1);
     242             : 
     243          44 :     PG_RETURN_BOOL(result);
     244             : }
     245             : 
     246           6 : PG_FUNCTION_INFO_V1(citext_lt);
     247             : 
     248             : Datum
     249           2 : citext_lt(PG_FUNCTION_ARGS)
     250             : {
     251           2 :     text       *left = PG_GETARG_TEXT_PP(0);
     252           2 :     text       *right = PG_GETARG_TEXT_PP(1);
     253             :     bool        result;
     254             : 
     255           2 :     result = citextcmp(left, right, PG_GET_COLLATION()) < 0;
     256             : 
     257           2 :     PG_FREE_IF_COPY(left, 0);
     258           2 :     PG_FREE_IF_COPY(right, 1);
     259             : 
     260           2 :     PG_RETURN_BOOL(result);
     261             : }
     262             : 
     263           6 : PG_FUNCTION_INFO_V1(citext_le);
     264             : 
     265             : Datum
     266           2 : citext_le(PG_FUNCTION_ARGS)
     267             : {
     268           2 :     text       *left = PG_GETARG_TEXT_PP(0);
     269           2 :     text       *right = PG_GETARG_TEXT_PP(1);
     270             :     bool        result;
     271             : 
     272           2 :     result = citextcmp(left, right, PG_GET_COLLATION()) <= 0;
     273             : 
     274           2 :     PG_FREE_IF_COPY(left, 0);
     275           2 :     PG_FREE_IF_COPY(right, 1);
     276             : 
     277           2 :     PG_RETURN_BOOL(result);
     278             : }
     279             : 
     280           6 : PG_FUNCTION_INFO_V1(citext_gt);
     281             : 
     282             : Datum
     283           6 : citext_gt(PG_FUNCTION_ARGS)
     284             : {
     285           6 :     text       *left = PG_GETARG_TEXT_PP(0);
     286           6 :     text       *right = PG_GETARG_TEXT_PP(1);
     287             :     bool        result;
     288             : 
     289           6 :     result = citextcmp(left, right, PG_GET_COLLATION()) > 0;
     290             : 
     291           6 :     PG_FREE_IF_COPY(left, 0);
     292           6 :     PG_FREE_IF_COPY(right, 1);
     293             : 
     294           6 :     PG_RETURN_BOOL(result);
     295             : }
     296             : 
     297           6 : PG_FUNCTION_INFO_V1(citext_ge);
     298             : 
     299             : Datum
     300           2 : citext_ge(PG_FUNCTION_ARGS)
     301             : {
     302           2 :     text       *left = PG_GETARG_TEXT_PP(0);
     303           2 :     text       *right = PG_GETARG_TEXT_PP(1);
     304             :     bool        result;
     305             : 
     306           2 :     result = citextcmp(left, right, PG_GET_COLLATION()) >= 0;
     307             : 
     308           2 :     PG_FREE_IF_COPY(left, 0);
     309           2 :     PG_FREE_IF_COPY(right, 1);
     310             : 
     311           2 :     PG_RETURN_BOOL(result);
     312             : }
     313             : 
     314           8 : PG_FUNCTION_INFO_V1(citext_pattern_lt);
     315             : 
     316             : Datum
     317          18 : citext_pattern_lt(PG_FUNCTION_ARGS)
     318             : {
     319          18 :     text       *left = PG_GETARG_TEXT_PP(0);
     320          18 :     text       *right = PG_GETARG_TEXT_PP(1);
     321             :     bool        result;
     322             : 
     323          18 :     result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) < 0;
     324             : 
     325          18 :     PG_FREE_IF_COPY(left, 0);
     326          18 :     PG_FREE_IF_COPY(right, 1);
     327             : 
     328          18 :     PG_RETURN_BOOL(result);
     329             : }
     330             : 
     331           8 : PG_FUNCTION_INFO_V1(citext_pattern_le);
     332             : 
     333             : Datum
     334          26 : citext_pattern_le(PG_FUNCTION_ARGS)
     335             : {
     336          26 :     text       *left = PG_GETARG_TEXT_PP(0);
     337          26 :     text       *right = PG_GETARG_TEXT_PP(1);
     338             :     bool        result;
     339             : 
     340          26 :     result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) <= 0;
     341             : 
     342          26 :     PG_FREE_IF_COPY(left, 0);
     343          26 :     PG_FREE_IF_COPY(right, 1);
     344             : 
     345          26 :     PG_RETURN_BOOL(result);
     346             : }
     347             : 
     348           8 : PG_FUNCTION_INFO_V1(citext_pattern_gt);
     349             : 
     350             : Datum
     351          20 : citext_pattern_gt(PG_FUNCTION_ARGS)
     352             : {
     353          20 :     text       *left = PG_GETARG_TEXT_PP(0);
     354          20 :     text       *right = PG_GETARG_TEXT_PP(1);
     355             :     bool        result;
     356             : 
     357          20 :     result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) > 0;
     358             : 
     359          20 :     PG_FREE_IF_COPY(left, 0);
     360          20 :     PG_FREE_IF_COPY(right, 1);
     361             : 
     362          20 :     PG_RETURN_BOOL(result);
     363             : }
     364             : 
     365           8 : PG_FUNCTION_INFO_V1(citext_pattern_ge);
     366             : 
     367             : Datum
     368          24 : citext_pattern_ge(PG_FUNCTION_ARGS)
     369             : {
     370          24 :     text       *left = PG_GETARG_TEXT_PP(0);
     371          24 :     text       *right = PG_GETARG_TEXT_PP(1);
     372             :     bool        result;
     373             : 
     374          24 :     result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) >= 0;
     375             : 
     376          24 :     PG_FREE_IF_COPY(left, 0);
     377          24 :     PG_FREE_IF_COPY(right, 1);
     378             : 
     379          24 :     PG_RETURN_BOOL(result);
     380             : }
     381             : 
     382             : /*
     383             :  *      ===================
     384             :  *      AGGREGATE FUNCTIONS
     385             :  *      ===================
     386             :  */
     387             : 
     388           6 : PG_FUNCTION_INFO_V1(citext_smaller);
     389             : 
     390             : Datum
     391          14 : citext_smaller(PG_FUNCTION_ARGS)
     392             : {
     393          14 :     text       *left = PG_GETARG_TEXT_PP(0);
     394          14 :     text       *right = PG_GETARG_TEXT_PP(1);
     395             :     text       *result;
     396             : 
     397          14 :     result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right;
     398          14 :     PG_RETURN_TEXT_P(result);
     399             : }
     400             : 
     401           6 : PG_FUNCTION_INFO_V1(citext_larger);
     402             : 
     403             : Datum
     404          12 : citext_larger(PG_FUNCTION_ARGS)
     405             : {
     406          12 :     text       *left = PG_GETARG_TEXT_PP(0);
     407          12 :     text       *right = PG_GETARG_TEXT_PP(1);
     408             :     text       *result;
     409             : 
     410          12 :     result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right;
     411          12 :     PG_RETURN_TEXT_P(result);
     412             : }

Generated by: LCOV version 1.14