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: 36.5 % 104 38
Test Date: 2026-03-24 07:17:40 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 :     strlcpy(input_data, src, src_len + 1);
      88              : 
      89          129 :     rc = pg_saslprep(input_data, &result);
      90          129 :     status = saslprep_status_to_text(rc);
      91              : 
      92          129 :     if (result)
      93              :     {
      94           95 :         result_len = strlen(result);
      95           95 :         result_bytea = palloc(result_len + VARHDRSZ);
      96           95 :         SET_VARSIZE(result_bytea, result_len + VARHDRSZ);
      97           95 :         memcpy(VARDATA(result_bytea), result, result_len);
      98           95 :         values[0] = PointerGetDatum(result_bytea);
      99              :     }
     100              :     else
     101           34 :         nulls[0] = true;
     102              : 
     103          129 :     values[1] = CStringGetTextDatum(status);
     104              : 
     105          129 :     PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
     106              : }
     107              : 
     108              : /* Context structure for set-returning function with ranges */
     109              : typedef struct
     110              : {
     111              :     int         current_range;
     112              :     char32_t    current_codepoint;
     113              : } pg_saslprep_test_context;
     114              : 
     115              : /*
     116              :  * UTF-8 code point ranges.
     117              :  */
     118              : typedef struct
     119              : {
     120              :     char32_t    start_codepoint;
     121              :     char32_t    end_codepoint;
     122              : } pg_utf8_codepoint_range;
     123              : 
     124              : static const pg_utf8_codepoint_range pg_utf8_test_ranges[] = {
     125              :     /* 1, 2, 3 bytes */
     126              :     {0x0000, 0xD7FF},           /* Basic Multilingual Plane, before surrogates */
     127              :     {0xE000, 0xFFFF},           /* Basic Multilingual Plane, after surrogates */
     128              :     /* 4 bytes */
     129              :     {0x10000, 0x1FFFF},         /* Supplementary Multilingual Plane */
     130              :     {0x20000, 0x2FFFF},         /* Supplementary Ideographic Plane */
     131              :     {0x30000, 0x3FFFF},         /* Tertiary Ideographic Plane */
     132              :     {0x40000, 0xDFFFF},         /* Unassigned planes */
     133              :     {0xE0000, 0xEFFFF},         /* Supplementary Special-purpose Plane */
     134              :     {0xF0000, 0xFFFFF},         /* Private Use Area A */
     135              :     {0x100000, 0x10FFFF},       /* Private Use Area B */
     136              : };
     137              : 
     138              : #define PG_UTF8_TEST_RANGES_LEN \
     139              :     (sizeof(pg_utf8_test_ranges) / sizeof(pg_utf8_test_ranges[0]))
     140              : 
     141              : 
     142              : /*
     143              :  * test_saslprep_ranges
     144              :  *
     145              :  * Test SASLprep across various UTF-8 ranges.
     146              :  */
     147            1 : PG_FUNCTION_INFO_V1(test_saslprep_ranges);
     148              : Datum
     149            0 : test_saslprep_ranges(PG_FUNCTION_ARGS)
     150              : {
     151              :     FuncCallContext *funcctx;
     152              :     pg_saslprep_test_context *ctx;
     153              :     HeapTuple   tuple;
     154              :     Datum       result;
     155              : 
     156              :     /* First call setup */
     157            0 :     if (SRF_IS_FIRSTCALL())
     158              :     {
     159              :         MemoryContext oldcontext;
     160              :         TupleDesc   tupdesc;
     161              : 
     162            0 :         funcctx = SRF_FIRSTCALL_INIT();
     163            0 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     164              : 
     165            0 :         if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     166            0 :             elog(ERROR, "return type must be a row type");
     167            0 :         funcctx->tuple_desc = tupdesc;
     168              : 
     169              :         /* Allocate context with range setup */
     170            0 :         ctx = (pg_saslprep_test_context *) palloc(sizeof(pg_saslprep_test_context));
     171            0 :         ctx->current_range = 0;
     172            0 :         ctx->current_codepoint = pg_utf8_test_ranges[0].start_codepoint;
     173            0 :         funcctx->user_fctx = ctx;
     174              : 
     175            0 :         MemoryContextSwitchTo(oldcontext);
     176              :     }
     177              : 
     178            0 :     funcctx = SRF_PERCALL_SETUP();
     179            0 :     ctx = (pg_saslprep_test_context *) funcctx->user_fctx;
     180              : 
     181            0 :     while (ctx->current_range < PG_UTF8_TEST_RANGES_LEN)
     182              :     {
     183            0 :         char32_t    codepoint = ctx->current_codepoint;
     184              :         unsigned char utf8_buf[5];
     185              :         char        input_str[6];
     186            0 :         char       *output = NULL;
     187              :         pg_saslprep_rc rc;
     188              :         int         utf8_len;
     189              :         const char *status;
     190              :         bytea      *input_bytea;
     191              :         bytea      *output_bytea;
     192              :         char        codepoint_str[16];
     193            0 :         Datum       values[4] = {0};
     194            0 :         bool        nulls[4] = {0};
     195            0 :         const pg_utf8_codepoint_range *range =
     196            0 :             &pg_utf8_test_ranges[ctx->current_range];
     197              : 
     198            0 :         CHECK_FOR_INTERRUPTS();
     199              : 
     200              :         /* Switch to next range if finished with the previous one */
     201            0 :         if (ctx->current_codepoint > range->end_codepoint)
     202              :         {
     203            0 :             ctx->current_range++;
     204            0 :             if (ctx->current_range < PG_UTF8_TEST_RANGES_LEN)
     205            0 :                 ctx->current_codepoint =
     206            0 :                     pg_utf8_test_ranges[ctx->current_range].start_codepoint;
     207            0 :             continue;
     208              :         }
     209              : 
     210            0 :         codepoint = ctx->current_codepoint;
     211              : 
     212              :         /* Convert code point to UTF-8 */
     213            0 :         utf8_len = unicode_utf8len(codepoint);
     214            0 :         if (utf8_len == 0)
     215              :         {
     216            0 :             ctx->current_codepoint++;
     217            0 :             continue;
     218              :         }
     219            0 :         unicode_to_utf8(codepoint, utf8_buf);
     220              : 
     221              :         /* Create null-terminated string */
     222            0 :         memcpy(input_str, utf8_buf, utf8_len);
     223            0 :         input_str[utf8_len] = '\0';
     224              : 
     225              :         /* Test with pg_saslprep */
     226            0 :         rc = pg_saslprep(input_str, &output);
     227              : 
     228              :         /* Prepare output values */
     229            0 :         memset(nulls, false, sizeof(nulls));
     230              : 
     231              :         /* codepoint as text U+XXXX format */
     232            0 :         if (codepoint <= 0xFFFF)
     233            0 :             snprintf(codepoint_str, sizeof(codepoint_str), "U+%04X", codepoint);
     234              :         else
     235            0 :             snprintf(codepoint_str, sizeof(codepoint_str), "U+%06X", codepoint);
     236            0 :         values[0] = CStringGetTextDatum(codepoint_str);
     237              : 
     238              :         /* status */
     239            0 :         status = saslprep_status_to_text(rc);
     240            0 :         values[1] = CStringGetTextDatum(status);
     241              : 
     242              :         /* input_bytes */
     243            0 :         input_bytea = (bytea *) palloc(VARHDRSZ + utf8_len);
     244            0 :         SET_VARSIZE(input_bytea, VARHDRSZ + utf8_len);
     245            0 :         memcpy(VARDATA(input_bytea), utf8_buf, utf8_len);
     246            0 :         values[2] = PointerGetDatum(input_bytea);
     247              : 
     248              :         /* output_bytes */
     249            0 :         if (output != NULL)
     250              :         {
     251            0 :             int         output_len = strlen(output);
     252              : 
     253            0 :             output_bytea = (bytea *) palloc(VARHDRSZ + output_len);
     254            0 :             SET_VARSIZE(output_bytea, VARHDRSZ + output_len);
     255            0 :             memcpy(VARDATA(output_bytea), output, output_len);
     256            0 :             values[3] = PointerGetDatum(output_bytea);
     257            0 :             pfree(output);
     258              :         }
     259              :         else
     260              :         {
     261            0 :             nulls[3] = true;
     262            0 :             values[3] = (Datum) 0;
     263              :         }
     264              : 
     265              :         /* Build and return tuple */
     266            0 :         tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
     267            0 :         result = HeapTupleGetDatum(tuple);
     268              : 
     269              :         /* Move to next code point */
     270            0 :         ctx->current_codepoint++;
     271              : 
     272            0 :         SRF_RETURN_NEXT(funcctx, result);
     273              :     }
     274              : 
     275              :     /* All done */
     276            0 :     SRF_RETURN_DONE(funcctx);
     277              : }
        

Generated by: LCOV version 2.0-1