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..cef1da0305 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 ":". + */ +namedop \:{identifier}\: +namedopfailed \:{identifier} + /* * Numbers * @@ -768,6 +777,23 @@ other . } <> { yyerror("unterminated dollar-quoted string"); } +{namedop} { + SET_YYLLOC(); + if (yyleng >= NAMEDATALEN) + yyerror("operator name too long"); + /* XXX Should we support double-quoted, case sensitive names? */ + yylval->str = downcase_identifier(yytext, yyleng, false, false); + return Op; + } + +{namedopfailed} { + SET_YYLLOC(); + /* throw back all but the initial ':' */ + yyless(1); + /* and treat it as {self}, since ':' is a member of that set of chars. */ + return yytext[0]; + } + {xdstart} { SET_YYLLOC(); BEGIN(xd); diff --git a/src/backend/parser/scansup.c b/src/backend/parser/scansup.c index 602108a40f..8d5287a5f5 100644 --- a/src/backend/parser/scansup.c +++ b/src/backend/parser/scansup.c @@ -125,3 +125,73 @@ 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; + char *tmp; + + if (len < 3 || len >= NAMEDATALEN) + return false; + + if (name[0] != ':' || name[len-1] != ':') + return false; + + // Make a copy of the name, since we need to scribble on it + tmp = pstrdup(name); + + // Disregard the delimiters + tmp[len-1] = '\0'; + tmp += 1; + valid_identifier = validIdentifier(tmp); + tmp -= 1; + pfree(tmp); + + 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/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index da427f4d4a..0aafb3297e 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -62,6 +62,7 @@ #include "getopt_long.h" #include "libpq/libpq-fs.h" #include "parallel.h" +#include "common/scansup.h" #include "pg_backup_db.h" #include "pg_backup_utils.h" #include "pg_dump.h" diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l index ae531ec240..39bacc6738 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 ":". + */ +namedop \:{identifier}\: +namedopfailed \:{identifier} + /* * Numbers * @@ -570,6 +579,16 @@ other . ECHO; } +{namedop} { + ECHO; + } + +{namedopfailed} { + /* throw back all but the initial ':' */ + yyless(1); + 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 */