diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 1017f2eed1..c5b8562cb5 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -31,6 +31,7 @@ #include "catalog/pg_type.h" #include "miscadmin.h" #include "parser/parse_oper.h" +#include "parser/scansup.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -79,6 +80,10 @@ validOperatorName(const char *name) if (len == 0 || len >= NAMEDATALEN) return false; + /* Is this a Named Operator? */ + if (validNamedOperator(name)) + return true; + /* Can't contain any invalid characters */ /* Test string here should match op_chars in scan.l */ if (strspn(name, "~!@#^&|`?+-*/%<>=") != len) diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index db8b0fe8eb..fa282dde4d 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -379,6 +379,15 @@ self [,()\[\].;\:\+\-\*\/\%\^\<\>\=] op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=] operator {op_chars}+ +/* + * Named Operators, e.g. \#foo + * + * {namedopfailed} is an error rule to avoid scanner backup when + * {namedop} fails to match its trailing {identifier}. + */ +namedop \\\#{identifier} +namedopfailed \\\# + /* * Numbers * @@ -768,6 +777,18 @@ other . } <> { yyerror("unterminated dollar-quoted string"); } +{namedop} { + SET_YYLLOC(); + if (yyleng >= NAMEDATALEN) + yyerror("operator name too long"); + yylval->str = downcase_identifier(yytext, yyleng, false, false); + return Op; + } + +{namedopfailed} { + yyerror("unexpected token"); + } + {xdstart} { SET_YYLLOC(); BEGIN(xd); diff --git a/src/backend/parser/scansup.c b/src/backend/parser/scansup.c index 602108a40f..db94a927f7 100644 --- a/src/backend/parser/scansup.c +++ b/src/backend/parser/scansup.c @@ -125,3 +125,65 @@ scanner_isspace(char ch) return true; return false; } + +/* + * validNamedOperator() -- return true if name adheres to the scanner rule + * {namedop} + */ +bool +validNamedOperator(const char *name) +{ + size_t len = strlen(name); + bool valid_identifier; + + if (len < 3 || len >= NAMEDATALEN) + return false; + + if (name[0] != '\\' || name[1] != '#') + return false; + + // Disregard the delimiters + valid_identifier = validIdentifier(name + 2); + + return valid_identifier; +} + +/* + * validIdentifier() -- return true if name adheres to the scanner rule + * {identifier} + * + * Note: this function does not check if the identifier length + * is less than NAMEDATALEN. + */ +bool +validIdentifier(const char *name) +{ + uint8 c; + size_t i, len = strlen(name); + + // Reject if first character is not part of ident_start + c = name[0]; + if ( !(c == '_' + || (c >='A' && c <= 'Z') + || (c >='a' && c <= 'z') + || (c >= 0200 && c <= 0377))) + { + return false; + } + + // Reject if other characters are not part of ident_cont + for (i = 1; i < len; ++i) + { + c = name[i]; + if ( !(c == '_' || c == '$' + || (c >='A' && c <= 'Z') + || (c >='a' && c <= 'z') + || (c >='0' && c <= '9') + || (c >= 0200 && c <= 0377))) + { + return false; + } + } + + return true; +} diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l index ae531ec240..8ec9572d5e 100644 --- a/src/fe_utils/psqlscan.l +++ b/src/fe_utils/psqlscan.l @@ -317,6 +317,15 @@ self [,()\[\].;\:\+\-\*\/\%\^\<\>\=] op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=] operator {op_chars}+ +/* + * Named Operators, e.g. \#foo + * + * {namedopfailed} is an error rule to avoid scanner backup when + * {namedop} fails to match its trailing {identifier}. + */ +namedop \\\#{identifier} +namedopfailed \\\# + /* * Numbers * @@ -570,6 +579,14 @@ other . ECHO; } +{namedop} { + ECHO; + } + +{namedopfailed} { + ECHO; + } + {xdstart} { BEGIN(xd); ECHO; diff --git a/src/include/parser/scansup.h b/src/include/parser/scansup.h index ff65224bf6..0f6aff8b44 100644 --- a/src/include/parser/scansup.h +++ b/src/include/parser/scansup.h @@ -24,4 +24,7 @@ extern void truncate_identifier(char *ident, int len, bool warn); extern bool scanner_isspace(char ch); +extern bool validNamedOperator(const char *name); +extern bool validIdentifier(const char *name); + #endif /* SCANSUP_H */