Re: Portal->commandTag as an enum - Mailing list pgsql-hackers

From Mark Dilger
Subject Re: Portal->commandTag as an enum
Date
Msg-id D46F74B8-E1C7-48DF-8746-3D6FA46F201A@enterprisedb.com
Whole thread Raw
In response to Re: Portal->commandTag as an enum  (Mark Dilger <mark.dilger@enterprisedb.com>)
Responses Re: Portal->commandTag as an enum
List pgsql-hackers

> On Feb 3, 2020, at 9:41 AM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
>
> In v1, I stayed closer to the existing code structure than you are requesting.  I like the direction you’re
suggestingthat I go, and I’ve begun that transition in anticipation of posting a v2 patch set soon. 

Ok, here is v2, attached.

In master, a number of functions pass a char *completionTag argument (really a char
completionTag[COMPLETION_TAG_BUFSIZE])which gets filled in with the string to return to the client from EndCommand.  I
haveremoved that kind of logic: 

-               /* save the rowcount if we're given a completionTag to fill */
-               if (completionTag)
-                       snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-                                        "SELECT " UINT64_FORMAT,
-                                        queryDesc->estate->es_processed);

In the patch, this is replaced with a new struct, QueryCompletionData.  That bit of code above is replaced with:

+               /* save the rowcount if we're given a qc to fill */
+               if (qc)
+                       SetQC(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed, DISPLAYFORMAT_NPROCESSED);

For wire protocol compatibility, we have to track the display format.  When this gets to EndCommand, the display format
allowsthe string to be written exactly as the client will expect.  If we ever get to the point where we can break with
thatcompatibility, the third member of this struct, display_format, can be removed. 

Where string parsing is being done in master to get the count back out, it changes to look like this:

-                                       if (strncmp(completionTag, "SELECT ", 7) == 0)
-                                               _SPI_current->processed =
-                                                       pg_strtouint64(completionTag + 7, NULL, 10);
+                                       if (qcdata.commandTag == COMMANDTAG_SELECT)
+                                               _SPI_current->processed = qcdata.nprocessed;

One of the advantages to the patch is that the commandTag for a portal is not overwritten by the commandTag in the
QueryCompletionData,meaning for example that if an EXECUTE command returns the string “UPDATE 0”, the
portal->commandTagremains COMMANDTAG_EXECUTE while the qcdata.commandTag becomes COMMANDTAG_UPDATE.  This could be
helpfulto code trying to track how many operations of a given type have run. 

In event_trigger.c, in master there are ad-hoc comparisons against c-strings:

-       /*
-        * Handle some idiosyncratic special cases.
-        */
-       if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
-               pg_strcasecmp(tag, "SELECT INTO") == 0 ||
-               pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
-               pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
-               pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
-               pg_strcasecmp(tag, "COMMENT") == 0 ||
-               pg_strcasecmp(tag, "GRANT") == 0 ||
-               pg_strcasecmp(tag, "REVOKE") == 0 ||
-               pg_strcasecmp(tag, "DROP OWNED") == 0 ||
-               pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
-               pg_strcasecmp(tag, "SECURITY LABEL") == 0)

These are replaced by switch() case statements over the possible commandTags:

+       switch (commandTag)
+       {
+                       /*
+                        * Supported idiosyncratic special cases.
+                        */
+               case COMMANDTAG_ALTER_DEFAULT_PRIVILEGES:
+               case COMMANDTAG_ALTER_LARGE_OBJECT:
+               case COMMANDTAG_COMMENT:
+               case COMMANDTAG_CREATE_TABLE_AS:
+               case COMMANDTAG_DROP_OWNED:
+               case COMMANDTAG_GRANT:
+               case COMMANDTAG_IMPORT_FOREIGN_SCHEMA:
+               case COMMANDTAG_REFRESH_MATERIALIZED_VIEW:
+               case COMMANDTAG_REVOKE:
+               case COMMANDTAG_SECURITY_LABEL:
+               case COMMANDTAG_SELECT_INTO:

I think this is easier to read, verify, and maintain.  The compiler can help if you leave a command tag out of the
list,which the compiler cannot help discover in master as it is currently written.  But I also think all those
pg_strcasecmpcalls are likely more expensive at runtime. 

In master, EventTriggerCacheItem tracks a sorted array of palloc’d cstrings.  In the patch, that becomes a Bitmapset
overthe enum: 

typedef struct
 {
        Oid                     fnoid;                  /* function to be called */
        char            enabled;                /* as SESSION_REPLICATION_ROLE_* */
-       int                     ntags;                  /* number of command tags */
-       char      **tag;                        /* command tags in SORTED order */
+       Bitmapset  *tagset;                     /* command tags, or NULL if empty */
 } EventTriggerCacheItem;

The code in evtcache.c is shorter and, in my opinion, easier to read.  In filter_event_trigger, rather than running
bsearchthrough a sorted array of strings, it just runs bms_is_member. 

I’ve kept this change to the event trigger code in its own separate patch file, to make the change easier to review in
isolation.

> I’ll include stats about code-size and speed when I post v2.

The benchmarks are from tpc-b_96.sql.  I think I’ll need to adjust the benchmark to put more emphasis on the particular
codethat I’m changing, but I have run this standard benchmark for this email: 

For master (1fd687a035):

    postgresql % find src -type f -name "*.c" -or -name "*.h" | xargs cat | wc
     1482117 5690660 45256959

    postgresql % find src -type f -name "*.o" | xargs cat | wc
       38283  476264 18999164

Averages for test set 1 by scale:
set    scale    tps    avg_latency    90%<    max_latency
1    1    3741    1.734    3.162    133.718
1    9    6124    0.904    1.05    230.547
1    81    5921    0.931    1.015    67.023

Averages for test set 1 by clients:
set    clients    tps    avg_latency    90%<    max_latency
1    1    2163    0.461    0.514    24.414
1    4    5968    0.675    0.791    40.354
1    16    7655    2.433    3.922    366.519


For command tag patch (branched from 1fd687a035):

    postgresql % find src -type f -name "*.c" -or -name "*.h" | xargs cat | wc
     1482969 5691908 45281399

    postgresql % find src -type f -name "*.o" | xargs cat | wc
       38209  476243 18999752


Averages for test set 1 by scale:
set    scale    tps    avg_latency    90%<    max_latency
1    1    3877    1.645    3.066    24.973
1    9    6383    0.859    1.032    64.566
1    81    5945    0.925    1.023    162.9

Averages for test set 1 by clients:
set    clients    tps    avg_latency    90%<    max_latency
1    1    2141    0.466    0.522    11.531
1    4    5967    0.673    0.783    136.882
1    16    8096    2.292    3.817    104.026





—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company




Attachment

pgsql-hackers by date:

Previous
From: Michael Paquier
Date:
Subject: Re: Expose lock group leader pid in pg_stat_activity
Next
From: Amit Kapila
Date:
Subject: Re: closesocket behavior in different platforms