pg.ddx.io  pgsql-committers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Peter Eisentraut <peter.eisentraut@enterprisedb.com>
To: Laurenz Albe <laurenz.albe@cybertec.at>
To: pgsql-committers@lists.postgresql.org
Subject: Re: pgsql: SQL-standard function body
Date: Mon, 12 Apr 2021 17:25:30 +0200
Message-ID: <823c7393-a2ca-c44a-c57a-b5ff84c3de28@enterprisedb.com> (raw)
In-Reply-To: <7466db64eb6abae93e583faf8ca4004d60c163c4.camel@cybertec.at>
References: <E1lUEF5-00027L-N4@gemulon.postgresql.org>
	<cee01d26fe55bc086b3bcf10bfe4e8d450e2f608.camel@cybertec.at>
	<7466db64eb6abae93e583faf8ca4004d60c163c4.camel@cybertec.at>

On 10.04.21 03:38, Laurenz Albe wrote:
> On Fri, 2021-04-09 at 19:44 +0200, I wrote:
>>> SQL-standard function body
>>>
>>> psql needs some new intelligence to keep track of function body
>>> boundaries so that it doesn't send off statements when it sees
>>> semicolons that are inside a function body.
>>
>> This causes psql to fail to recognize the semicolon in the following
>> statement as the end of a statement:
>>
>>    IMPORT FOREIGN SCHEMA x FROM SERVER y INTO z OPTIONS (case 'lower');
>>
>> The cause is the "case", which is recognized as the start of a function
>> body.
>>
>> Would it be an option to recognize BEGIN and CASE as starting a
>> function body only if they are *not* inside parentheses, like in
>> the attached?
> 
> Here is an improved patch, which treats END in the same fashion
> (not properly indented for readability).

Thanks, I took another look at this and augmented your change with a 
change that tracks whether the statement starts with CREATE [OR REPLACE] 
{FUNCTION|PROCEDURE}.  That should make it pretty safe.  What do you think?

Attachments:

0001-psql-Refine-lexing-of-BEGIN.END-blocks-in-CREATE-FUN.patch (text/plain, 3.9K) download From 26251be9be89583278573ed28540e5f16f2616fb Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter@eisentraut.org> Date: Mon, 12 Apr 2021 17:19:59 +0200 Subject: [PATCH] psql: Refine lexing of BEGIN...END blocks in CREATE FUNCTION statements Only track BEGIN...END blocks if they are in a CREATE [OR REPLACE] {FUNCTION|PROCEDURE} statement. Ignore if in parentheses. Reported-by: Laurenz Albe <laurenz.albe@cybertec.at> --- src/fe_utils/psqlscan.l | 53 ++++++++++++++++++++++++----- src/include/fe_utils/psqlscan_int.h | 8 ++++- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l index 4ec57e96a9..991b7de0b5 100644 --- a/src/fe_utils/psqlscan.l +++ b/src/fe_utils/psqlscan.l @@ -870,18 +870,55 @@ other . {identifier} { - cur_state->identifier_count++; - if (pg_strcasecmp(yytext, "begin") == 0 - || pg_strcasecmp(yytext, "case") == 0) + /* + * We need to track if we are inside a BEGIN .. END block + * in a function definition, so that semicolons contained + * therein don't terminate the whole statement. Short of + * writing a full parser here, the following heuristic + * should work. First, we track whether the beginning of + * the statement matches CREATE [OR REPLACE] + * {FUNCTION|PROCEDURE} + */ + + if (cur_state->identifier_count == 0) + memset(cur_state->identifiers, 0, sizeof(cur_state->identifiers)); + + if (pg_strcasecmp(yytext, "create") == 0 || + pg_strcasecmp(yytext, "function") == 0 || + pg_strcasecmp(yytext, "procedure") == 0 || + pg_strcasecmp(yytext, "or") == 0 || + pg_strcasecmp(yytext, "replace") == 0) { - if (cur_state->identifier_count > 1) - cur_state->begin_depth++; + if (cur_state->identifier_count < sizeof(cur_state->identifiers)) + cur_state->identifiers[cur_state->identifier_count] = pg_tolower((unsigned char) yytext[0]); } - else if (pg_strcasecmp(yytext, "end") == 0) + + cur_state->identifier_count++; + + if (cur_state->identifiers[0] == 'c' && + (cur_state->identifiers[1] == 'f' || cur_state->identifiers[1] == 'p' || + (cur_state->identifiers[1] == 'o' && cur_state->identifiers[2] == 'r' && + (cur_state->identifiers[3] == 'f' || cur_state->identifiers[3] == 'p'))) && + cur_state->paren_depth == 0) { - if (cur_state->begin_depth > 0) - cur_state->begin_depth--; + if (pg_strcasecmp(yytext, "begin") == 0) + cur_state->begin_depth++; + else if (pg_strcasecmp(yytext, "case") == 0) + { + /* + * CASE also ends with END. We only need to track + * this if we are already inside a BEGIN. + */ + if (cur_state->begin_depth >= 1) + cur_state->begin_depth++; + } + else if (pg_strcasecmp(yytext, "end") == 0) + { + if (cur_state->begin_depth > 0) + cur_state->begin_depth--; + } } + ECHO; } diff --git a/src/include/fe_utils/psqlscan_int.h b/src/include/fe_utils/psqlscan_int.h index 91d7d4d5c6..434e554961 100644 --- a/src/include/fe_utils/psqlscan_int.h +++ b/src/include/fe_utils/psqlscan_int.h @@ -114,8 +114,14 @@ typedef struct PsqlScanStateData int paren_depth; /* depth of nesting in parentheses */ int xcdepth; /* depth of nesting in slash-star comments */ char *dolqstart; /* current $foo$ quote start string */ + + /* + * State to track boundaries of BEGIN ... END blocks in function + * definitions, so that semicolons do not send query too early. + */ int identifier_count; /* identifiers since start of statement */ - int begin_depth; /* depth of begin/end routine body blocks */ + char identifiers[4]; /* records the first few identifiers */ + int begin_depth; /* depth of begin/end pairs */ /* * Callback functions provided by the program making use of the lexer, -- 2.31.1

view thread (18+ messages)  latest in thread

Message-ID: <823c7393-a2ca-c44a-c57a-b5ff84c3de28@enterprisedb.com>
Permalink:  ../823c7393-a2ca-c44a-c57a-b5ff84c3de28@enterprisedb.com/
Also on:    postgresql.org/message-id/823c7393-a2ca-c44a-c57a-b5ff84c3de28@enterprisedb.com

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-committers@postgresql.org
  Cc: peter.eisentraut@enterprisedb.com, laurenz.albe@cybertec.at, pgsql-committers@lists.postgresql.org
  Subject: Re: pgsql: SQL-standard function body
  In-Reply-To: <823c7393-a2ca-c44a-c57a-b5ff84c3de28@enterprisedb.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox