mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Fix lexing of standard multi-character operators in edge cases.
Commitsc6b3c939b
(which fixed the precedence of >=, <=, <> operators) and865f14a2d
(which added support for the standard => notation for named arguments) created a class of lexer tokens which look like multi-character operators but which have their own token IDs distinct from Op. However, longest-match rules meant that following any of these tokens with another operator character, as in (1<>-1), would cause them to be incorrectly returned as Op. The error here isn't immediately obvious, because the parser would usually still find the correct operator via the Op token, but there were more subtle problems: 1. If immediately followed by a comment or +-, >= <= <> would be given the old precedence of Op rather than the correct new precedence; 2. If followed by a comment, != would be returned as Op rather than as NOT_EQUAL, causing it not to be found at all; 3. If followed by a comment or +-, the => token for named arguments would be lexed as Op, causing the argument to be mis-parsed as a simple expression, usually causing an error. Fix by explicitly checking for the operators in the {operator} code block in addition to all the existing special cases there. Backpatch to 9.5 where the problem was introduced. Analysis and patch by me; review by Tom Lane. Discussion: https://postgr.es/m/87va851ppl.fsf@news-spur.riddles.org.uk
This commit is contained in:
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user