LCOV - code coverage report
Current view: top level - src/test/modules/test_saslprep - test_saslprep.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 37.1 % 105 39
Test Date: 2026-07-03 19:57:34 Functions: 83.3 % 6 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 19.4 % 31 6

             Branch data     Line data    Source code
       1                 :             : /*--------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * test_saslprep.c
       4                 :             :  *      Test harness for the SASLprep implementation.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  *
       8                 :             :  * IDENTIFICATION
       9                 :             :  *      src/test/modules/test_saslprep/test_saslprep.c
      10                 :             :  *
      11                 :             :  * -------------------------------------------------------------------------
      12                 :             :  */
      13                 :             : 
      14                 :             : #include "postgres.h"
      15                 :             : 
      16                 :             : #include "access/htup_details.h"
      17                 :             : #include "common/saslprep.h"
      18                 :             : #include "fmgr.h"
      19                 :             : #include "funcapi.h"
      20                 :             : #include "mb/pg_wchar.h"
      21                 :             : #include "miscadmin.h"
      22                 :             : #include "utils/builtins.h"
      23                 :             : 
      24                 :           1 : PG_MODULE_MAGIC;
      25                 :             : 
      26                 :             : static const char *
      27                 :         129 : saslprep_status_to_text(pg_saslprep_rc rc)
      28                 :             : {
      29                 :         129 :     const char *status = "???";
      30                 :             : 
      31   [ -  +  +  +  :         129 :     switch (rc)
                      - ]
      32                 :             :     {
      33                 :           0 :         case SASLPREP_OOM:
      34                 :           0 :             status = "OOM";
      35                 :           0 :             break;
      36                 :          95 :         case SASLPREP_SUCCESS:
      37                 :          95 :             status = "SUCCESS";
      38                 :          95 :             break;
      39                 :           1 :         case SASLPREP_INVALID_UTF8:
      40                 :           1 :             status = "INVALID_UTF8";
      41                 :           1 :             break;
      42                 :          33 :         case SASLPREP_PROHIBITED:
      43                 :          33 :             status = "PROHIBITED";
      44                 :          33 :             break;
      45                 :             :     }
      46                 :             : 
      47                 :         129 :     return status;
      48                 :             : }
      49                 :             : 
      50                 :             : /*
      51                 :             :  * Simple function to test SASLprep with arbitrary bytes as input.
      52                 :             :  *
      53                 :             :  * This takes a bytea in input, returning in output the generating data as
      54                 :             :  * bytea with the status returned by pg_saslprep().
      55                 :             :  */
      56                 :           2 : PG_FUNCTION_INFO_V1(test_saslprep);
      57                 :             : Datum
      58                 :         129 : test_saslprep(PG_FUNCTION_ARGS)
      59                 :             : {
      60                 :         129 :     bytea      *string = PG_GETARG_BYTEA_PP(0);
      61                 :             :     char       *src;
      62                 :             :     Size        src_len;
      63                 :             :     char       *input_data;
      64                 :             :     char       *result;
      65                 :             :     Size        result_len;
      66                 :         129 :     bytea      *result_bytea = NULL;
      67                 :         129 :     const char *status = NULL;
      68                 :             :     Datum      *values;
      69                 :             :     bool       *nulls;
      70                 :             :     TupleDesc   tupdesc;
      71                 :             :     pg_saslprep_rc rc;
      72                 :             : 
      73                 :             :     /* determine result type */
      74         [ -  + ]:         129 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
      75         [ #  # ]:           0 :         elog(ERROR, "return type must be a row type");
      76                 :             : 
      77                 :         129 :     values = palloc0_array(Datum, tupdesc->natts);
      78                 :         129 :     nulls = palloc0_array(bool, tupdesc->natts);
      79                 :             : 
      80                 :         129 :     src_len = VARSIZE_ANY_EXHDR(string);
      81                 :         129 :     src = VARDATA_ANY(string);
      82                 :             : 
      83                 :             :     /*
      84                 :             :      * Copy the input given, to make SASLprep() act on a sanitized string.
      85                 :             :      */
      86                 :         129 :     input_data = palloc0(src_len + 1);
      87                 :         129 :     memcpy(input_data, src, src_len);
      88                 :         129 :     input_data[src_len] = '\0';
      89                 :             : 
      90                 :         129 :     rc = pg_saslprep(input_data, &result);
      91                 :         129 :     status = saslprep_status_to_text(rc);
      92                 :             : 
      93         [ +  + ]:         129 :     if (result)
      94                 :             :     {
      95                 :          95 :         result_len = strlen(result);
      96                 :          95 :         result_bytea = palloc(result_len + VARHDRSZ);
      97                 :          95 :         SET_VARSIZE(result_bytea, result_len + VARHDRSZ);
      98                 :          95 :         memcpy(VARDATA(result_bytea), result, result_len);
      99                 :          95 :         values[0] = PointerGetDatum(result_bytea);
     100                 :             :     }
     101                 :             :     else
     102                 :          34 :         nulls[0] = true;
     103                 :             : 
     104                 :         129 :     values[1] = CStringGetTextDatum(status);
     105                 :             : 
     106                 :         129 :     PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
     107                 :             : }
     108                 :             : 
     109                 :             : /* Context structure for set-returning function with ranges */
     110                 :             : typedef struct
     111                 :             : {
     112                 :             :     int         current_range;
     113                 :             :     char32_t    current_codepoint;
     114                 :             : } pg_saslprep_test_context;
     115                 :             : 
     116                 :             : /*
     117                 :             :  * UTF-8 code point ranges.
     118                 :             :  */
     119                 :             : typedef struct
     120                 :             : {
     121                 :             :     char32_t    start_codepoint;
     122                 :             :     char32_t    end_codepoint;
     123                 :             : } pg_utf8_codepoint_range;
     124                 :             : 
     125                 :             : static const pg_utf8_codepoint_range pg_utf8_test_ranges[] = {
     126                 :             :     /* 1, 2, 3 bytes */
     127                 :             :     {0x0000, 0xD7FF},           /* Basic Multilingual Plane, before surrogates */
     128                 :             :     {0xE000, 0xFFFF},           /* Basic Multilingual Plane, after surrogates */
     129                 :             :     /* 4 bytes */
     130                 :             :     {0x10000, 0x1FFFF},         /* Supplementary Multilingual Plane */
     131                 :             :     {0x20000, 0x2FFFF},         /* Supplementary Ideographic Plane */
     132                 :             :     {0x30000, 0x3FFFF},         /* Tertiary Ideographic Plane */
     133                 :             :     {0x40000, 0xDFFFF},         /* Unassigned planes */
     134                 :             :     {0xE0000, 0xEFFFF},         /* Supplementary Special-purpose Plane */
     135                 :             :     {0xF0000, 0xFFFFF},         /* Private Use Area A */
     136                 :             :     {0x100000, 0x10FFFF},       /* Private Use Area B */
     137                 :             : };
     138                 :             : 
     139                 :             : #define PG_UTF8_TEST_RANGES_LEN \
     140                 :             :     (sizeof(pg_utf8_test_ranges) / sizeof(pg_utf8_test_ranges[0]))
     141                 :             : 
     142                 :             : 
     143                 :             : /*
     144                 :             :  * test_saslprep_ranges
     145                 :             :  *
     146                 :             :  * Test SASLprep across various UTF-8 ranges.
     147                 :             :  */
     148                 :           1 : PG_FUNCTION_INFO_V1(test_saslprep_ranges);
     149                 :             : Datum
     150                 :           0 : test_saslprep_ranges(PG_FUNCTION_ARGS)
     151                 :             : {
     152                 :             :     FuncCallContext *funcctx;
     153                 :             :     pg_saslprep_test_context *ctx;
     154                 :             :     HeapTuple   tuple;
     155                 :             :     Datum       result;
     156                 :             : 
     157                 :             :     /* First call setup */
     158         [ #  # ]:           0 :     if (SRF_IS_FIRSTCALL())
     159                 :             :     {
     160                 :             :         MemoryContext oldcontext;
     161                 :             :         TupleDesc   tupdesc;
     162                 :             : 
     163                 :           0 :         funcctx = SRF_FIRSTCALL_INIT();
     164                 :           0 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     165                 :             : 
     166         [ #  # ]:           0 :         if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     167         [ #  # ]:           0 :             elog(ERROR, "return type must be a row type");
     168                 :           0 :         funcctx->tuple_desc = tupdesc;
     169                 :             : 
     170                 :             :         /* Allocate context with range setup */
     171                 :           0 :         ctx = (pg_saslprep_test_context *) palloc(sizeof(pg_saslprep_test_context));
     172                 :           0 :         ctx->current_range = 0;
     173                 :           0 :         ctx->current_codepoint = pg_utf8_test_ranges[0].start_codepoint;
     174                 :           0 :         funcctx->user_fctx = ctx;
     175                 :             : 
     176                 :           0 :         MemoryContextSwitchTo(oldcontext);
     177                 :             :     }
     178                 :             : 
     179                 :           0 :     funcctx = SRF_PERCALL_SETUP();
     180                 :           0 :     ctx = (pg_saslprep_test_context *) funcctx->user_fctx;
     181                 :             : 
     182         [ #  # ]:           0 :     while (ctx->current_range < PG_UTF8_TEST_RANGES_LEN)
     183                 :             :     {
     184                 :           0 :         char32_t    codepoint = ctx->current_codepoint;
     185                 :             :         unsigned char utf8_buf[5];
     186                 :             :         char        input_str[6];
     187                 :           0 :         char       *output = NULL;
     188                 :             :         pg_saslprep_rc rc;
     189                 :             :         int         utf8_len;
     190                 :             :         const char *status;
     191                 :             :         bytea      *input_bytea;
     192                 :             :         bytea      *output_bytea;
     193                 :             :         char        codepoint_str[16];
     194                 :           0 :         Datum       values[4] = {0};
     195                 :           0 :         bool        nulls[4] = {0};
     196                 :           0 :         const pg_utf8_codepoint_range *range =
     197                 :           0 :             &pg_utf8_test_ranges[ctx->current_range];
     198                 :             : 
     199         [ #  # ]:           0 :         CHECK_FOR_INTERRUPTS();
     200                 :             : 
     201                 :             :         /* Switch to next range if finished with the previous one */
     202         [ #  # ]:           0 :         if (ctx->current_codepoint > range->end_codepoint)
     203                 :             :         {
     204                 :           0 :             ctx->current_range++;
     205         [ #  # ]:           0 :             if (ctx->current_range < PG_UTF8_TEST_RANGES_LEN)
     206                 :           0 :                 ctx->current_codepoint =
     207                 :           0 :                     pg_utf8_test_ranges[ctx->current_range].start_codepoint;
     208                 :           0 :             continue;
     209                 :             :         }
     210                 :             : 
     211                 :           0 :         codepoint = ctx->current_codepoint;
     212                 :             : 
     213                 :             :         /* Convert code point to UTF-8 */
     214                 :           0 :         utf8_len = unicode_utf8len(codepoint);
     215         [ #  # ]:           0 :         if (utf8_len == 0)
     216                 :             :         {
     217                 :           0 :             ctx->current_codepoint++;
     218                 :           0 :             continue;
     219                 :             :         }
     220                 :           0 :         unicode_to_utf8(codepoint, utf8_buf);
     221                 :             : 
     222                 :             :         /* Create null-terminated string */
     223                 :           0 :         memcpy(input_str, utf8_buf, utf8_len);
     224                 :           0 :         input_str[utf8_len] = '\0';
     225                 :             : 
     226                 :             :         /* Test with pg_saslprep */
     227                 :           0 :         rc = pg_saslprep(input_str, &output);
     228                 :             : 
     229                 :             :         /* Prepare output values */
     230                 :           0 :         memset(nulls, false, sizeof(nulls));
     231                 :             : 
     232                 :             :         /* codepoint as text U+XXXX format */
     233         [ #  # ]:           0 :         if (codepoint <= 0xFFFF)
     234                 :           0 :             snprintf(codepoint_str, sizeof(codepoint_str), "U+%04X", codepoint);
     235                 :             :         else
     236                 :           0 :             snprintf(codepoint_str, sizeof(codepoint_str), "U+%06X", codepoint);
     237                 :           0 :         values[0] = CStringGetTextDatum(codepoint_str);
     238                 :             : 
     239                 :             :         /* status */
     240                 :           0 :         status = saslprep_status_to_text(rc);
     241                 :           0 :         values[1] = CStringGetTextDatum(status);
     242                 :             : 
     243                 :             :         /* input_bytes */
     244                 :           0 :         input_bytea = (bytea *) palloc(VARHDRSZ + utf8_len);
     245                 :           0 :         SET_VARSIZE(input_bytea, VARHDRSZ + utf8_len);
     246                 :           0 :         memcpy(VARDATA(input_bytea), utf8_buf, utf8_len);
     247                 :           0 :         values[2] = PointerGetDatum(input_bytea);
     248                 :             : 
     249                 :             :         /* output_bytes */
     250         [ #  # ]:           0 :         if (output != NULL)
     251                 :             :         {
     252                 :           0 :             int         output_len = strlen(output);
     253                 :             : 
     254                 :           0 :             output_bytea = (bytea *) palloc(VARHDRSZ + output_len);
     255                 :           0 :             SET_VARSIZE(output_bytea, VARHDRSZ + output_len);
     256                 :           0 :             memcpy(VARDATA(output_bytea), output, output_len);
     257                 :           0 :             values[3] = PointerGetDatum(output_bytea);
     258                 :           0 :             pfree(output);
     259                 :             :         }
     260                 :             :         else
     261                 :             :         {
     262                 :           0 :             nulls[3] = true;
     263                 :           0 :             values[3] = (Datum) 0;
     264                 :             :         }
     265                 :             : 
     266                 :             :         /* Build and return tuple */
     267                 :           0 :         tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
     268                 :           0 :         result = HeapTupleGetDatum(tuple);
     269                 :             : 
     270                 :             :         /* Move to next code point */
     271                 :           0 :         ctx->current_codepoint++;
     272                 :             : 
     273                 :           0 :         SRF_RETURN_NEXT(funcctx, result);
     274                 :             :     }
     275                 :             : 
     276                 :             :     /* All done */
     277                 :           0 :     SRF_RETURN_DONE(funcctx);
     278                 :             : }
        

Generated by: LCOV version 2.0-1