LCOV - differential code coverage report
Current view: top level - src/test/modules/test_json_parser - test_json_parser_incremental.c (source / functions) Coverage Total Hit UNC GNC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 75.9 % 133 101 32 101
Current Date: 2024-04-14 14:21:10 Functions: 91.7 % 12 11 1 11
Baseline: 16@8cea358b128 Branches: 65.9 % 41 27 14 27
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed [..60] days: 75.9 % 133 101 32 101
Function coverage date bins:
[..60] days: 91.7 % 12 11 1 11
Branch coverage date bins:
[..60] days: 65.9 % 41 27 14 27

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * test_json_parser_incremental.c
                                  4                 :                :  *    Test program for incremental JSON parser
                                  5                 :                :  *
                                  6                 :                :  * Copyright (c) 2024, PostgreSQL Global Development Group
                                  7                 :                :  *
                                  8                 :                :  * IDENTIFICATION
                                  9                 :                :  *    src/test/modules/test_json_parser/test_json_parser_incremental.c
                                 10                 :                :  *
                                 11                 :                :  * This program tests incremental parsing of json. The input is fed into
                                 12                 :                :  * the parser in very small chunks. In practice you would normally use
                                 13                 :                :  * much larger chunks, but doing this makes it more likely that the
                                 14                 :                :  * full range of increment handling, especially in the lexer, is exercised.
                                 15                 :                :  * If the "-c SIZE" option is provided, that chunk size is used instead
                                 16                 :                :  * of the default of 60.
                                 17                 :                :  *
                                 18                 :                :  * The argument specifies the file containing the JSON input.
                                 19                 :                :  *
                                 20                 :                :  *-------------------------------------------------------------------------
                                 21                 :                :  */
                                 22                 :                : 
                                 23                 :                : #include "postgres_fe.h"
                                 24                 :                : 
                                 25                 :                : #include <stdio.h>
                                 26                 :                : #include <sys/types.h>
                                 27                 :                : #include <sys/stat.h>
                                 28                 :                : #include <unistd.h>
                                 29                 :                : 
                                 30                 :                : #include "common/jsonapi.h"
                                 31                 :                : #include "lib/stringinfo.h"
                                 32                 :                : #include "mb/pg_wchar.h"
                                 33                 :                : #include "pg_getopt.h"
                                 34                 :                : 
                                 35                 :                : #define BUFSIZE 6000
                                 36                 :                : #define DEFAULT_CHUNK_SIZE 60
                                 37                 :                : 
                                 38                 :                : typedef struct DoState
                                 39                 :                : {
                                 40                 :                :     JsonLexContext *lex;
                                 41                 :                :     bool        elem_is_first;
                                 42                 :                :     StringInfo  buf;
                                 43                 :                : } DoState;
                                 44                 :                : 
                                 45                 :                : static void usage(const char *progname);
                                 46                 :                : static void escape_json(StringInfo buf, const char *str);
                                 47                 :                : 
                                 48                 :                : /* semantic action functions for parser */
                                 49                 :                : static JsonParseErrorType do_object_start(void *state);
                                 50                 :                : static JsonParseErrorType do_object_end(void *state);
                                 51                 :                : static JsonParseErrorType do_object_field_start(void *state, char *fname, bool isnull);
                                 52                 :                : static JsonParseErrorType do_object_field_end(void *state, char *fname, bool isnull);
                                 53                 :                : static JsonParseErrorType do_array_start(void *state);
                                 54                 :                : static JsonParseErrorType do_array_end(void *state);
                                 55                 :                : static JsonParseErrorType do_array_element_start(void *state, bool isnull);
                                 56                 :                : static JsonParseErrorType do_array_element_end(void *state, bool isnull);
                                 57                 :                : static JsonParseErrorType do_scalar(void *state, char *token, JsonTokenType tokentype);
                                 58                 :                : 
                                 59                 :                : JsonSemAction sem = {
                                 60                 :                :     .object_start = do_object_start,
                                 61                 :                :     .object_end = do_object_end,
                                 62                 :                :     .object_field_start = do_object_field_start,
                                 63                 :                :     .object_field_end = do_object_field_end,
                                 64                 :                :     .array_start = do_array_start,
                                 65                 :                :     .array_end = do_array_end,
                                 66                 :                :     .array_element_start = do_array_element_start,
                                 67                 :                :     .array_element_end = do_array_element_end,
                                 68                 :                :     .scalar = do_scalar
                                 69                 :                : };
                                 70                 :                : 
                                 71                 :                : int
   35 andrew@dunslane.net        72                 :GNC         487 : main(int argc, char **argv)
                                 73                 :                : {
                                 74                 :                :     char        buff[BUFSIZE];
                                 75                 :                :     FILE       *json_file;
                                 76                 :                :     JsonParseErrorType result;
                                 77                 :                :     JsonLexContext lex;
                                 78                 :                :     StringInfoData json;
                                 79                 :                :     int         n_read;
    5                            80                 :            487 :     size_t      chunk_size = DEFAULT_CHUNK_SIZE;
                                 81                 :                :     struct stat statbuf;
                                 82                 :                :     off_t       bytes_left;
   35                            83                 :            487 :     JsonSemAction *testsem = &nullSemAction;
                                 84                 :                :     char       *testfile;
                                 85                 :                :     int         c;
                                 86                 :            487 :     bool        need_strings = false;
                                 87                 :                : 
                                 88         [ +  + ]:           1461 :     while ((c = getopt(argc, argv, "c:s")) != -1)
                                 89                 :                :     {
                                 90      [ -  +  + ]:            487 :         switch (c)
                                 91                 :                :         {
                                 92                 :            486 :             case 'c':           /* chunksize */
                                 93                 :            486 :                 sscanf(optarg, "%zu", &chunk_size);
    5                            94         [ -  + ]:            486 :                 if (chunk_size > BUFSIZE)
                                 95                 :                :                 {
    5 andrew@dunslane.net        96                 :UNC           0 :                     fprintf(stderr, "chunk size cannot exceed %d\n", BUFSIZE);
                                 97                 :              0 :                     exit(1);
                                 98                 :                :                 }
   35 andrew@dunslane.net        99                 :GNC         486 :                 break;
                                100                 :              1 :             case 's':           /* do semantic processing */
                                101                 :              1 :                 testsem = &sem;
                                102                 :              1 :                 sem.semstate = palloc(sizeof(struct DoState));
                                103                 :              1 :                 ((struct DoState *) sem.semstate)->lex = &lex;
                                104                 :              1 :                 ((struct DoState *) sem.semstate)->buf = makeStringInfo();
                                105                 :              1 :                 need_strings = true;
                                106                 :              1 :                 break;
                                107                 :                :         }
                                108                 :                :     }
                                109                 :                : 
                                110         [ +  - ]:            487 :     if (optind < argc)
                                111                 :                :     {
                                112                 :            487 :         testfile = pg_strdup(argv[optind]);
                                113                 :            487 :         optind++;
                                114                 :                :     }
                                115                 :                :     else
                                116                 :                :     {
   35 andrew@dunslane.net       117                 :UNC           0 :         usage(argv[0]);
                                118                 :              0 :         exit(1);
                                119                 :                :     }
                                120                 :                : 
   35 andrew@dunslane.net       121                 :GNC         487 :     makeJsonLexContextIncremental(&lex, PG_UTF8, need_strings);
                                122                 :            487 :     initStringInfo(&json);
                                123                 :                : 
                                124                 :            487 :     json_file = fopen(testfile, "r");
                                125                 :            487 :     fstat(fileno(json_file), &statbuf);
                                126                 :            487 :     bytes_left = statbuf.st_size;
                                127                 :                : 
                                128                 :                :     for (;;)
                                129                 :                :     {
                                130                 :          92974 :         n_read = fread(buff, 1, chunk_size, json_file);
                                131                 :          92974 :         appendBinaryStringInfo(&json, buff, n_read);
                                132                 :                : 
                                133                 :                :         /*
                                134                 :                :          * Append some trailing junk to the buffer passed to the parser. This
                                135                 :                :          * helps us ensure that the parser does the right thing even if the
                                136                 :                :          * chunk isn't terminated with a '\0'.
                                137                 :                :          */
                                138                 :          92974 :         appendStringInfoString(&json, "1+23 trailing junk");
                                139                 :          92974 :         bytes_left -= n_read;
                                140         [ +  + ]:          92974 :         if (bytes_left > 0)
                                141                 :                :         {
                                142                 :          92530 :             result = pg_parse_json_incremental(&lex, testsem,
                                143                 :                :                                                json.data, n_read,
                                144                 :                :                                                false);
                                145         [ +  + ]:          92530 :             if (result != JSON_INCOMPLETE)
                                146                 :                :             {
                                147                 :             43 :                 fprintf(stderr, "%s\n", json_errdetail(result, &lex));
                                148                 :             43 :                 exit(1);
                                149                 :                :             }
                                150                 :          92487 :             resetStringInfo(&json);
                                151                 :                :         }
                                152                 :                :         else
                                153                 :                :         {
                                154                 :            444 :             result = pg_parse_json_incremental(&lex, testsem,
                                155                 :                :                                                json.data, n_read,
                                156                 :                :                                                true);
                                157         [ +  + ]:            444 :             if (result != JSON_SUCCESS)
                                158                 :                :             {
                                159                 :            192 :                 fprintf(stderr, "%s\n", json_errdetail(result, &lex));
                                160                 :            192 :                 exit(1);
                                161                 :                :             }
                                162         [ +  + ]:            252 :             if (!need_strings)
                                163                 :            251 :                 printf("SUCCESS!\n");
                                164                 :            252 :             break;
                                165                 :                :         }
                                166                 :                :     }
                                167                 :            252 :     fclose(json_file);
                                168                 :            252 :     exit(0);
                                169                 :                : }
                                170                 :                : 
                                171                 :                : /*
                                172                 :                :  * The semantic routines here essentially just output the same json, except
                                173                 :                :  * for white space. We could pretty print it but there's no need for our
                                174                 :                :  * purposes. The result should be able to be fed to any JSON processor
                                175                 :                :  * such as jq for validation.
                                176                 :                :  */
                                177                 :                : 
                                178                 :                : static JsonParseErrorType
                                179                 :             40 : do_object_start(void *state)
                                180                 :                : {
                                181                 :             40 :     DoState    *_state = (DoState *) state;
                                182                 :                : 
                                183                 :             40 :     printf("{\n");
                                184                 :             40 :     _state->elem_is_first = true;
                                185                 :                : 
                                186                 :             40 :     return JSON_SUCCESS;
                                187                 :                : }
                                188                 :                : 
                                189                 :                : static JsonParseErrorType
                                190                 :             40 : do_object_end(void *state)
                                191                 :                : {
                                192                 :             40 :     DoState    *_state = (DoState *) state;
                                193                 :                : 
                                194                 :             40 :     printf("\n}\n");
                                195                 :             40 :     _state->elem_is_first = false;
                                196                 :                : 
                                197                 :             40 :     return JSON_SUCCESS;
                                198                 :                : }
                                199                 :                : 
                                200                 :                : static JsonParseErrorType
                                201                 :            155 : do_object_field_start(void *state, char *fname, bool isnull)
                                202                 :                : {
                                203                 :            155 :     DoState    *_state = (DoState *) state;
                                204                 :                : 
                                205         [ +  + ]:            155 :     if (!_state->elem_is_first)
                                206                 :            120 :         printf(",\n");
                                207                 :            155 :     resetStringInfo(_state->buf);
                                208                 :            155 :     escape_json(_state->buf, fname);
                                209                 :            155 :     printf("%s: ", _state->buf->data);
                                210                 :            155 :     _state->elem_is_first = false;
                                211                 :                : 
                                212                 :            155 :     return JSON_SUCCESS;
                                213                 :                : }
                                214                 :                : 
                                215                 :                : static JsonParseErrorType
                                216                 :            155 : do_object_field_end(void *state, char *fname, bool isnull)
                                217                 :                : {
                                218                 :                :     /* nothing to do really */
                                219                 :                : 
                                220                 :            155 :     return JSON_SUCCESS;
                                221                 :                : }
                                222                 :                : 
                                223                 :                : static JsonParseErrorType
                                224                 :             11 : do_array_start(void *state)
                                225                 :                : {
                                226                 :             11 :     DoState    *_state = (DoState *) state;
                                227                 :                : 
                                228                 :             11 :     printf("[\n");
                                229                 :             11 :     _state->elem_is_first = true;
                                230                 :                : 
                                231                 :             11 :     return JSON_SUCCESS;
                                232                 :                : }
                                233                 :                : 
                                234                 :                : static JsonParseErrorType
                                235                 :             11 : do_array_end(void *state)
                                236                 :                : {
                                237                 :             11 :     DoState    *_state = (DoState *) state;
                                238                 :                : 
                                239                 :             11 :     printf("\n]\n");
                                240                 :             11 :     _state->elem_is_first = false;
                                241                 :                : 
                                242                 :             11 :     return JSON_SUCCESS;
                                243                 :                : }
                                244                 :                : 
                                245                 :                : static JsonParseErrorType
                                246                 :             30 : do_array_element_start(void *state, bool isnull)
                                247                 :                : {
                                248                 :             30 :     DoState    *_state = (DoState *) state;
                                249                 :                : 
                                250         [ +  + ]:             30 :     if (!_state->elem_is_first)
                                251                 :             19 :         printf(",\n");
                                252                 :             30 :     _state->elem_is_first = false;
                                253                 :                : 
                                254                 :             30 :     return JSON_SUCCESS;
                                255                 :                : }
                                256                 :                : 
                                257                 :                : static JsonParseErrorType
                                258                 :             30 : do_array_element_end(void *state, bool isnull)
                                259                 :                : {
                                260                 :                :     /* nothing to do */
                                261                 :                : 
                                262                 :             30 :     return JSON_SUCCESS;
                                263                 :                : }
                                264                 :                : 
                                265                 :                : static JsonParseErrorType
                                266                 :            135 : do_scalar(void *state, char *token, JsonTokenType tokentype)
                                267                 :                : {
                                268                 :            135 :     DoState    *_state = (DoState *) state;
                                269                 :                : 
                                270         [ +  + ]:            135 :     if (tokentype == JSON_TOKEN_STRING)
                                271                 :                :     {
                                272                 :            105 :         resetStringInfo(_state->buf);
                                273                 :            105 :         escape_json(_state->buf, token);
                                274                 :            105 :         printf("%s", _state->buf->data);
                                275                 :                :     }
                                276                 :                :     else
                                277                 :             30 :         printf("%s", token);
                                278                 :                : 
                                279                 :            135 :     return JSON_SUCCESS;
                                280                 :                : }
                                281                 :                : 
                                282                 :                : 
                                283                 :                : /*  copied from backend code */
                                284                 :                : static void
                                285                 :            260 : escape_json(StringInfo buf, const char *str)
                                286                 :                : {
                                287                 :                :     const char *p;
                                288                 :                : 
                                289         [ -  + ]:            260 :     appendStringInfoCharMacro(buf, '"');
                                290         [ +  + ]:           4093 :     for (p = str; *p; p++)
                                291                 :                :     {
                                292   [ -  -  -  -  :           3833 :         switch (*p)
                                        -  -  -  + ]
                                293                 :                :         {
   35 andrew@dunslane.net       294                 :UNC           0 :             case '\b':
                                295                 :              0 :                 appendStringInfoString(buf, "\\b");
                                296                 :              0 :                 break;
                                297                 :              0 :             case '\f':
                                298                 :              0 :                 appendStringInfoString(buf, "\\f");
                                299                 :              0 :                 break;
                                300                 :              0 :             case '\n':
                                301                 :              0 :                 appendStringInfoString(buf, "\\n");
                                302                 :              0 :                 break;
                                303                 :              0 :             case '\r':
                                304                 :              0 :                 appendStringInfoString(buf, "\\r");
                                305                 :              0 :                 break;
                                306                 :              0 :             case '\t':
                                307                 :              0 :                 appendStringInfoString(buf, "\\t");
                                308                 :              0 :                 break;
                                309                 :              0 :             case '"':
                                310                 :              0 :                 appendStringInfoString(buf, "\\\"");
                                311                 :              0 :                 break;
                                312                 :              0 :             case '\\':
                                313                 :              0 :                 appendStringInfoString(buf, "\\\\");
                                314                 :              0 :                 break;
   35 andrew@dunslane.net       315                 :GNC        3833 :             default:
                                316         [ -  + ]:           3833 :                 if ((unsigned char) *p < ' ')
   35 andrew@dunslane.net       317                 :UNC           0 :                     appendStringInfo(buf, "\\u%04x", (int) *p);
                                318                 :                :                 else
   35 andrew@dunslane.net       319         [ -  + ]:GNC        3833 :                     appendStringInfoCharMacro(buf, *p);
                                320                 :           3833 :                 break;
                                321                 :                :         }
                                322                 :                :     }
                                323         [ -  + ]:            260 :     appendStringInfoCharMacro(buf, '"');
                                324                 :            260 : }
                                325                 :                : 
                                326                 :                : static void
   35 andrew@dunslane.net       327                 :UNC           0 : usage(const char *progname)
                                328                 :                : {
                                329                 :              0 :     fprintf(stderr, "Usage: %s [OPTION ...] testfile\n", progname);
                                330                 :              0 :     fprintf(stderr, "Options:\n");
                                331                 :              0 :     fprintf(stderr, "  -c chunksize      size of piece fed to parser (default 64)n");
                                332                 :              0 :     fprintf(stderr, "  -s                do semantic processing\n");
                                333                 :                : 
                                334                 :              0 : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622