aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/parser/scan.l28
-rw-r--r--src/fe_utils/psqlscan.l9
-rw-r--r--src/interfaces/ecpg/preproc/pgc.l28
-rw-r--r--src/test/regress/expected/create_operator.out74
-rw-r--r--src/test/regress/expected/polymorphism.out36
-rw-r--r--src/test/regress/sql/create_operator.sql31
-rw-r--r--src/test/regress/sql/polymorphism.sql15
7 files changed, 221 insertions, 0 deletions
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index 96f51bfd596..950b8b85918 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -339,6 +339,15 @@ identifier {ident_start}{ident_cont}*
typecast "::"
dot_dot \.\.
colon_equals ":="
+
+/*
+ * These operator-like tokens (unlike the above ones) also match the {operator}
+ * rule, which means that they might be overridden by a longer match if they
+ * are followed by a comment start or a + or - character. Accordingly, if you
+ * add to this list, you must also add corresponding code to the {operator}
+ * block to return the correct token in such cases. (This is not needed in
+ * psqlscan.l since the token value is ignored there.)
+ */
equals_greater "=>"
less_equals "<="
greater_equals ">="
@@ -929,6 +938,25 @@ other .
if (nchars == 1 &&
strchr(",()[].;:+-*/%^<>=", yytext[0]))
return yytext[0];
+ /*
+ * Likewise, if what we have left is two chars, and
+ * those match the tokens ">=", "<=", "=>", "<>" or
+ * "!=", then we must return the appropriate token
+ * rather than the generic Op.
+ */
+ if (nchars == 2)
+ {
+ if (yytext[0] == '=' && yytext[1] == '>')
+ return EQUALS_GREATER;
+ if (yytext[0] == '>' && yytext[1] == '=')
+ return GREATER_EQUALS;
+ if (yytext[0] == '<' && yytext[1] == '=')
+ return LESS_EQUALS;
+ if (yytext[0] == '<' && yytext[1] == '>')
+ return NOT_EQUALS;
+ if (yytext[0] == '!' && yytext[1] == '=')
+ return NOT_EQUALS;
+ }
}
/*
diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l
index 989284dc6fe..fdf49875a72 100644
--- a/src/fe_utils/psqlscan.l
+++ b/src/fe_utils/psqlscan.l
@@ -298,6 +298,15 @@ identifier {ident_start}{ident_cont}*
typecast "::"
dot_dot \.\.
colon_equals ":="
+
+/*
+ * These operator-like tokens (unlike the above ones) also match the {operator}
+ * rule, which means that they might be overridden by a longer match if they
+ * are followed by a comment start or a + or - character. Accordingly, if you
+ * add to this list, you must also add corresponding code to the {operator}
+ * block to return the correct token in such cases. (This is not needed in
+ * psqlscan.l since the token value is ignored there.)
+ */
equals_greater "=>"
less_equals "<="
greater_equals ">="
diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l
index 9ad50b99119..0792118cfe3 100644
--- a/src/interfaces/ecpg/preproc/pgc.l
+++ b/src/interfaces/ecpg/preproc/pgc.l
@@ -245,6 +245,15 @@ array ({ident_cont}|{whitespace}|[\[\]\+\-\*\%\/\(\)\>\.])*
typecast "::"
dot_dot \.\.
colon_equals ":="
+
+/*
+ * These operator-like tokens (unlike the above ones) also match the {operator}
+ * rule, which means that they might be overridden by a longer match if they
+ * are followed by a comment start or a + or - character. Accordingly, if you
+ * add to this list, you must also add corresponding code to the {operator}
+ * block to return the correct token in such cases. (This is not needed in
+ * psqlscan.l since the token value is ignored there.)
+ */
equals_greater "=>"
less_equals "<="
greater_equals ">="
@@ -732,6 +741,25 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
if (nchars == 1 &&
strchr(",()[].;:+-*/%^<>=", yytext[0]))
return yytext[0];
+ /*
+ * Likewise, if what we have left is two chars, and
+ * those match the tokens ">=", "<=", "=>", "<>" or
+ * "!=", then we must return the appropriate token
+ * rather than the generic Op.
+ */
+ if (nchars == 2)
+ {
+ if (yytext[0] == '=' && yytext[1] == '>')
+ return EQUALS_GREATER;
+ if (yytext[0] == '>' && yytext[1] == '=')
+ return GREATER_EQUALS;
+ if (yytext[0] == '<' && yytext[1] == '=')
+ return LESS_EQUALS;
+ if (yytext[0] == '<' && yytext[1] == '>')
+ return NOT_EQUALS;
+ if (yytext[0] == '!' && yytext[1] == '=')
+ return NOT_EQUALS;
+ }
}
base_yylval.str = mm_strdup(yytext);
diff --git a/src/test/regress/expected/create_operator.out b/src/test/regress/expected/create_operator.out
index 77237f48500..54e8b791595 100644
--- a/src/test/regress/expected/create_operator.out
+++ b/src/test/regress/expected/create_operator.out
@@ -45,6 +45,80 @@ CREATE OPERATOR => (
ERROR: syntax error at or near "=>"
LINE 1: CREATE OPERATOR => (
^
+-- lexing of <=, >=, <>, != has a number of edge cases
+-- (=> is tested elsewhere)
+-- this is legal because ! is not allowed in sql ops
+CREATE OPERATOR !=- (
+ leftarg = int8, -- right unary
+ procedure = numeric_fac
+);
+SELECT 2 !=-;
+ ?column?
+----------
+ 2
+(1 row)
+
+-- make sure lexer returns != as <> even in edge cases
+SELECT 2 !=/**/ 1, 2 !=/**/ 2;
+ ?column? | ?column?
+----------+----------
+ t | f
+(1 row)
+
+SELECT 2 !=-- comment to be removed by psql
+ 1;
+ ?column?
+----------
+ t
+(1 row)
+
+DO $$ -- use DO to protect -- from psql
+ declare r boolean;
+ begin
+ execute $e$ select 2 !=-- comment
+ 1 $e$ into r;
+ raise info 'r = %', r;
+ end;
+$$;
+INFO: r = t
+-- check that <= etc. followed by more operator characters are returned
+-- as the correct token with correct precedence
+SELECT true<>-1 BETWEEN 1 AND 1; -- BETWEEN has prec. above <> but below Op
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT false<>/**/1 BETWEEN 1 AND 1;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT false<=-1 BETWEEN 1 AND 1;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT false>=-1 BETWEEN 1 AND 1;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 2<=/**/3, 3>=/**/2, 2<>/**/3;
+ ?column? | ?column? | ?column?
+----------+----------+----------
+ t | t | t
+(1 row)
+
+SELECT 3<=/**/2, 2>=/**/3, 2<>/**/2;
+ ?column? | ?column? | ?column?
+----------+----------+----------
+ f | f | f
+(1 row)
+
-- Should fail. CREATE OPERATOR requires USAGE on SCHEMA
BEGIN TRANSACTION;
CREATE ROLE regress_rol_op1;
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 67e70c8c140..986417a1881 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -1478,6 +1478,42 @@ select dfunc('a'::text, 'b', flag => true); -- mixed notation
a
(1 row)
+-- this tests lexer edge cases around =>
+select dfunc(a =>-1);
+ dfunc
+-------
+ -1
+(1 row)
+
+select dfunc(a =>+1);
+ dfunc
+-------
+ 1
+(1 row)
+
+select dfunc(a =>/**/1);
+ dfunc
+-------
+ 1
+(1 row)
+
+select dfunc(a =>--comment to be removed by psql
+ 1);
+ dfunc
+-------
+ 1
+(1 row)
+
+-- need DO to protect the -- from psql
+do $$
+ declare r integer;
+ begin
+ select dfunc(a=>-- comment
+ 1) into r;
+ raise info 'r = %', r;
+ end;
+$$;
+INFO: r = 1
-- check reverse-listing of named-arg calls
CREATE VIEW dfview AS
SELECT q1, q2,
diff --git a/src/test/regress/sql/create_operator.sql b/src/test/regress/sql/create_operator.sql
index 625e9b97485..8b6fd0bb43d 100644
--- a/src/test/regress/sql/create_operator.sql
+++ b/src/test/regress/sql/create_operator.sql
@@ -45,6 +45,37 @@ CREATE OPERATOR => (
procedure = numeric_fac
);
+-- lexing of <=, >=, <>, != has a number of edge cases
+-- (=> is tested elsewhere)
+
+-- this is legal because ! is not allowed in sql ops
+CREATE OPERATOR !=- (
+ leftarg = int8, -- right unary
+ procedure = numeric_fac
+);
+SELECT 2 !=-;
+-- make sure lexer returns != as <> even in edge cases
+SELECT 2 !=/**/ 1, 2 !=/**/ 2;
+SELECT 2 !=-- comment to be removed by psql
+ 1;
+DO $$ -- use DO to protect -- from psql
+ declare r boolean;
+ begin
+ execute $e$ select 2 !=-- comment
+ 1 $e$ into r;
+ raise info 'r = %', r;
+ end;
+$$;
+
+-- check that <= etc. followed by more operator characters are returned
+-- as the correct token with correct precedence
+SELECT true<>-1 BETWEEN 1 AND 1; -- BETWEEN has prec. above <> but below Op
+SELECT false<>/**/1 BETWEEN 1 AND 1;
+SELECT false<=-1 BETWEEN 1 AND 1;
+SELECT false>=-1 BETWEEN 1 AND 1;
+SELECT 2<=/**/3, 3>=/**/2, 2<>/**/3;
+SELECT 3<=/**/2, 2>=/**/3, 2<>/**/2;
+
-- Should fail. CREATE OPERATOR requires USAGE on SCHEMA
BEGIN TRANSACTION;
CREATE ROLE regress_rol_op1;
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index 2f65f0f97d6..03606671d92 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -785,6 +785,21 @@ select dfunc('a'::text, 'b', flag => false); -- mixed notation
select dfunc('a'::text, 'b', true); -- full positional notation
select dfunc('a'::text, 'b', flag => true); -- mixed notation
+-- this tests lexer edge cases around =>
+select dfunc(a =>-1);
+select dfunc(a =>+1);
+select dfunc(a =>/**/1);
+select dfunc(a =>--comment to be removed by psql
+ 1);
+-- need DO to protect the -- from psql
+do $$
+ declare r integer;
+ begin
+ select dfunc(a=>-- comment
+ 1) into r;
+ raise info 'r = %', r;
+ end;
+$$;
-- check reverse-listing of named-arg calls
CREATE VIEW dfview AS