LCOV - code coverage report
Current view: top level - src/test/modules/test_saslprep - test_saslprep.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 37.1 % 105 39
Test Date: 2026-05-04 16:16:34 Functions: 83.3 % 6 5
Legend: Lines:     hit not hit

            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