agora inbox for pgsql-performance@postgresql.org
help / color / mirror / Atom feedproposal: schema variables
433+ messages / 45 participants
[nested] [flat]
* proposal: schema variables
@ 2017-10-26 07:21 Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:30 ` Re: proposal: schema variables Tatsuo Ishii <ishii@sraoss.co.jp>
2017-10-27 05:47 ` Re: proposal: schema variables Tsunakawa, Takayuki <tsunakawa.takay@jp.fujitsu.com>
2017-10-27 13:38 ` Re: proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2017-10-28 14:24 ` Re: proposal: schema variables Chris Travers <chris.travers@adjust.com>
2017-11-01 18:03 ` Re: proposal: schema variables Mark Dilger <hornschnorter@gmail.com>
2017-11-02 12:35 ` Re: proposal: schema variables Robert Haas <robertmhaas@gmail.com>
2017-11-02 15:07 ` Re: proposal: schema variables Craig Ringer <craig@2ndquadrant.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2018-04-17 14:14 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
0 siblings, 10 replies; 433+ messages in thread
From: Pavel Stehule @ 2017-10-26 07:21 UTC (permalink / raw)
To: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi,
I propose a new database object - a variable. The variable is persistent
object, that holds unshared session based not transactional in memory value
of any type. Like variables in any other languages. The persistence is
required for possibility to do static checks, but can be limited to session
- the variables can be temporal.
My proposal is related to session variables from Sybase, MSSQL or MySQL
(based on prefix usage @ or @@), or package variables from Oracle (access
is controlled by scope), or schema variables from DB2. Any design is coming
from different sources, traditions and has some advantages or
disadvantages. The base of my proposal is usage schema variables as session
variables for stored procedures. It should to help to people who try to
port complex projects to PostgreSQL from other databases.
The Sybase (T-SQL) design is good for interactive work, but it is weak for
usage in stored procedures - the static check is not possible. Is not
possible to set some access rights on variables.
The ADA design (used on Oracle) based on scope is great, but our
environment is not nested. And we should to support other PL than PLpgSQL
more strongly.
There is not too much other possibilities - the variable that should be
accessed from different PL, different procedures (in time) should to live
somewhere over PL, and there is the schema only.
The variable can be created by CREATE statement:
CREATE VARIABLE public.myvar AS integer;
CREATE VARIABLE myschema.myvar AS mytype;
CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
[ DEFAULT expression ] [[NOT] NULL]
[ ON TRANSACTION END { RESET | DROP } ]
[ { VOLATILE | STABLE } ];
It is dropped by command DROP VARIABLE [ IF EXISTS] varname.
The access rights is controlled by usual access rights - by commands
GRANT/REVOKE. The possible rights are: READ, WRITE
The variables can be modified by SQL command SET (this is taken from
standard, and it natural)
SET varname = expression;
Unfortunately we use the SET command for different purpose. But I am
thinking so we can solve it with few tricks. The first is moving our GUC to
pg_catalog schema. We can control the strictness of SET command. In one
variant, we can detect custom GUC and allow it, in another we can disallow
a custom GUC and allow only schema variables. A new command LET can be
alternative.
The variables should be used in queries implicitly (without JOIN)
SELECT varname;
The SEARCH_PATH is used, when varname is located. The variables can be used
everywhere where query parameters are allowed.
I hope so this proposal is good enough and simple.
Comments, notes?
regards
Pavel
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-10-26 22:07 ` Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
9 siblings, 1 reply; 433+ messages in thread
From: Nico Williams @ 2017-10-26 22:07 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
On Thu, Oct 26, 2017 at 09:21:24AM +0200, Pavel Stehule wrote:
> Comments, notes?
I like it.
I would further like to move all of postgresql.conf into the database,
as much as possible, as well as pg_ident.conf and pg_hba.conf.
Variables like current_user have a sort of nesting context
functionality: calling a SECURITY DEFINER function "pushes" a new value
onto current_user, then when the function returns the new value of
current_user is "popped" and the previous value restored.
It might be nice to be able to generalize this.
Questions that then arise:
- can one see up the stack?
- are there permissions issues with seeing up the stack?
I recently posted proposing a feature such that SECURITY DEFINER
functions could observe the _caller_'s current_user.
Nico
--
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
@ 2017-10-27 05:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2017-10-27 05:08 UTC (permalink / raw)
To: Nico Williams <nico@cryptonector.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi
2017-10-27 0:07 GMT+02:00 Nico Williams <nico@cryptonector.com>:
> On Thu, Oct 26, 2017 at 09:21:24AM +0200, Pavel Stehule wrote:
> > Comments, notes?
>
> I like it.
>
> I would further like to move all of postgresql.conf into the database,
> as much as possible, as well as pg_ident.conf and pg_hba.conf.
>
> Variables like current_user have a sort of nesting context
> functionality: calling a SECURITY DEFINER function "pushes" a new value
> onto current_user, then when the function returns the new value of
> current_user is "popped" and the previous value restored.
>
My proposal doesn't expecting with nesting, because there is only one scope
- schema / session - but I don't think so it is necessary
current_user is a function - it is based on parser magic in Postgres. The
origin from Oracle uses the feature of ADA language. When function has no
parameters then parenthesis are optional. So current_user, current_time are
functions current_user(), current_time().
> It might be nice to be able to generalize this.
>
> Questions that then arise:
>
> - can one see up the stack?
> - are there permissions issues with seeing up the stack?
>
these variables are pined to schema - so there is not any relation to
stack. It is like global variables.
Theoretically we can introduce "functional" variables, where the value is
based on immediate evaluation of expression. It can be very similar to
current current_user.
>
>
> I recently posted proposing a feature such that SECURITY DEFINER
> functions could observe the _caller_'s current_user.
>
your use case is good example - this proposed feature doesn't depend on
stack, depends on security context (security context stack) what is super
set of call stack
Regards
Pavel
> Nico
> --
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-10-30 21:42 ` srielau <serge@rielau.com>
2017-10-31 20:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: srielau @ 2017-10-30 21:42 UTC (permalink / raw)
To: pgsql-hackers@postgresql.org
Pavel,
I wouldn't put in the DROP option.
Or at least not in that form of syntax.
By convention CREATE persists DDL and makes object definitions visible
across sessions.
DECLARE defines session private objects which cannot collide with other
sessions.
If you want variables with a short lifetime that get dropped at the end of
the transaction that by definition would imply a session private object. So
it ought to be DECLARE'd.
As far as I can see PG has been following this practice so far.
Cheers
Serge Rielau
Salesforce.com
--
Sent from: http://www.postgresql-archive.org/PostgreSQL-hackers-f1928748.html
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
@ 2017-10-31 20:33 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:08 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2017-10-31 20:33 UTC (permalink / raw)
To: srielau <serge@rielau.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi
2017-10-30 22:42 GMT+01:00 srielau <serge@rielau.com>:
> Pavel,
>
> I wouldn't put in the DROP option.
> Or at least not in that form of syntax.
>
> By convention CREATE persists DDL and makes object definitions visible
> across sessions.
> DECLARE defines session private objects which cannot collide with other
> sessions.
>
> If you want variables with a short lifetime that get dropped at the end of
> the transaction that by definition would imply a session private object. So
> it ought to be DECLARE'd.
>
> As far as I can see PG has been following this practice so far.
>
I am thinking so there is little bit overlap between DECLARE and CREATE
TEMP VARIABLE command. With DECLARE command, you are usually has not any
control when variable will be destroyed. For CREATE TEMP xxxx is DROP IF
EXISTS, but it should not be used.
It should be very similar to our current temporary tables, that are created
in session related temp schema.
I can imagine, so DECLARE command will be introduced as short cut for
CREATE TEMP VARIABLE, but in this moment I would not to open this topic. I
afraid of bikeshedding and I hope so CREATE TEMP VAR is anough.
Regards
Pavel
> Cheers
> Serge Rielau
> Salesforce.com
>
>
>
> --
> Sent from: http://www.postgresql-archive.org/PostgreSQL-hackers-
> f1928748.html
>
>
> --
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-10-31 20:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-10-31 21:08 ` Serge Rielau <serge@rielau.com>
2017-10-31 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Serge Rielau @ 2017-10-31 21:08 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Pavel, I can imagine, so DECLARE command will be introduced as short cut
for CREATE TEMP VARIABLE, but in this moment I would not to open this
topic. I afraid of bikeshedding and I hope so CREATE TEMP VAR is anough.
Language is important because language stays. You choice of syntax will
outlive your code and possibly yourself.
My 2 cents Serge
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-10-31 20:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:08 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
@ 2017-10-31 21:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:28 ` Re: proposal: schema variables srielau <serge@rielau.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2017-10-31 21:10 UTC (permalink / raw)
To: Serge Rielau <serge@rielau.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2017-10-31 22:08 GMT+01:00 Serge Rielau <serge@rielau.com>:
> Pavel,
>
> I can imagine, so DECLARE command will be introduced as short cut for
> CREATE TEMP VARIABLE, but in this moment I would not to open this topic. I
> afraid of bikeshedding and I hope so CREATE TEMP VAR is anough.
>
> Language is important because language stays.
> You choice of syntax will outlive your code and possibly yourself.
>
sure. But in this moment I don't see difference between DECLARE VARIABLE
and CREATE TEMP VARIABLE different than "TEMP" keyword.
Regards
Pavel
> My 2 cents
> Serge
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-10-31 20:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:08 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
2017-10-31 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-10-31 21:28 ` srielau <serge@rielau.com>
2017-10-31 22:36 ` Re: proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2017-11-01 04:15 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 2 replies; 433+ messages in thread
From: srielau @ 2017-10-31 21:28 UTC (permalink / raw)
To: pgsql-hackers@postgresql.org
Pavel,
There is no
DECLARE TEMP CURSOR
or
DECLARE TEMP variable in PLpgSQL
and
CREATE TEMP TABLE has a different meaning from what I understand you
envision for variables.
But maybe I'm mistaken. Your original post did not describe the entire
syntax:
CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
[ DEFAULT expression ] [[NOT] NULL]
[ ON TRANSACTION END { RESET | DROP } ]
[ { VOLATILE | STABLE } ];
Especially the TEMP is not spelled out and how its presence affects or
doesn't ON TRANSACTION END.
So may be if you elaborate I understand where you are coming from.
--
Sent from: http://www.postgresql-archive.org/PostgreSQL-hackers-f1928748.html
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-10-31 20:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:08 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
2017-10-31 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:28 ` Re: proposal: schema variables srielau <serge@rielau.com>
@ 2017-10-31 22:36 ` Gilles Darold <gilles.darold@dalibo.com>
2017-10-31 23:02 ` Re: proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
1 sibling, 1 reply; 433+ messages in thread
From: Gilles Darold @ 2017-10-31 22:36 UTC (permalink / raw)
To: pgsql-hackers@postgresql.org
Le 31/10/2017 à 22:28, srielau a écrit :
> Pavel,
>
> There is no
> DECLARE TEMP CURSOR
> or
> DECLARE TEMP variable in PLpgSQL
> and
> CREATE TEMP TABLE has a different meaning from what I understand you
> envision for variables.
>
> But maybe I'm mistaken. Your original post did not describe the entire
> syntax:
> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
> [ DEFAULT expression ] [[NOT] NULL]
> [ ON TRANSACTION END { RESET | DROP } ]
> [ { VOLATILE | STABLE } ];
>
> Especially the TEMP is not spelled out and how its presence affects or
> doesn't ON TRANSACTION END.
> So may be if you elaborate I understand where you are coming from.
I think that the TEMP keyword can be removed. If I understand well the
default scope for variable is the session, every transaction in a
session will see the same value. For the transaction level, probably the
reason of the TEMP keyword, I think the [ ON TRANSACTION END { RESET |
DROP } ] will allow to restrict the scope to this transaction level
without needing the TEMP keyword. When a variable is created in a
transaction, it is temporary if "ON TRANSACTION END DROP" is used
otherwise it will persist after the transaction end. I guess that this
is the same as using TEMP keyword?
--
Gilles Darold
Consultant PostgreSQL
http://dalibo.com - http://dalibo.org
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-10-31 20:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:08 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
2017-10-31 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:28 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-10-31 22:36 ` Re: proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
@ 2017-10-31 23:02 ` Gilles Darold <gilles.darold@dalibo.com>
0 siblings, 0 replies; 433+ messages in thread
From: Gilles Darold @ 2017-10-31 23:02 UTC (permalink / raw)
To: pgsql-hackers@postgresql.org
Le 31/10/2017 à 23:36, Gilles Darold a écrit :
> Le 31/10/2017 à 22:28, srielau a écrit :
>> Pavel,
>>
>> There is no
>> DECLARE TEMP CURSOR
>> or
>> DECLARE TEMP variable in PLpgSQL
>> and
>> CREATE TEMP TABLE has a different meaning from what I understand you
>> envision for variables.
>>
>> But maybe I'm mistaken. Your original post did not describe the entire
>> syntax:
>> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
>> [ DEFAULT expression ] [[NOT] NULL]
>> [ ON TRANSACTION END { RESET | DROP } ]
>> [ { VOLATILE | STABLE } ];
>>
>> Especially the TEMP is not spelled out and how its presence affects or
>> doesn't ON TRANSACTION END.
>> So may be if you elaborate I understand where you are coming from.
> I think that the TEMP keyword can be removed. If I understand well the
> default scope for variable is the session, every transaction in a
> session will see the same value. For the transaction level, probably the
> reason of the TEMP keyword, I think the [ ON TRANSACTION END { RESET |
> DROP } ] will allow to restrict the scope to this transaction level
> without needing the TEMP keyword. When a variable is created in a
> transaction, it is temporary if "ON TRANSACTION END DROP" is used
> otherwise it will persist after the transaction end. I guess that this
> is the same as using TEMP keyword?
I forgot to say that in the last case the DECLARE statement can be used
so I don't see the reason of this kind of "temporary" variables.
Maybe the variable object like used in DB2 and defined in document :
https://www.ibm.com/support/knowledgecenter/en/SSEPEK_11.0.0/sqlref/src/tpc/db2z_sql_createvariable....
could be enough to cover our needs.
--
Gilles Darold
Consultant PostgreSQL
http://dalibo.com - http://dalibo.org
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-10-31 20:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:08 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
2017-10-31 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:28 ` Re: proposal: schema variables srielau <serge@rielau.com>
@ 2017-11-01 04:15 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-11-01 05:07 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
2017-11-01 22:13 ` Re: proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
1 sibling, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2017-11-01 04:15 UTC (permalink / raw)
To: srielau <serge@rielau.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2017-10-31 22:28 GMT+01:00 srielau <serge@rielau.com>:
> Pavel,
>
> There is no
> DECLARE TEMP CURSOR
> or
> DECLARE TEMP variable in PLpgSQL
> and
>
sure .. DECLARE TEMP has no sense, I talked about similarity DECLARE and
CREATE TEMP
CREATE TEMP TABLE has a different meaning from what I understand you
> envision for variables.
>
> But maybe I'm mistaken. Your original post did not describe the entire
> syntax:
> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
> [ DEFAULT expression ] [[NOT] NULL]
> [ ON TRANSACTION END { RESET | DROP } ]
> [ { VOLATILE | STABLE } ];
>
> Especially the TEMP is not spelled out and how its presence affects or
> doesn't ON TRANSACTION END.
> So may be if you elaborate I understand where you are coming from.
>
TEMP has same functionality (and implementation) like our temp tables - so
at session end the temp variables are destroyed, but it can be assigned to
transaction.
>
>
>
>
> --
> Sent from: http://www.postgresql-archive.org/PostgreSQL-hackers-
> f1928748.html
>
>
> --
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-10-31 20:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:08 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
2017-10-31 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:28 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-11-01 04:15 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-11-01 05:07 ` Serge Rielau <serge@rielau.com>
2017-11-01 05:56 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Serge Rielau @ 2017-11-01 05:07 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
" Although the syntax of CREATE TEMPORARY TABLE resembles that of the SQL standard, the effect is not the same. In the standard, temporary tables are defined just once and automatically exist (starting with empty contents) in every session that needs them. PostgreSQL instead requires each session to issue its own CREATE TEMPORARY TABLE command for each temporary table to be used. This allows different sessions to use the same temporary table name for different purposes, whereas the standard's approach constrains all instances of a given temporary table name to have the same table structure.”
Yeah, that’s a DECLAREd table in my book. No wonder we didn’t link up.
Cheers Serge
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-10-31 20:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:08 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
2017-10-31 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:28 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-11-01 04:15 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-01 05:07 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
@ 2017-11-01 05:56 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2017-11-01 05:56 UTC (permalink / raw)
To: Serge Rielau <serge@rielau.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2017-11-01 6:07 GMT+01:00 Serge Rielau <serge@rielau.com>:
> "Although the syntax of CREATE TEMPORARY TABLE resembles that of the SQL
> standard, the effect is not the same. In the standard, temporary tables are
> defined just once and automatically exist (starting with empty contents) in
> every session that needs them. PostgreSQL instead requires each session
> to issue its own CREATE TEMPORARY TABLE command for each temporary table
> to be used. This allows different sessions to use the same temporary table
> name for different purposes, whereas the standard's approach constrains all
> instances of a given temporary table name to have the same table structure.”
> Yeah, that’s a DECLAREd table in my book. No wonder we didn’t link up.
>
This is known discussion about local / global temp tables in PostgresSQL.
And ToDo point: implementation of global temp tables in Postgres.
This temporary behave is marginal part of proposal - so I can to remove it
from proposal - and later open discussion about CREATE TEMPORARY VARIABLE
versus DECLARE VARIABLE
Regards
Pavel
Serge
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-10-31 20:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:08 ` Re: proposal: schema variables Serge Rielau <serge@rielau.com>
2017-10-31 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:28 ` Re: proposal: schema variables srielau <serge@rielau.com>
2017-11-01 04:15 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-11-01 22:13 ` Gilles Darold <gilles.darold@dalibo.com>
1 sibling, 0 replies; 433+ messages in thread
From: Gilles Darold @ 2017-11-01 22:13 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Le 01/11/2017 à 05:15, Pavel Stehule a écrit :
>
>
> 2017-10-31 22:28 GMT+01:00 srielau <serge@rielau.com
> <mailto:serge@rielau.com>>:
>
> Pavel,
>
> There is no
> DECLARE TEMP CURSOR
> or
> DECLARE TEMP variable in PLpgSQL
> and
>
>
> sure .. DECLARE TEMP has no sense, I talked about similarity DECLARE
> and CREATE TEMP
>
>
> CREATE TEMP TABLE has a different meaning from what I understand you
> envision for variables.
>
> But maybe I'm mistaken. Your original post did not describe the entire
> syntax:
> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
> [ DEFAULT expression ] [[NOT] NULL]
> [ ON TRANSACTION END { RESET | DROP } ]
> [ { VOLATILE | STABLE } ];
>
> Especially the TEMP is not spelled out and how its presence affects or
> doesn't ON TRANSACTION END.
> So may be if you elaborate I understand where you are coming from.
>
>
> TEMP has same functionality (and implementation) like our temp tables
> - so at session end the temp variables are destroyed, but it can be
> assigned to transaction.
Oh ok, I understand thanks for the precision.
--
Gilles Darold
Consultant PostgreSQL
http://dalibo.com - http://dalibo.org
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-10-27 05:30 ` Tatsuo Ishii <ishii@sraoss.co.jp>
9 siblings, 0 replies; 433+ messages in thread
From: Tatsuo Ishii @ 2017-10-27 05:30 UTC (permalink / raw)
To: pavel.stehule@gmail.com; +Cc: pgsql-hackers@postgresql.org
> Hi,
>
> I propose a new database object - a variable. The variable is persistent
> object, that holds unshared session based not transactional in memory value
> of any type. Like variables in any other languages. The persistence is
> required for possibility to do static checks, but can be limited to session
> - the variables can be temporal.
>
> My proposal is related to session variables from Sybase, MSSQL or MySQL
> (based on prefix usage @ or @@), or package variables from Oracle (access
> is controlled by scope), or schema variables from DB2. Any design is coming
> from different sources, traditions and has some advantages or
> disadvantages. The base of my proposal is usage schema variables as session
> variables for stored procedures. It should to help to people who try to
> port complex projects to PostgreSQL from other databases.
>
> The Sybase (T-SQL) design is good for interactive work, but it is weak for
> usage in stored procedures - the static check is not possible. Is not
> possible to set some access rights on variables.
>
> The ADA design (used on Oracle) based on scope is great, but our
> environment is not nested. And we should to support other PL than PLpgSQL
> more strongly.
>
> There is not too much other possibilities - the variable that should be
> accessed from different PL, different procedures (in time) should to live
> somewhere over PL, and there is the schema only.
>
> The variable can be created by CREATE statement:
>
> CREATE VARIABLE public.myvar AS integer;
> CREATE VARIABLE myschema.myvar AS mytype;
>
> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
> [ DEFAULT expression ] [[NOT] NULL]
> [ ON TRANSACTION END { RESET | DROP } ]
> [ { VOLATILE | STABLE } ];
>
> It is dropped by command DROP VARIABLE [ IF EXISTS] varname.
>
> The access rights is controlled by usual access rights - by commands
> GRANT/REVOKE. The possible rights are: READ, WRITE
>
> The variables can be modified by SQL command SET (this is taken from
> standard, and it natural)
>
> SET varname = expression;
>
> Unfortunately we use the SET command for different purpose. But I am
> thinking so we can solve it with few tricks. The first is moving our GUC to
> pg_catalog schema. We can control the strictness of SET command. In one
> variant, we can detect custom GUC and allow it, in another we can disallow
> a custom GUC and allow only schema variables. A new command LET can be
> alternative.
>
> The variables should be used in queries implicitly (without JOIN)
>
> SELECT varname;
>
> The SEARCH_PATH is used, when varname is located. The variables can be used
> everywhere where query parameters are allowed.
>
> I hope so this proposal is good enough and simple.
>
> Comments, notes?
Just q quick follow up. Looks like a greate feature!
Best regards,
--
Tatsuo Ishii
SRA OSS, Inc. Japan
English: http://www.sraoss.co.jp/index_en.php
Japanese:http://www.sraoss.co.jp
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-10-27 05:47 ` Tsunakawa, Takayuki <tsunakawa.takay@jp.fujitsu.com>
2017-10-27 06:16 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
9 siblings, 1 reply; 433+ messages in thread
From: Tsunakawa, Takayuki @ 2017-10-27 05:47 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
From: pgsql-hackers-owner@postgresql.org
> [mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Pavel Stehule
> I propose a new database object - a variable. The variable is persistent
> object, that holds unshared session based not transactional in memory value
> of any type. Like variables in any other languages. The persistence is
> required for possibility to do static checks, but can be limited to session
> - the variables can be temporal.
>
>
> My proposal is related to session variables from Sybase, MSSQL or MySQL
> (based on prefix usage @ or @@), or package variables from Oracle (access
> is controlled by scope), or schema variables from DB2. Any design is coming
> from different sources, traditions and has some advantages or disadvantages.
> The base of my proposal is usage schema variables as session variables for
> stored procedures. It should to help to people who try to port complex
> projects to PostgreSQL from other databases.
Very interesting. I hope I could join the review and testing.
How do you think this would contribute to easing the port of Oracle PL/SQL procedures? Would the combination of orafce and this feature promote auto-translation of PL/SQL procedures? I'm curious what will be the major road blocks after adding the schema variable.
Regards
Takayuki Tsunakawa
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-27 05:47 ` Re: proposal: schema variables Tsunakawa, Takayuki <tsunakawa.takay@jp.fujitsu.com>
@ 2017-10-27 06:16 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2017-10-27 06:16 UTC (permalink / raw)
To: Tsunakawa, Takayuki <tsunakawa.takay@jp.fujitsu.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2017-10-27 7:47 GMT+02:00 Tsunakawa, Takayuki <
tsunakawa.takay@jp.fujitsu.com>:
> From: pgsql-hackers-owner@postgresql.org
> > [mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Pavel Stehule
> > I propose a new database object - a variable. The variable is persistent
> > object, that holds unshared session based not transactional in memory
> value
> > of any type. Like variables in any other languages. The persistence is
> > required for possibility to do static checks, but can be limited to
> session
> > - the variables can be temporal.
> >
> >
> > My proposal is related to session variables from Sybase, MSSQL or MySQL
> > (based on prefix usage @ or @@), or package variables from Oracle (access
> > is controlled by scope), or schema variables from DB2. Any design is
> coming
> > from different sources, traditions and has some advantages or
> disadvantages.
> > The base of my proposal is usage schema variables as session variables
> for
> > stored procedures. It should to help to people who try to port complex
> > projects to PostgreSQL from other databases.
>
> Very interesting. I hope I could join the review and testing.
>
you are welcome. I wrote a prototype last year based on envelope functions.
But the integration must be much more close to SQL to be some clear benefit
of this feature. So there is lot of work. I hope so I have a prototype
after this winter. It is my plan for winter.
>
> How do you think this would contribute to easing the port of Oracle PL/SQL
> procedures? Would the combination of orafce and this feature promote
> auto-translation of PL/SQL procedures? I'm curious what will be the major
> road blocks after adding the schema variable.
>
It depends on creativity of PL/SQL developers. Usual .. 80% application is
possible to migrate with current GUC - some work does ora2pg. But GUC is
little bit slower (not too important) and is not simple possibility to
secure it.
So work with variables will be similar like GUC, but significantly more
natural (not necessary to build wrap functions). It should be much better
when value is of some composite type. The migrations will need some
inteligence still, but less work and code will be more readable and cleaner.
I talked already about "schema pined" functions (schema private/public
objects) - but I didn't think about it more deeply. There can be special
access right to schema variables, the pined schema can be preferred before
search_path. With this feature the schema will have very similar behave
like Oracle Modules. Using different words - we can implement scope access
rights based on schemas. But it is far horizon. What is important -
proposal doesn't block any future enhancing in this case, and is consistent
with current state. In future you can work with schema private functions,
tables, variables, sequences. So variables are nothing special.
Regards
Pavel
Regards
> Takayuki Tsunakawa
>
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-10-27 13:38 ` Gilles Darold <gilles.darold@dalibo.com>
2017-10-27 14:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
9 siblings, 1 reply; 433+ messages in thread
From: Gilles Darold @ 2017-10-27 13:38 UTC (permalink / raw)
To: pgsql-hackers@postgresql.org
Le 26/10/2017 à 09:21, Pavel Stehule a écrit :
> Hi,
>
> I propose a new database object - a variable. The variable is
> persistent object, that holds unshared session based not transactional
> in memory value of any type. Like variables in any other languages.
> The persistence is required for possibility to do static checks, but
> can be limited to session - the variables can be temporal.
>
> My proposal is related to session variables from Sybase, MSSQL or
> MySQL (based on prefix usage @ or @@), or package variables from
> Oracle (access is controlled by scope), or schema variables from DB2.
> Any design is coming from different sources, traditions and has some
> advantages or disadvantages. The base of my proposal is usage schema
> variables as session variables for stored procedures. It should to
> help to people who try to port complex projects to PostgreSQL from
> other databases.
>
> The Sybase (T-SQL) design is good for interactive work, but it is
> weak for usage in stored procedures - the static check is not
> possible. Is not possible to set some access rights on variables.
>
> The ADA design (used on Oracle) based on scope is great, but our
> environment is not nested. And we should to support other PL than
> PLpgSQL more strongly.
>
> There is not too much other possibilities - the variable that should
> be accessed from different PL, different procedures (in time) should
> to live somewhere over PL, and there is the schema only.
>
> The variable can be created by CREATE statement:
>
> CREATE VARIABLE public.myvar AS integer;
> CREATE VARIABLE myschema.myvar AS mytype;
>
> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
> [ DEFAULT expression ] [[NOT] NULL]
> [ ON TRANSACTION END { RESET | DROP } ]
> [ { VOLATILE | STABLE } ];
>
> It is dropped by command DROP VARIABLE [ IF EXISTS] varname.
>
> The access rights is controlled by usual access rights - by commands
> GRANT/REVOKE. The possible rights are: READ, WRITE
>
> The variables can be modified by SQL command SET (this is taken from
> standard, and it natural)
>
> SET varname = expression;
>
> Unfortunately we use the SET command for different purpose. But I am
> thinking so we can solve it with few tricks. The first is moving our
> GUC to pg_catalog schema. We can control the strictness of SET
> command. In one variant, we can detect custom GUC and allow it, in
> another we can disallow a custom GUC and allow only schema variables.
> A new command LET can be alternative.
>
> The variables should be used in queries implicitly (without JOIN)
>
> SELECT varname;
>
> The SEARCH_PATH is used, when varname is located. The variables can be
> used everywhere where query parameters are allowed.
>
> I hope so this proposal is good enough and simple.
>
> Comments, notes?
>
> regards
>
> Pavel
>
>
Great feature that will help for migration. How will you handle CONSTANT
declaration? With Oracle it is possible to declare a constant as follow:
varname CONSTANT INTEGER := 500;
for a variable that can't be changed. Do you plan to add a CONSTANT or
READONLY keyword or do you want use GRANT on the object to deal with
this case?
Regards
--
Gilles Darold
Consultant PostgreSQL
http://dalibo.com - http://dalibo.org
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-27 13:38 ` Re: proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
@ 2017-10-27 14:09 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2017-10-27 14:09 UTC (permalink / raw)
To: Gilles Darold <gilles.darold@dalibo.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2017-10-27 15:38 GMT+02:00 Gilles Darold <gilles.darold@dalibo.com>:
> Le 26/10/2017 à 09:21, Pavel Stehule a écrit :
> > Hi,
> >
> > I propose a new database object - a variable. The variable is
> > persistent object, that holds unshared session based not transactional
> > in memory value of any type. Like variables in any other languages.
> > The persistence is required for possibility to do static checks, but
> > can be limited to session - the variables can be temporal.
> >
> > My proposal is related to session variables from Sybase, MSSQL or
> > MySQL (based on prefix usage @ or @@), or package variables from
> > Oracle (access is controlled by scope), or schema variables from DB2.
> > Any design is coming from different sources, traditions and has some
> > advantages or disadvantages. The base of my proposal is usage schema
> > variables as session variables for stored procedures. It should to
> > help to people who try to port complex projects to PostgreSQL from
> > other databases.
> >
> > The Sybase (T-SQL) design is good for interactive work, but it is
> > weak for usage in stored procedures - the static check is not
> > possible. Is not possible to set some access rights on variables.
> >
> > The ADA design (used on Oracle) based on scope is great, but our
> > environment is not nested. And we should to support other PL than
> > PLpgSQL more strongly.
> >
> > There is not too much other possibilities - the variable that should
> > be accessed from different PL, different procedures (in time) should
> > to live somewhere over PL, and there is the schema only.
> >
> > The variable can be created by CREATE statement:
> >
> > CREATE VARIABLE public.myvar AS integer;
> > CREATE VARIABLE myschema.myvar AS mytype;
> >
> > CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
> > [ DEFAULT expression ] [[NOT] NULL]
> > [ ON TRANSACTION END { RESET | DROP } ]
> > [ { VOLATILE | STABLE } ];
> >
> > It is dropped by command DROP VARIABLE [ IF EXISTS] varname.
> >
> > The access rights is controlled by usual access rights - by commands
> > GRANT/REVOKE. The possible rights are: READ, WRITE
> >
> > The variables can be modified by SQL command SET (this is taken from
> > standard, and it natural)
> >
> > SET varname = expression;
> >
> > Unfortunately we use the SET command for different purpose. But I am
> > thinking so we can solve it with few tricks. The first is moving our
> > GUC to pg_catalog schema. We can control the strictness of SET
> > command. In one variant, we can detect custom GUC and allow it, in
> > another we can disallow a custom GUC and allow only schema variables.
> > A new command LET can be alternative.
> >
> > The variables should be used in queries implicitly (without JOIN)
> >
> > SELECT varname;
> >
> > The SEARCH_PATH is used, when varname is located. The variables can be
> > used everywhere where query parameters are allowed.
> >
> > I hope so this proposal is good enough and simple.
> >
> > Comments, notes?
> >
> > regards
> >
> > Pavel
> >
> >
>
> Great feature that will help for migration. How will you handle CONSTANT
> declaration? With Oracle it is possible to declare a constant as follow:
>
>
> varname CONSTANT INTEGER := 500;
>
>
> for a variable that can't be changed. Do you plan to add a CONSTANT or
> READONLY keyword or do you want use GRANT on the object to deal with
> this case?
>
Plpgsql declaration supports CONSTANT
I forgot it. Thank you
Pavel
>
> Regards
>
> --
> Gilles Darold
> Consultant PostgreSQL
> http://dalibo.com - http://dalibo.org
>
>
>
>
> --
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-10-28 14:24 ` Chris Travers <chris.travers@adjust.com>
2017-10-28 14:56 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
9 siblings, 1 reply; 433+ messages in thread
From: Chris Travers @ 2017-10-28 14:24 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
On Thu, Oct 26, 2017 at 9:21 AM, Pavel Stehule <pavel.stehule@gmail.com>
wrote:
> Hi,
>
> I propose a new database object - a variable. The variable is persistent
> object, that holds unshared session based not transactional in memory value
> of any type. Like variables in any other languages. The persistence is
> required for possibility to do static checks, but can be limited to session
> - the variables can be temporal.
>
> My proposal is related to session variables from Sybase, MSSQL or MySQL
> (based on prefix usage @ or @@), or package variables from Oracle (access
> is controlled by scope), or schema variables from DB2. Any design is coming
> from different sources, traditions and has some advantages or
> disadvantages. The base of my proposal is usage schema variables as session
> variables for stored procedures. It should to help to people who try to
> port complex projects to PostgreSQL from other databases.
>
> The Sybase (T-SQL) design is good for interactive work, but it is weak
> for usage in stored procedures - the static check is not possible. Is not
> possible to set some access rights on variables.
>
> The ADA design (used on Oracle) based on scope is great, but our
> environment is not nested. And we should to support other PL than PLpgSQL
> more strongly.
>
> There is not too much other possibilities - the variable that should be
> accessed from different PL, different procedures (in time) should to live
> somewhere over PL, and there is the schema only.
>
> The variable can be created by CREATE statement:
>
> CREATE VARIABLE public.myvar AS integer;
> CREATE VARIABLE myschema.myvar AS mytype;
>
> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
> [ DEFAULT expression ] [[NOT] NULL]
> [ ON TRANSACTION END { RESET | DROP } ]
> [ { VOLATILE | STABLE } ];
>
> It is dropped by command DROP VARIABLE [ IF EXISTS] varname.
>
> The access rights is controlled by usual access rights - by commands
> GRANT/REVOKE. The possible rights are: READ, WRITE
>
> The variables can be modified by SQL command SET (this is taken from
> standard, and it natural)
>
> SET varname = expression;
>
> Unfortunately we use the SET command for different purpose. But I am
> thinking so we can solve it with few tricks. The first is moving our GUC to
> pg_catalog schema. We can control the strictness of SET command. In one
> variant, we can detect custom GUC and allow it, in another we can disallow
> a custom GUC and allow only schema variables. A new command LET can be
> alternative.
>
> The variables should be used in queries implicitly (without JOIN)
>
> SELECT varname;
>
> The SEARCH_PATH is used, when varname is located. The variables can be
> used everywhere where query parameters are allowed.
>
> I hope so this proposal is good enough and simple.
>
> Comments, notes?
>
I have a question on this. Since one can issue set commands on arbitrary
settings (and later ALTER database/role/system on settings you have created
in the current session) I am wondering how much overlap there is between a
sort of extended GUC with custom settings and variables.
Maybe it would be simpler to treat variables and GUC settings to be similar
and see what can be done to extend GUC in this way?
I mean if instead we allowed restricting SET to known settings then we
could have a CREATE SETTING command which would behave like this and then
use SET the same way across both.
In essence I am wondering if this really needs to be as separate from GUC
as you are proposing.
If done this way then:
1. You could issue grant or revoke on GUC settings, allowing some users
but not others to set things like work_mem for their queries
2. You could specify allowed types in custom settings.
3. In a subsequent stage you might be able to SELECT .... INTO
setting_name FROM ....; allowing access to setting writes based on queries.
> regards
>
> Pavel
>
>
>
--
Best Regards,
Chris Travers
Database Administrator
Tel: +49 162 9037 210 | Skype: einhverfr | www.adjust.com
Saarbrücker Straße 37a, 10405 Berlin
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-28 14:24 ` Re: proposal: schema variables Chris Travers <chris.travers@adjust.com>
@ 2017-10-28 14:56 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-10-29 08:51 ` Re: proposal: schema variables Chris Travers <chris.travers@adjust.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2017-10-28 14:56 UTC (permalink / raw)
To: Chris Travers <chris.travers@adjust.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi
2017-10-28 16:24 GMT+02:00 Chris Travers <chris.travers@adjust.com>:
>
>
> On Thu, Oct 26, 2017 at 9:21 AM, Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
>
>> Hi,
>>
>> I propose a new database object - a variable. The variable is persistent
>> object, that holds unshared session based not transactional in memory value
>> of any type. Like variables in any other languages. The persistence is
>> required for possibility to do static checks, but can be limited to session
>> - the variables can be temporal.
>>
>> My proposal is related to session variables from Sybase, MSSQL or MySQL
>> (based on prefix usage @ or @@), or package variables from Oracle (access
>> is controlled by scope), or schema variables from DB2. Any design is coming
>> from different sources, traditions and has some advantages or
>> disadvantages. The base of my proposal is usage schema variables as session
>> variables for stored procedures. It should to help to people who try to
>> port complex projects to PostgreSQL from other databases.
>>
>> The Sybase (T-SQL) design is good for interactive work, but it is weak
>> for usage in stored procedures - the static check is not possible. Is not
>> possible to set some access rights on variables.
>>
>> The ADA design (used on Oracle) based on scope is great, but our
>> environment is not nested. And we should to support other PL than PLpgSQL
>> more strongly.
>>
>> There is not too much other possibilities - the variable that should be
>> accessed from different PL, different procedures (in time) should to live
>> somewhere over PL, and there is the schema only.
>>
>> The variable can be created by CREATE statement:
>>
>> CREATE VARIABLE public.myvar AS integer;
>> CREATE VARIABLE myschema.myvar AS mytype;
>>
>> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
>> [ DEFAULT expression ] [[NOT] NULL]
>> [ ON TRANSACTION END { RESET | DROP } ]
>> [ { VOLATILE | STABLE } ];
>>
>> It is dropped by command DROP VARIABLE [ IF EXISTS] varname.
>>
>> The access rights is controlled by usual access rights - by commands
>> GRANT/REVOKE. The possible rights are: READ, WRITE
>>
>> The variables can be modified by SQL command SET (this is taken from
>> standard, and it natural)
>>
>> SET varname = expression;
>>
>> Unfortunately we use the SET command for different purpose. But I am
>> thinking so we can solve it with few tricks. The first is moving our GUC to
>> pg_catalog schema. We can control the strictness of SET command. In one
>> variant, we can detect custom GUC and allow it, in another we can disallow
>> a custom GUC and allow only schema variables. A new command LET can be
>> alternative.
>>
>> The variables should be used in queries implicitly (without JOIN)
>>
>> SELECT varname;
>>
>> The SEARCH_PATH is used, when varname is located. The variables can be
>> used everywhere where query parameters are allowed.
>>
>> I hope so this proposal is good enough and simple.
>>
>> Comments, notes?
>>
>
>
> I have a question on this. Since one can issue set commands on arbitrary
> settings (and later ALTER database/role/system on settings you have created
> in the current session) I am wondering how much overlap there is between a
> sort of extended GUC with custom settings and variables.
>
> Maybe it would be simpler to treat variables and GUC settings to be
> similar and see what can be done to extend GUC in this way?
>
> I mean if instead we allowed restricting SET to known settings then we
> could have a CREATE SETTING command which would behave like this and then
> use SET the same way across both.
>
> In essence I am wondering if this really needs to be as separate from GUC
> as you are proposing.
>
> If done this way then:
>
> 1. You could issue grant or revoke on GUC settings, allowing some users
> but not others to set things like work_mem for their queries
> 2. You could specify allowed types in custom settings.
> 3. In a subsequent stage you might be able to SELECT .... INTO
> setting_name FROM ....; allowing access to setting writes based on queries.
>
>
The creating database objects and necessary infrastructure is the most
simple task of this project. I'll be more happy if there are zero
intersection because variables and GUC are designed for different purposes.
But due SET keyword the intersection there is.
When I thinking about it, I have only one, but important reason, why I
prefer design new type of database object -the GUC are stack based with
different default granularity - global, database, user, session, function.
This can be unwanted behave for variables - it can be source of hard to
detected bugs. I afraid so this behave can be too messy for usage as
variables.
@1 I have not clean opinion about it - not sure if rights are good enough -
probably some user limits can be more practical - but can be hard to choose
result when some user limits and GUC will be against
@2 With variables typed custom GUC are not necessary
@3 Why you need it? It is possible with set_config function now.
Regards
Pavel
>
>
>> regards
>>
>> Pavel
>>
>>
>>
>
>
> --
> Best Regards,
> Chris Travers
> Database Administrator
>
> Tel: +49 162 9037 210 <+49%20162%209037210> | Skype: einhverfr |
> www.adjust.com
> Saarbrücker Straße 37a, 10405 Berlin
> <https://maps.google.com/?q=Saarbr%C3%BCcker+Stra%C3%9Fe+37a,+10405+Berlin&entry=gmail&source...;
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-28 14:24 ` Re: proposal: schema variables Chris Travers <chris.travers@adjust.com>
2017-10-28 14:56 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-10-29 08:51 ` Chris Travers <chris.travers@adjust.com>
2017-10-29 10:47 ` Re: proposal: schema variables Hannu Krosing <hannu.krosing@2ndquadrant.com>
0 siblings, 1 reply; 433+ messages in thread
From: Chris Travers @ 2017-10-29 08:51 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
On Sat, Oct 28, 2017 at 4:56 PM, Pavel Stehule <pavel.stehule@gmail.com>
wrote:
>
>>
> The creating database objects and necessary infrastructure is the most
> simple task of this project. I'll be more happy if there are zero
> intersection because variables and GUC are designed for different purposes.
> But due SET keyword the intersection there is.
>
> When I thinking about it, I have only one, but important reason, why I
> prefer design new type of database object -the GUC are stack based with
> different default granularity - global, database, user, session, function.
> This can be unwanted behave for variables - it can be source of hard to
> detected bugs. I afraid so this behave can be too messy for usage as
> variables.
>
> @1 I have not clean opinion about it - not sure if rights are good enough
> - probably some user limits can be more practical - but can be hard to
> choose result when some user limits and GUC will be against
>
I was mostly thinking that users can probably set things like work_mem and
possibly this might be a problem.
> @2 With variables typed custom GUC are not necessary
>
I don't know about that. For example with the geoip2lookup extension it is
nice that you could set the preferred language for translation on a per
user basis or the mmdb path on a per-db basis.
> @3 Why you need it? It is possible with set_config function now.
>
Yeah you could do it safely with set_config and a CTE, but suppose I have:
with a (Id, value) as (values (1::Int, 'foo'), (2, 'bar'), (3, 'baz'))
SELECT set_config('custom_val', value) from a where id = 2;
What is the result out of this? I would *expect* that this would probably
run set_config 3 times and filter the output.
>
> Regards
>
> Pavel
>
>
>
>
>>
>>
>>> regards
>>>
>>> Pavel
>>>
>>>
>>>
>>
>>
>> --
>> Best Regards,
>> Chris Travers
>> Database Administrator
>>
>> Tel: +49 162 9037 210 <+49%20162%209037210> | Skype: einhverfr |
>> www.adjust.com
>> Saarbrücker Straße 37a, 10405 Berlin
>> <https://maps.google.com/?q=Saarbr%C3%BCcker+Stra%C3%9Fe+37a,+10405+Berlin&entry=gmail&source...;
>>
>>
>
--
Best Regards,
Chris Travers
Database Administrator
Tel: +49 162 9037 210 | Skype: einhverfr | www.adjust.com
Saarbrücker Straße 37a, 10405 Berlin
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-28 14:24 ` Re: proposal: schema variables Chris Travers <chris.travers@adjust.com>
2017-10-28 14:56 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-29 08:51 ` Re: proposal: schema variables Chris Travers <chris.travers@adjust.com>
@ 2017-10-29 10:47 ` Hannu Krosing <hannu.krosing@2ndquadrant.com>
0 siblings, 0 replies; 433+ messages in thread
From: Hannu Krosing @ 2017-10-29 10:47 UTC (permalink / raw)
To: Chris Travers <chris.travers@adjust.com>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
but you can always do
with a (id, value) as (
values (1, 'foo'), (2, 'bar'), (3, 'baz')
)
select set_config('custom.value',(select value from a where id = 2),true);
if you are worried about the evaluation order
On 29 October 2017 at 09:51, Chris Travers <chris.travers@adjust.com> wrote:
>
>
> On Sat, Oct 28, 2017 at 4:56 PM, Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
>
>>
>>>
>> The creating database objects and necessary infrastructure is the most
>> simple task of this project. I'll be more happy if there are zero
>> intersection because variables and GUC are designed for different purposes.
>> But due SET keyword the intersection there is.
>>
>> When I thinking about it, I have only one, but important reason, why I
>> prefer design new type of database object -the GUC are stack based with
>> different default granularity - global, database, user, session, function.
>> This can be unwanted behave for variables - it can be source of hard to
>> detected bugs. I afraid so this behave can be too messy for usage as
>> variables.
>>
>> @1 I have not clean opinion about it - not sure if rights are good enough
>> - probably some user limits can be more practical - but can be hard to
>> choose result when some user limits and GUC will be against
>>
>
> I was mostly thinking that users can probably set things like work_mem and
> possibly this might be a problem.
>
>
>> @2 With variables typed custom GUC are not necessary
>>
>
> I don't know about that. For example with the geoip2lookup extension it
> is nice that you could set the preferred language for translation on a per
> user basis or the mmdb path on a per-db basis.
>
>
>> @3 Why you need it? It is possible with set_config function now.
>>
>
> Yeah you could do it safely with set_config and a CTE, but suppose I have:
>
> with a (Id, value) as (values (1::Int, 'foo'), (2, 'bar'), (3, 'baz'))
> SELECT set_config('custom_val', value) from a where id = 2;
>
> What is the result out of this? I would *expect* that this would probably
> run set_config 3 times and filter the output.
>
>
>>
>> Regards
>>
>> Pavel
>>
>>
>>
>>
>>>
>>>
>>>> regards
>>>>
>>>> Pavel
>>>>
>>>>
>>>>
>>>
>>>
>>> --
>>> Best Regards,
>>> Chris Travers
>>> Database Administrator
>>>
>>> Tel: +49 162 9037 210 <+49%20162%209037210> | Skype: einhverfr |
>>> www.adjust.com
>>> Saarbrücker Straße 37a, 10405 Berlin
>>> <https://maps.google.com/?q=Saarbr%C3%BCcker+Stra%C3%9Fe+37a,+10405+Berlin&entry=gmail&source...;
>>>
>>>
>>
>
>
> --
> Best Regards,
> Chris Travers
> Database Administrator
>
> Tel: +49 162 9037 210 <+49%20162%209037210> | Skype: einhverfr |
> www.adjust.com
> Saarbrücker Straße 37a, 10405 Berlin
> <https://maps.google.com/?q=Saarbr%C3%BCcker+Stra%C3%9Fe+37a,+10405+Berlin&entry=gmail&source...;
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-11-01 18:03 ` Mark Dilger <hornschnorter@gmail.com>
2017-11-01 19:19 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
9 siblings, 1 reply; 433+ messages in thread
From: Mark Dilger @ 2017-11-01 18:03 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
> Comments, notes?
How would variables behave on transaction rollback?
CREATE TEMP VARIABLE myvar;
SET myvar := 1;
BEGIN;
SET myvar := 2;
COMMIT;
BEGIN;
SET myvar := 3;
ROLLBACK;
SELECT myvar;
How would variables behave when modified in a procedure
that aborts rather than returning cleanly?
mark
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-01 18:03 ` Re: proposal: schema variables Mark Dilger <hornschnorter@gmail.com>
@ 2017-11-01 19:19 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2017-11-01 19:19 UTC (permalink / raw)
To: Mark Dilger <hornschnorter@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2017-11-01 19:03 GMT+01:00 Mark Dilger <hornschnorter@gmail.com>:
>
> > Comments, notes?
>
> How would variables behave on transaction rollback?
>
> CREATE TEMP VARIABLE myvar;
> SET myvar := 1;
> BEGIN;
> SET myvar := 2;
> COMMIT;
> BEGIN;
> SET myvar := 3;
> ROLLBACK;
> SELECT myvar;
>
> How would variables behave when modified in a procedure
> that aborts rather than returning cleanly?
>
>
The result is 3
When you create variable like you did, then there are not any relation
between variable content and transactions. Almost every where session -
package - schema variables are untransactional. It can be changed, but with
negative impact on performance - so I propose relative simply solution -
reset to default on rollback, when variables was changed in transaction -
but it is not default behave.
Variables are variables like you know from PlpgSQL. But the holder is not
the plpgsql function. The holder is a schema in this case. The variable
(meta) is permanent. The content of variable is session based
untransactional.
Regards
Pavel
> mark
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-11-02 12:35 ` Robert Haas <robertmhaas@gmail.com>
2017-11-02 15:35 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-11-02 15:49 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
9 siblings, 2 replies; 433+ messages in thread
From: Robert Haas @ 2017-11-02 12:35 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
On Thu, Oct 26, 2017 at 12:51 PM, Pavel Stehule <pavel.stehule@gmail.com> wrote:
> The variables can be modified by SQL command SET (this is taken from
> standard, and it natural)
>
> SET varname = expression;
Overloading SET to handle both variables and GUCs seems likely to
create problems, possibly including security problems. For example,
maybe a security-definer function could leave behind variables to
trick the calling code into failing to set GUCs that it intended to
set. Or maybe creating a variable at the wrong time will just break
things randomly.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 12:35 ` Re: proposal: schema variables Robert Haas <robertmhaas@gmail.com>
@ 2017-11-02 15:35 ` Nico Williams <nico@cryptonector.com>
2017-11-02 15:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 15:48 ` Re: proposal: schema variables Tom Lane <tgl@sss.pgh.pa.us>
2017-11-02 17:21 ` Re: proposal: schema variables Robert Haas <robertmhaas@gmail.com>
1 sibling, 3 replies; 433+ messages in thread
From: Nico Williams @ 2017-11-02 15:35 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
On Thu, Nov 02, 2017 at 06:05:54PM +0530, Robert Haas wrote:
> On Thu, Oct 26, 2017 at 12:51 PM, Pavel Stehule <pavel.stehule@gmail.com> wrote:
> > The variables can be modified by SQL command SET (this is taken from
> > standard, and it natural)
> >
> > SET varname = expression;
>
> Overloading SET to handle both variables and GUCs seems likely to
> create problems, possibly including security problems. For example,
> maybe a security-definer function could leave behind variables to
> trick the calling code into failing to set GUCs that it intended to
> set. Or maybe creating a variable at the wrong time will just break
> things randomly.
That's already true of GUCs, since there are no access controls on
set_config()/current_setting().
Presumably "schema variables" would really just be GUC-like and not at
all like lexically scoped variables. And also subject to access
controls, thus an overall improvement on set_config()/current_setting().
With access controls, GUCs could become schema variables, and settings
from postgresql.conf could move into the database itself (which I think
would be nice).
Nico
--
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 12:35 ` Re: proposal: schema variables Robert Haas <robertmhaas@gmail.com>
2017-11-02 15:35 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
@ 2017-11-02 15:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2017-11-02 15:40 UTC (permalink / raw)
To: Nico Williams <nico@cryptonector.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2017-11-02 16:35 GMT+01:00 Nico Williams <nico@cryptonector.com>:
> On Thu, Nov 02, 2017 at 06:05:54PM +0530, Robert Haas wrote:
> > On Thu, Oct 26, 2017 at 12:51 PM, Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> > > The variables can be modified by SQL command SET (this is taken from
> > > standard, and it natural)
> > >
> > > SET varname = expression;
> >
> > Overloading SET to handle both variables and GUCs seems likely to
> > create problems, possibly including security problems. For example,
> > maybe a security-definer function could leave behind variables to
> > trick the calling code into failing to set GUCs that it intended to
> > set. Or maybe creating a variable at the wrong time will just break
> > things randomly.
>
> That's already true of GUCs, since there are no access controls on
> set_config()/current_setting().
>
> Presumably "schema variables" would really just be GUC-like and not at
> all like lexically scoped variables. And also subject to access
> controls, thus an overall improvement on set_config()/current_setting().
>
> With access controls, GUCs could become schema variables, and settings
> from postgresql.conf could move into the database itself (which I think
> would be nice).
>
I am sorry, but I don't plan it. the behave of GUC is too different than
behave of variables. But I am planning so system GUC can be "moved" to
pg_catalog to be possibility to specify any object exactly.
Regards
Pavel
>
> Nico
> --
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 12:35 ` Re: proposal: schema variables Robert Haas <robertmhaas@gmail.com>
2017-11-02 15:35 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
@ 2017-11-02 15:48 ` Tom Lane <tgl@sss.pgh.pa.us>
2017-11-02 18:52 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-11-03 12:58 ` Re: proposal: schema variables Chris Travers <chris.travers@adjust.com>
2 siblings, 2 replies; 433+ messages in thread
From: Tom Lane @ 2017-11-02 15:48 UTC (permalink / raw)
To: Nico Williams <nico@cryptonector.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Pavel Stehule <pavel.stehule@gmail.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Nico Williams <nico@cryptonector.com> writes:
> With access controls, GUCs could become schema variables, and settings
> from postgresql.conf could move into the database itself (which I think
> would be nice).
People re-propose some variant of that every so often, but it never works,
because it ignores the fact that some of the GUCs' values are needed
before you can access system catalogs at all, or in places where relying
on system catalog access would be a bad idea.
Sure, we could have two completely different configuration mechanisms
so that some of the variables could be "inside the database", but that
doesn't seem like a net improvement to me. The point of the Grand Unified
Configuration mechanism was to be unified, after all.
I'm on board with having a totally different mechanism for session
variables. The fact that people have been abusing GUC to store
user-defined variables doesn't make it a good way to do that.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 12:35 ` Re: proposal: schema variables Robert Haas <robertmhaas@gmail.com>
2017-11-02 15:35 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-11-02 15:48 ` Re: proposal: schema variables Tom Lane <tgl@sss.pgh.pa.us>
@ 2017-11-02 18:52 ` Nico Williams <nico@cryptonector.com>
1 sibling, 0 replies; 433+ messages in thread
From: Nico Williams @ 2017-11-02 18:52 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Robert Haas <robertmhaas@gmail.com>; Pavel Stehule <pavel.stehule@gmail.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
On Thu, Nov 02, 2017 at 11:48:44AM -0400, Tom Lane wrote:
> Nico Williams <nico@cryptonector.com> writes:
> > With access controls, GUCs could become schema variables, and settings
> > from postgresql.conf could move into the database itself (which I think
> > would be nice).
>
> People re-propose some variant of that every so often, but it never works,
> because it ignores the fact that some of the GUCs' values are needed
> before you can access system catalogs at all, or in places where relying
> on system catalog access would be a bad idea.
ISTM that it should be possible to break the chicken-egg issue by having
the config variables stored in such a way that knowing only the pgdata
directory path should suffice to find them. That's effectively the case
already in that postgresql.conf is found... there.
One could do probably this as a PoC entirely as a SQL-coded VIEW that
reads and writes (via the adminpack module's pg_catalog.pg_file_write())
postgresql.conf (without preserving comments, or with some rules
regarding comments so that they are effectively attached to params).
Nico
--
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 12:35 ` Re: proposal: schema variables Robert Haas <robertmhaas@gmail.com>
2017-11-02 15:35 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
2017-11-02 15:48 ` Re: proposal: schema variables Tom Lane <tgl@sss.pgh.pa.us>
@ 2017-11-03 12:58 ` Chris Travers <chris.travers@adjust.com>
1 sibling, 0 replies; 433+ messages in thread
From: Chris Travers @ 2017-11-03 12:58 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Nico Williams <nico@cryptonector.com>; Robert Haas <robertmhaas@gmail.com>; Pavel Stehule <pavel.stehule@gmail.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Some thoughts on this.
On Thu, Nov 2, 2017 at 4:48 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Nico Williams <nico@cryptonector.com> writes:
> > With access controls, GUCs could become schema variables, and settings
> > from postgresql.conf could move into the database itself (which I think
> > would be nice).
>
> People re-propose some variant of that every so often, but it never works,
> because it ignores the fact that some of the GUCs' values are needed
> before you can access system catalogs at all, or in places where relying
> on system catalog access would be a bad idea.
>
I think the basic point one should get here is that no matter the
unification, you still have some things in the db and some things out.
I would rather look at how the GUC could be improved on a functional/use
case level before we look at the question of a technical solution.
One major use case today would be restricting how high various users can
set something like work_mem or the like. As it stands, there isn't really
a way to control this with any granularity. So some of the proposals
regarding granting access to a session variable would be very handy in
granting access to a GUC variable.
>
> Sure, we could have two completely different configuration mechanisms
> so that some of the variables could be "inside the database", but that
> doesn't seem like a net improvement to me. The point of the Grand Unified
> Configuration mechanism was to be unified, after all.
>
+1
>
> I'm on board with having a totally different mechanism for session
> variables. The fact that people have been abusing GUC to store
> user-defined variables doesn't make it a good way to do that.
>
What about having a more clunky syntax as:
SET VARIABLE foo='bar';
Perhaps one can have a short form of:
SET VAR foo = 'bar';
vs
SET foo = 'bar'; -- GUC
>
> regards, tom lane
>
>
> --
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
>
--
Best Regards,
Chris Travers
Database Administrator
Tel: +49 162 9037 210 | Skype: einhverfr | www.adjust.com
Saarbrücker Straße 37a, 10405 Berlin
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 12:35 ` Re: proposal: schema variables Robert Haas <robertmhaas@gmail.com>
2017-11-02 15:35 ` Re: proposal: schema variables Nico Williams <nico@cryptonector.com>
@ 2017-11-02 17:21 ` Robert Haas <robertmhaas@gmail.com>
2 siblings, 0 replies; 433+ messages in thread
From: Robert Haas @ 2017-11-02 17:21 UTC (permalink / raw)
To: Nico Williams <nico@cryptonector.com>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
On Thu, Nov 2, 2017 at 9:05 PM, Nico Williams <nico@cryptonector.com> wrote:
>> Overloading SET to handle both variables and GUCs seems likely to
>> create problems, possibly including security problems. For example,
>> maybe a security-definer function could leave behind variables to
>> trick the calling code into failing to set GUCs that it intended to
>> set. Or maybe creating a variable at the wrong time will just break
>> things randomly.
>
> That's already true of GUCs, since there are no access controls on
> set_config()/current_setting().
No, it isn't. Right now, SET always refers to a GUC, never a
variable, so there's no possibility of getting confused about whether
it's intending to change a GUC or an eponymous variable. Once you
make SET able to change either one of two different kinds of objects,
then that possibility does exist.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 12:35 ` Re: proposal: schema variables Robert Haas <robertmhaas@gmail.com>
@ 2017-11-02 15:49 ` Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2017-11-02 15:49 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2017-11-02 13:35 GMT+01:00 Robert Haas <robertmhaas@gmail.com>:
> On Thu, Oct 26, 2017 at 12:51 PM, Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> > The variables can be modified by SQL command SET (this is taken from
> > standard, and it natural)
> >
> > SET varname = expression;
>
> Overloading SET to handle both variables and GUCs seems likely to
> create problems, possibly including security problems. For example,
> maybe a security-definer function could leave behind variables to
> trick the calling code into failing to set GUCs that it intended to
> set. Or maybe creating a variable at the wrong time will just break
> things randomly.
>
The syntax CREATE OR REPLACE FUNCTION xxx $$ ... $$ SET GUC=, ... is always
related only to GUC. So there should not be any security risk.
It is another reason why GUC and variables should be separated.
I know so there is risk of possibility of collision. There are two
possibilities
a) use different keyword - but it is out of SQL/PSM and out of another
databases.
b) detect possible collision and raise error when assignment is ambiguous.
I am thinking about similar solution used in plpgsql, where is a
possibility of collision between SQL identifier and plpgsql variable.
Regards
Pavel
>
> --
> Robert Haas
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-11-02 15:07 ` Craig Ringer <craig@2ndquadrant.com>
2017-11-02 15:42 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
9 siblings, 1 reply; 433+ messages in thread
From: Craig Ringer @ 2017-11-02 15:07 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
On 26 October 2017 at 15:21, Pavel Stehule <pavel.stehule@gmail.com> wrote:
> Hi,
>
> I propose a new database object - a variable.
Didn't we have a pretty long discussion about this already in
Yeah.
https://www.postgresql.org/message-id/flat/CAMsr%2BYF0G8_FehQyFS8gSfnEer9OPsMOvpfniDJOVGQzJzHzsw%40m...
It'd be nice if you summarised any outcomes from that and addressed
it, rather than taking this as a new topic.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 15:07 ` Re: proposal: schema variables Craig Ringer <craig@2ndquadrant.com>
@ 2017-11-02 15:42 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2017-11-02 15:42 UTC (permalink / raw)
To: Craig Ringer <craig@2ndquadrant.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2017-11-02 16:07 GMT+01:00 Craig Ringer <craig@2ndquadrant.com>:
> On 26 October 2017 at 15:21, Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> > Hi,
> >
> > I propose a new database object - a variable.
>
> Didn't we have a pretty long discussion about this already in
>
> Yeah.
>
> https://www.postgresql.org/message-id/flat/CAMsr%2BYF0G8_
> FehQyFS8gSfnEer9OPsMOvpfniDJOVGQzJzHzsw%40mail.gmail.com#CAMsr+YF0G8_
> FehQyFS8gSfnEer9OPsMOvpfniDJOVGQzJzHzsw@mail.gmail.com
>
> It'd be nice if you summarised any outcomes from that and addressed
> it, rather than taking this as a new topic.
>
I am sorry. This thread follow mentioned and I started with small
recapitulation.
Regards
Pavel
> --
> Craig Ringer http://www.2ndQuadrant.com/
> PostgreSQL Development, 24x7 Support, Training & Services
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2017-11-13 12:15 ` Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
9 siblings, 1 reply; 433+ messages in thread
From: Pavel Golub @ 2017-11-13 12:15 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hello, Pavel.
You wrote:
PS> Hi,
PS> I propose a new database object - a variable. The variable is
PS> persistent object, that holds unshared session based not
PS> transactional in memory value of any type. Like variables in any
PS> other languages. The persistence is required for possibility to do
PS> static checks, but can be limited to session - the variables can be temporal.
Great idea.
PS> My proposal is related to session variables from Sybase, MSSQL or
PS> MySQL (based on prefix usage @ or @@), or package variables from
PS> Oracle (access is controlled by scope), or schema variables from
PS> DB2. Any design is coming from different sources, traditions and
PS> has some advantages or disadvantages. The base of my proposal is
PS> usage schema variables as session variables for stored procedures.
PS> It should to help to people who try to port complex projects to PostgreSQL from other databases.
PS> The Sybase (T-SQL) design is good for interactive work, but it
PS> is weak for usage in stored procedures - the static check is not
PS> possible. Is not possible to set some access rights on variables.
PS> The ADA design (used on Oracle) based on scope is great, but our
PS> environment is not nested. And we should to support other PL than PLpgSQL more strongly.
PS> There is not too much other possibilities - the variable that
PS> should be accessed from different PL, different procedures (in
PS> time) should to live somewhere over PL, and there is the schema only.
PS> The variable can be created by CREATE statement:
PS> CREATE VARIABLE public.myvar AS integer;
PS> CREATE VARIABLE myschema.myvar AS mytype;
PS> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
PS> [ DEFAULT expression ] [[NOT] NULL]
PS> [ ON TRANSACTION END { RESET | DROP } ]
PS> [ { VOLATILE | STABLE } ];
PS> It is dropped by command DROP VARIABLE [ IF EXISTS] varname.
PS> The access rights is controlled by usual access rights - by
PS> commands GRANT/REVOKE. The possible rights are: READ, WRITE
PS> The variables can be modified by SQL command SET (this is taken from standard, and it natural)
PS> SET varname = expression;
I propose LET keyword for this to distinguish GUC from variables, e.g.
LET varname = expression;
PS> Unfortunately we use the SET command for different purpose. But I
PS> am thinking so we can solve it with few tricks. The first is
PS> moving our GUC to pg_catalog schema. We can control the strictness
PS> of SET command. In one variant, we can detect custom GUC and allow
PS> it, in another we can disallow a custom GUC and allow only schema
PS> variables. A new command LET can be alternative.
PS> The variables should be used in queries implicitly (without JOIN)
PS> SELECT varname;
PS> The SEARCH_PATH is used, when varname is located. The variables
PS> can be used everywhere where query parameters are allowed.
PS> I hope so this proposal is good enough and simple.
PS> Comments, notes?
PS> regards
PS> Pavel
--
With best wishes,
Pavel mailto:pavel@gf.microolap.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
@ 2017-11-13 12:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2017-11-13 12:30 UTC (permalink / raw)
To: Pavel Golub <pavel@gf.microolap.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi
2017-11-13 13:15 GMT+01:00 Pavel Golub <pavel@microolap.com>:
> Hello, Pavel.
>
> You wrote:
>
> PS> Hi,
>
> PS> I propose a new database object - a variable. The variable is
> PS> persistent object, that holds unshared session based not
> PS> transactional in memory value of any type. Like variables in any
> PS> other languages. The persistence is required for possibility to do
> PS> static checks, but can be limited to session - the variables can be
> temporal.
>
> Great idea.
>
> PS> My proposal is related to session variables from Sybase, MSSQL or
> PS> MySQL (based on prefix usage @ or @@), or package variables from
> PS> Oracle (access is controlled by scope), or schema variables from
> PS> DB2. Any design is coming from different sources, traditions and
> PS> has some advantages or disadvantages. The base of my proposal is
> PS> usage schema variables as session variables for stored procedures.
> PS> It should to help to people who try to port complex projects to
> PostgreSQL from other databases.
>
> PS> The Sybase (T-SQL) design is good for interactive work, but it
> PS> is weak for usage in stored procedures - the static check is not
> PS> possible. Is not possible to set some access rights on variables.
>
> PS> The ADA design (used on Oracle) based on scope is great, but our
> PS> environment is not nested. And we should to support other PL than
> PLpgSQL more strongly.
>
> PS> There is not too much other possibilities - the variable that
> PS> should be accessed from different PL, different procedures (in
> PS> time) should to live somewhere over PL, and there is the schema only.
>
> PS> The variable can be created by CREATE statement:
>
> PS> CREATE VARIABLE public.myvar AS integer;
> PS> CREATE VARIABLE myschema.myvar AS mytype;
>
> PS> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
> PS> [ DEFAULT expression ] [[NOT] NULL]
> PS> [ ON TRANSACTION END { RESET | DROP } ]
> PS> [ { VOLATILE | STABLE } ];
>
>
> PS> It is dropped by command DROP VARIABLE [ IF EXISTS] varname.
>
> PS> The access rights is controlled by usual access rights - by
> PS> commands GRANT/REVOKE. The possible rights are: READ, WRITE
>
> PS> The variables can be modified by SQL command SET (this is taken from
> standard, and it natural)
>
> PS> SET varname = expression;
>
> I propose LET keyword for this to distinguish GUC from variables, e.g.
>
> LET varname = expression;
>
It is one possible variant. I plan to implement more variants and then
choose one.
Regards
Pavel
>
> PS> Unfortunately we use the SET command for different purpose. But I
> PS> am thinking so we can solve it with few tricks. The first is
> PS> moving our GUC to pg_catalog schema. We can control the strictness
> PS> of SET command. In one variant, we can detect custom GUC and allow
> PS> it, in another we can disallow a custom GUC and allow only schema
> PS> variables. A new command LET can be alternative.
>
>
>
> PS> The variables should be used in queries implicitly (without JOIN)
>
>
> PS> SELECT varname;
>
>
> PS> The SEARCH_PATH is used, when varname is located. The variables
> PS> can be used everywhere where query parameters are allowed.
>
>
>
> PS> I hope so this proposal is good enough and simple.
>
>
> PS> Comments, notes?
>
>
> PS> regards
>
>
> PS> Pavel
>
>
>
>
>
>
> --
> With best wishes,
> Pavel mailto:pavel@gf.microolap.com
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-02-02 22:06 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
0 siblings, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-02-02 22:06 UTC (permalink / raw)
To: Pavel Golub <pavel@gf.microolap.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi
I wrote proof concept of schema variables. The patch is not nice, but the
functionality is almost complete (for scalars only) and can be good enough
for playing with this concept.
I recap a goals (the order is random):
1. feature like PL/SQL package variables (with similar content life cycle)
2. available from any PL used by PostgreSQL, data can be shared between
different PL
3. possibility to store short life data in fast secured storage
4. possibility to pass parameters and results to/from anonymous blocks
5. session variables with possibility to process static code check
6. multiple API available from different environments - SQL commands, SQL
functions, internal functions
7. data are stored in binary form
Example:
CREATE VARIABLE public.foo AS integer;
LET foo = 10 + 20;
DO $$
declare x int = random() * 1000;
BEGIN
RAISE NOTICE '%', foo;
LET foo = x + 100;
END;
$$;
SELECT public.foo + 10;
SELECT * FROM data WHERE col = foo;
All implemented features are described by regress tests
Interesting note - it is running without any modification of plpgsql code.
Regards
Pavel
Attachments:
[text/x-patch] schema-variables-poc.patch (118.1K, ../../CAFj8pRBfb-GTZSHSRVTpMzGr26-7e-_RmOmRpmuk+xuDTgC=mA@mail.gmail.com/3-schema-variables-poc.patch)
download | inline diff:
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 71e20f2740..fbf78e602d 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1813,7 +1813,8 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
<literal>m</literal> = materialized view,
<literal>c</literal> = composite type,
<literal>f</literal> = foreign table,
- <literal>p</literal> = partitioned table
+ <literal>p</literal> = partitioned table,
+ <literal>V</literal> = schema variable
</entry>
</row>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 487c7ff750..5031cd4d70 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15742,6 +15742,82 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
</sect1>
+ <sect1 id="functions-schemavar">
+ <title>Functions for access to schema variables</title>
+
+ <indexterm zone="functions-schemavar">
+ <primary>Functions for access to schema variables</primary>
+ <secondary>functions</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>get_schema_variable</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>set_schema_variable</primary>
+ </indexterm>
+
+ <para>
+ These functions allow reading and writing schema variables values.
+ </para>
+
+ <table id="functions-schemavar-tab">
+ <title>Functions for access to chema variables</title>
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Function</entry>
+ <entry>Argument Type</entry>
+ <entry>Return Type</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal><function>get_schema_variable(<parameter>variable</parameter>, <parameter>expected type</parameter>)</function></literal></entry>
+ <entry><type>regclass</type>, <type>anyelement</type></entry>
+ <entry><type>anyelement</type></entry>
+ <entry>
+ Returns value of schema variables coverted to expected type.
+ </entry>
+ </row>
+
+ <row>
+ <entry><literal><function>set_schema_variable(<parameter>variable</parameter>, <parameter>value</parameter>)</function></literal></entry>
+ <entry><type>regclass</type>, <type>anyelement</type></entry>
+ <entry><type>void</type></entry>
+ <entry>
+ Set a value of schema variable. Value is converted to type of schema variable.
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ The usage is very simple:
+<programlisting>
+CREATE TEMP VARIABLE foo AS numeric;
+SELECT set_schema_variable('foo', 345.445);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT get_schema_variable('foo', null::numeric);
+
+ get_schema_variable
+---------------------
+ 345.445
+(1 row)
+</programlisting>
+ </para>
+
+ </sect1>
+
<sect1 id="functions-info">
<title>System Information Functions</title>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 22e6893211..1d34f72bdd 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -99,6 +99,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createType SYSTEM "create_type.sgml">
<!ENTITY createUser SYSTEM "create_user.sgml">
<!ENTITY createUserMapping SYSTEM "create_user_mapping.sgml">
+<!ENTITY createVariable SYSTEM "create_variable.sgml">
<!ENTITY createView SYSTEM "create_view.sgml">
<!ENTITY deallocate SYSTEM "deallocate.sgml">
<!ENTITY declare SYSTEM "declare.sgml">
@@ -147,6 +148,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropType SYSTEM "drop_type.sgml">
<!ENTITY dropUser SYSTEM "drop_user.sgml">
<!ENTITY dropUserMapping SYSTEM "drop_user_mapping.sgml">
+<!ENTITY dropVariable SYSTEM "drop_variable.sgml">
<!ENTITY dropView SYSTEM "drop_view.sgml">
<!ENTITY end SYSTEM "end.sgml">
<!ENTITY execute SYSTEM "execute.sgml">
@@ -155,6 +157,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY grant SYSTEM "grant.sgml">
<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
<!ENTITY insert SYSTEM "insert.sgml">
+<!ENTITY let SYSTEM "let.sgml">
<!ENTITY listen SYSTEM "listen.sgml">
<!ENTITY load SYSTEM "load.sgml">
<!ENTITY lock SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
new file mode 100644
index 0000000000..037fa087f5
--- /dev/null
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -0,0 +1,144 @@
+<!--
+doc/src/sgml/ref/create_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-createvariable">
+ <indexterm zone="sql-createvariable">
+ <primary>CREATE VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE VARIABLE</refname>
+ <refpurpose>define a new schema secure typed variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> creates a new schema variable.
+ These variables are memory only non transactional, but typed and
+ secure. The access is controlled by rights defined by command
+ <command>GRANT</command> and command <command>REVOKE</command>.
+ </para>
+
+ <para>
+ The schema variable is initialized to NULL value. The content of
+ variable is lost when session is destroyed.
+ </para>
+
+ <para>
+ The schema variable can be any scalar only.
+ type.
+ </para>
+
+ <para>
+ After a variable is created, you use the special functions
+ <function>get_schema_variables</function>, <function>set_schema_variables</function>.
+ type.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF NOT EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if a relation with the same name already exists.
+ A notice is issued in this case. Note that there is no guarantee that
+ the existing relation is anything like the variable that would have
+ been created - it might not even be a variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the data type ofvariable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Use <command>DROP VARIABLE</command> to remove a variable.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Create an integer variable <literal>var1</literal>:
+<programlisting>
+CREATE VARIABLE var1 AS integer;
+</programlisting>
+ </para>
+
+ <para>
+ Set a value of this variable:
+<programlisting>
+CREATE VARIABLE
+postgres=# select set_schema_variable('var1', 10);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+postgres=# select get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 10
+(1 row)
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> is PostgreSQL feature
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index 6b909b7232..b348c02e0b 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
+DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES}
</synopsis>
</refsynopsisdiv>
@@ -75,6 +75,15 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>VARIABLES</literal></term>
+ <listitem>
+ <para>
+ Releases content of all schema variables in current session.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
new file mode 100644
index 0000000000..f6c2e46476
--- /dev/null
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -0,0 +1,89 @@
+<!--
+doc/src/sgml/ref/drop_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropvariable">
+ <indexterm zone="sql-dropvariable">
+ <primary>DROP VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP VARIABLE</refname>
+ <refpurpose>remove a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP VARIABLE</command> removes schema variable.
+ A variable can only be dropped by its owner or a superuser.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the variable does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To remove the schema variable <literal>var1</literal>:
+
+<programlisting>
+DROP VARIABLE var1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index ff64c7a3ba..7dde54ce0f 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -79,6 +79,12 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE <replaceable>type_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+GRANT { { SELECT | UPDATE }
+ [, ...] | ALL [ PRIVILEGES ] }
+ ON { VARIABLE <replaceable class="parameter">variable_name</replaceable> [, ...]
+ | ALL VARIABLES IN SCHEMA <replaceable class="parameter">schema_name</replaceable> [, ...] }
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
[ GROUP ] <replaceable class="parameter">role_name</replaceable>
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
new file mode 100644
index 0000000000..b040b5e1fe
--- /dev/null
+++ b/doc/src/sgml/ref/let.sgml
@@ -0,0 +1,88 @@
+<!--
+doc/src/sgml/ref/let.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-let">
+ <indexterm zone="sql-let">
+ <primary>LET</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>LET</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>LET</refname>
+ <refpurpose>change a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable class="parameter">sql_expression</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ The <command>LET</command> command sets specified schema variable.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>schema_variable</literal></term>
+ <listitem>
+ <para>
+ Specifies that the name of schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sql expression</literal></term>
+ <listitem>
+ <para>
+ Any SQL expression.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Example:
+<programlisting>
+CREATE VARIABLE myvar AS integer;
+LET myvar = 10;
+LET myvar = (SELECT sum(val) FROM tab);
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <literal>LET</literal> extends syntax defined in the SQL
+ standard. The standard knows <literal>SET</literal> command,
+ that is used for different purpouse in PostgreSQL.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 7018202f14..73778f01f9 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -108,6 +108,14 @@ REVOKE [ GRANT OPTION FOR ]
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_name</replaceable> [, ...]
[ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { SELECT | UPDATE }
+ [, ...] | ALL [ PRIVILEGES ] }
+ ON { VARIABLE <replaceable class="parameter">variable_name</replaceable> [, ...]
+ | ALL VARIABLES IN SCHEMA <replaceable>schema_name</replaceable> [, ...] }
+ FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index d27fb414f7..b3f9fff511 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -127,6 +127,7 @@
&createType;
&createUser;
&createUserMapping;
+ &createVariable;
&createView;
&deallocate;
&declare;
@@ -175,6 +176,7 @@
&dropType;
&dropUser;
&dropUserMapping;
+ &dropVariable;
&dropView;
&end;
&execute;
@@ -183,6 +185,7 @@
&grant;
&importForeignSchema;
&insert;
+ &let;
&listen;
&load;
&lock;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1156627b9e..268534ea87 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -284,6 +284,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -506,6 +509,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -576,6 +583,7 @@ ExecGrantStmt_oids(InternalGrant *istmt)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_VARIABLE:
ExecGrant_Relation(istmt);
break;
case OBJECT_DATABASE:
@@ -645,6 +653,7 @@ objectNamesToOids(ObjectType objtype, List *objnames)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_VARIABLE:
foreach(cell, objnames)
{
RangeVar *relvar = (RangeVar *) lfirst(cell);
@@ -1021,6 +1030,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1218,6 +1231,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1444,6 +1463,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -3459,6 +3481,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3569,6 +3594,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3683,6 +3711,7 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
pg_attribute_aclmask(table_oid, attnum, roleid, mask, how);
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_VARIABLE:
return pg_class_aclmask(table_oid, roleid, mask, how);
case OBJECT_DATABASE:
return pg_database_aclmask(table_oid, roleid, mask, how);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 774c07b03a..569bae00e2 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -291,6 +291,7 @@ heap_create(const char *relname,
switch (relkind)
{
case RELKIND_VIEW:
+ case RELKIND_VARIABLE:
case RELKIND_COMPOSITE_TYPE:
case RELKIND_FOREIGN_TABLE:
case RELKIND_PARTITIONED_TABLE:
@@ -1067,7 +1068,9 @@ heap_create_with_catalog(const char *relname,
if (existing_relid != InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_TABLE),
- errmsg("relation \"%s\" already exists", relname)));
+ errmsg("%s \"%s\" already exists",
+ relkind == RELKIND_VARIABLE ? "variable" : "relation",
+ relname)));
/*
* Since we are going to create a rowtype as well, also check for
@@ -1150,6 +1153,10 @@ heap_create_with_catalog(const char *relname,
relacl = get_user_default_acl(OBJECT_SEQUENCE, ownerid,
relnamespace);
break;
+ case RELKIND_VARIABLE:
+ relacl = get_user_default_acl(OBJECT_VARIABLE, ownerid,
+ relnamespace);
+ break;
default:
relacl = NULL;
break;
@@ -1181,7 +1188,8 @@ heap_create_with_catalog(const char *relname,
* Decide whether to create an array type over the relation's rowtype. We
* do not create any array types for system catalogs (ie, those made
* during initdb). We do not create them where the use of a relation as
- * such is an implementation detail: toast tables, sequences and indexes.
+ * such is an implementation detail: toast tables, sequences, indexes and
+ * variables.
*/
if (IsUnderPostmaster && (relkind == RELKIND_RELATION ||
relkind == RELKIND_VIEW ||
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 570e65affb..62479743c7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -520,6 +520,9 @@ static const struct object_type_map
{
"sequence", OBJECT_SEQUENCE
},
+ {
+ "variable", OBJECT_VARIABLE
+ },
{
"toast table", -1
}, /* unmapped */
@@ -824,6 +827,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
+ case OBJECT_VARIABLE:
address =
get_relation_by_qualified_name(objtype, castNode(List, object),
&relation, lockmode,
@@ -1260,6 +1264,14 @@ get_relation_by_qualified_name(ObjectType objtype, List *object,
errmsg("\"%s\" is not a foreign table",
RelationGetRelationName(relation))));
break;
+ case OBJECT_VARIABLE:
+ if (relation->rd_rel->relkind != RELKIND_VARIABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a schema variable",
+ RelationGetRelationName(relation))));
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
break;
@@ -1847,6 +1859,8 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2109,6 +2123,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_INDEX:
@@ -2233,6 +2248,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_INDEX:
case OBJECT_SEQUENCE:
case OBJECT_TABLE:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
@@ -3299,6 +3315,11 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new schemas belonging to role %s"),
GetUserNameFromId(defacl->defaclrole, false));
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfo(&buffer,
+ _("default privileges on new schema variables belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole, false));
+ break;
default:
/* shouldn't get here */
appendStringInfo(&buffer,
@@ -3502,6 +3523,10 @@ getRelationDescription(StringInfo buffer, Oid relid)
appendStringInfo(buffer, _("sequence %s"),
relname);
break;
+ case RELKIND_VARIABLE:
+ appendStringInfo(buffer, _("variable %s"),
+ relname);
+ break;
case RELKIND_TOASTVALUE:
appendStringInfo(buffer, _("toast table %s"),
relname);
@@ -4830,6 +4855,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on schema variables");
+ break;
}
if (objname)
@@ -5122,6 +5151,8 @@ get_relkind_objtype(char relkind)
return OBJECT_INDEX;
case RELKIND_SEQUENCE:
return OBJECT_SEQUENCE;
+ case RELKIND_VARIABLE:
+ return OBJECT_VARIABLE;
case RELKIND_VIEW:
return OBJECT_VIEW;
case RELKIND_MATVIEW:
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..5747272c9a 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,7 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
- schemacmds.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
+ schemacmds.o schemavar.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
tablecmds.o tablespace.o trigger.o tsearchcmds.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 04a24c6082..20d1483a4e 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1485,6 +1485,9 @@ BeginCopy(ParseState *pstate,
Assert(query->utilityStmt == NULL);
+ /* Don't expect LET stmt here, is not possible to do write it */
+ Assert(query->commandType != CMD_LET);
+
/*
* Similarly the grammar doesn't enforce the presence of a RETURNING
* clause, but this is required here.
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 353ec990af..33db47e634 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -18,6 +18,7 @@
#include "commands/async.h"
#include "commands/discard.h"
#include "commands/prepare.h"
+#include "commands/schemavar.h"
#include "commands/sequence.h"
#include "utils/guc.h"
#include "utils/portal.h"
@@ -25,7 +26,7 @@
static void DiscardAll(bool isTopLevel);
/*
- * DISCARD { ALL | SEQUENCES | TEMP | PLANS }
+ * DISCARD { ALL | SEQUENCES | TEMP | PLANS | VARIABLES}
*/
void
DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
@@ -48,6 +49,10 @@ DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
ResetTempTableNamespace();
break;
+ case DISCARD_VARIABLES:
+ ResetSchemaVariablesCache();
+ break;
+
default:
elog(ERROR, "unrecognized DISCARD target: %d", stmt->target);
}
@@ -75,4 +80,5 @@ DiscardAll(bool isTopLevel)
ResetPlanCache();
ResetTempTableNamespace();
ResetSequenceCaches();
+ ResetSchemaVariablesCache();
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 549c7ea51d..c8e2b822e1 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -126,6 +126,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"TEXT SEARCH TEMPLATE", true},
{"TYPE", true},
{"USER MAPPING", true},
+ {"VARIABLE", true},
{"VIEW", true},
{NULL, false}
};
@@ -1124,6 +1125,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -2222,6 +2224,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2304,6 +2308,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 41cd47e8bc..11c8257fca 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -893,6 +893,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
case CMD_DELETE:
pname = operation = "Delete";
break;
+ case CMD_LET:
+ pname = operation = "Let";
+ break;
default:
pname = "???";
break;
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b1556a..a69471e926 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_LET:
/* OK */
break;
default:
diff --git a/src/backend/commands/schemavar.c b/src/backend/commands/schemavar.c
new file mode 100644
index 0000000000..cb803fab0c
--- /dev/null
+++ b/src/backend/commands/schemavar.c
@@ -0,0 +1,663 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavar.c
+ * PostgreSQL session variable support code.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/schemavar.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/objectaddress.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_type.h"
+#include "commands/tablecmds.h"
+#include "commands/schemavar.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_type.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/hsearch.h"
+#include "utils/inval.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/syscache.h"
+
+/*
+ * This schema variable cache mixes the cache and storages behave. That is not
+ * correct and it is problematic, when variable is removed. The own storage
+ * based on storage manager can be implemented, RelFileNode can be defined and
+ * mechanism based on PendingRelDelete struct can be used. This is a argument
+ * for implementation schema variables based on pg_class.
+ * Alternative solution can be detection of schema changes and recheck at and
+ * of transaction.
+ */
+typedef struct SchemaVarData
+{
+ Oid varid; /* pg_class OID of this sequence (hash key) */
+ Oid typid; /* OID of the data type */
+ int32 typmod;
+ int16 typlen;
+ bool typbyval;
+ bool isnull;
+ bool freeval;
+ Datum value;
+} SchemaVarData;
+
+typedef SchemaVarData *SchemaVar;
+
+static HTAB *schemavarhashtab = NULL; /* hash table for session variables */
+static MemoryContext SchemaVarMemoryContext = NULL;
+
+static Datum datumCast(Datum value,
+ Oid target_typid, int target_typmod,
+ Oid source_typid, int source_typmod);
+
+static bool first_time = true;
+static bool cache_is_valid = true;
+
+static void InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
+
+/* just mark cache to recheck */
+static void
+InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ /*
+ * because this cache holds values of schema variables, then
+ * the content cannot be removed in this momemt. We should to
+ * wait on transaction end.
+ */
+ cache_is_valid = false;
+}
+
+/*
+ * Wait on commit or rollback and clean values that miss entry in system
+ * catalog. It is temporary solution (although it is working). Storage manager
+ * based solution will be better, but it is not necessary for this PoC.
+ *
+ * removes uncommitted or dropped schema variables, so event can be ignored.
+ */
+static void
+recheck_schema_variables(XactEvent event, void *arg)
+{
+ HASH_SEQ_STATUS status;
+ SchemaVar var;
+
+ if (cache_is_valid || schemavarhashtab == NULL || !IsTransactionState())
+ return;
+
+ hash_seq_init(&status, schemavarhashtab);
+
+ while ((var = (SchemaVar) hash_seq_search(&status)) != NULL)
+ {
+ HeapTuple tp = InvalidOid;
+
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(var->varid));
+ if (!HeapTupleIsValid(tp))
+ {
+ elog(DEBUG1, "variable %d is removed from cache", var->varid);
+
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (hash_search(schemavarhashtab,
+ (void *) &var->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(ERROR, "hash table corrupted");
+ }
+ else
+ ReleaseSysCache(tp);
+ }
+ cache_is_valid = true;
+}
+
+/*
+ * DefineSessionVariable
+ * Creates a new variable related relation
+ */
+ObjectAddress
+DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *var)
+{
+ CreateStmt *stmt = makeNode(CreateStmt);
+ Oid typoid;
+ Oid varoid;
+ ObjectAddress address;
+
+ /*
+ * If if_not_exists was given and a relation with the same name already
+ * exists, bail out. (Note: we needn't check this when not if_not_exists,
+ * because DefineRelation will complain anyway.)
+ */
+ if (var->if_not_exists)
+ {
+ RangeVarGetAndCheckCreationNamespace(var->variable, NoLock, &varoid);
+ if (OidIsValid(varoid))
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_TABLE),
+ errmsg("variable \"%s\" already exists, skipping",
+ var->variable->relname)));
+ return InvalidObjectAddress;
+ }
+ }
+
+ typoid = LookupTypeNameOid(pstate, var->typeName, false);
+
+ /*
+ * Don't allow composite types and arrays. The left expression of
+ * LET statement is simple in this moment (don't allow record field
+ * or array field specification). Without this support we should
+ * not to support non scalars ever.
+ */
+ if (type_is_rowtype(typoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("Composite types are not allowed as variable type.")));
+
+ if (get_base_element_type(typoid) != InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("Schema variables cannot be a array.")));
+
+ if (get_typtype(typoid) == TYPTYPE_PSEUDO)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable cannot be %s",
+ format_type_be(varoid))));
+
+ stmt->tableElts = NIL;
+ stmt->relation = var->variable;
+ stmt->inhRelations = NIL;
+ stmt->constraints = NIL;
+ stmt->options = NIL;
+ stmt->oncommit = ONCOMMIT_NOOP;
+ stmt->tablespacename = NULL;
+ stmt->if_not_exists = var->if_not_exists;
+
+ /*
+ * Use reloftype attribute. This attribute should be composite type for
+ * tables, but there are no reason to apply this rule for variables. Can
+ * be changed later with composite type support. In this moment I don't
+ * play with it, because I would not allow queries like:
+ * SELECT schemavar FROM schemavar, because there is semantic colission
+ * with SELECT schemavar. Users expects composite value (one attribute)
+ * from first query, but scalar from second query. This schisma can be
+ * solved by disallowing SELECT . FROM schemavar for scalar variables.
+ *
+ * On second hand - without additional fields, just with reloftype is
+ * not possible to store typmod. So all variables can be typmod less.
+ * Is not possible to store default expressions. So final design should
+ * be based on aux composite types for scalar variables.
+ *
+ * Theoretically, there can be used a reltype and reloftype together.
+ * reloftype will be scalar, and reltype will be composite one field
+ * row type. When reloftype = reltype, then schema variable is based
+ * on composite type, else schema variable is of scalar type.
+ */
+ stmt->ofTypename = var->typeName;
+
+ address = DefineRelation(stmt, RELKIND_VARIABLE, InvalidOid, NULL, NULL);
+ Assert(address.objectId != InvalidOid);
+
+ return address;
+}
+
+/*
+ * Implementation of schemavar cache. It is question if it should be in this place, or
+ * it should be storage related or cache related place? But for this moment (PoC) it
+ * can be here. Cache is implemented as hash table with own memory context.
+ */
+
+/*
+ * Create the hash table for storing schema variables
+ */
+static void
+create_schemavar_hashtable(void)
+{
+ HASHCTL ctl;
+
+ /* set callbacks */
+ if (first_time)
+ {
+
+ CacheRegisterSyscacheCallback(RELOID,
+ InvalidateSchemaVarCacheCallback,
+ (Datum) 0);
+ RegisterXactCallback(recheck_schema_variables, NULL);
+
+ first_time = false;
+ }
+
+ /* needs own long life memory context */
+ if (SchemaVarMemoryContext == NULL)
+ {
+ SchemaVarMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "schema variables",
+ ALLOCSET_START_SMALL_SIZES);
+ }
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(SchemaVarData);
+ ctl.hcxt = SchemaVarMemoryContext;
+
+ schemavarhashtab = hash_create("Schema variables", 64, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+ cache_is_valid = true;
+}
+
+/*
+ * Fast drop complete content of schema variables
+ */
+void
+ResetSchemaVariablesCache(void)
+{
+ if (schemavarhashtab)
+ {
+ hash_destroy(schemavarhashtab);
+ schemavarhashtab = NULL;
+ }
+
+ if (SchemaVarMemoryContext != NULL)
+ {
+ MemoryContextReset(SchemaVarMemoryContext);
+ }
+}
+
+/*
+ * Copy datum value to schema variables cache place
+ */
+static void
+SetValue(SchemaVar var,
+ Datum value, bool isNull,
+ Oid typid, int32 typmod)
+{
+ /* release previously stored value */
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (!isNull)
+ {
+ MemoryContext oldcxt;
+
+ /*
+ * cast the value if conversion is necessary.
+ * Expecting: current context is short context.
+ *
+ * QUESTION: how much should be this cast tolerant/strict?
+ */
+ if (var->typid != typid || var->typmod != typmod)
+ {
+ value = datumCast(value,
+ var->typid, var->typmod,
+ typid, typmod);
+ }
+
+ var->isnull = false;
+
+ oldcxt = MemoryContextSwitchTo(SchemaVarMemoryContext);
+
+ var->value = datumCopy(value, var->typbyval, var->typlen);
+ if (var->value != value)
+ var->freeval = true;
+
+ MemoryContextSwitchTo(oldcxt);
+ }
+ else
+ {
+ var->value = (Datum) 0;
+ var->isnull = true;
+ }
+}
+
+/*
+ * Access functions to schema variables.
+ */
+void
+SetSchemaVariable(Oid varid, Datum value, bool isNull,
+ Oid typid, int32 typmod,
+ int16 typlen, bool typbyval)
+{
+ SchemaVar var;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ {
+ /* don't init hashtable for NULL values */
+ if (isNull)
+ return;
+
+ create_schemavar_hashtable();
+ }
+
+ var = (SchemaVar) hash_search(schemavarhashtab, &varid, HASH_ENTER, &found);
+ if (!found)
+ {
+ HeapTuple tp;
+ Form_pg_class vartup;
+
+ var->value = (Datum) 0;
+ var->isnull = true;
+ var->freeval = false;
+
+ /* now, type info for schema variable is collected */
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup faild for variable %u", varid);
+
+ vartup = (Form_pg_class) GETSTRUCT(tp);
+ var->typid = vartup->reloftype;
+
+ /* typmod is not saved */
+ var->typmod = -1;
+
+ ReleaseSysCache(tp);
+
+ get_typlenbyval(var->typid, &var->typlen, &var->typbyval);
+ }
+
+ SetValue(var, value, isNull, typid, typmod);
+}
+
+/*
+ * Returns variable name
+ */
+char *
+get_schemavar_name(Oid varid)
+{
+ HeapTuple relTup;
+ Form_pg_class relForm;
+ char *nspname;
+ char *relname;
+
+ relTup = SearchSysCache1(RELOID,
+ ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(relTup))
+ elog(ERROR, "cache lookup failed for schema variable %u", varid);
+ relForm = (Form_pg_class) GETSTRUCT(relTup);
+
+ /* Qualify the name if not visible in search path */
+ if (RelationIsVisible(varid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(relForm->relnamespace);
+
+ relname = quote_qualified_identifier(nspname, NameStr(relForm->relname));
+
+ ReleaseSysCache(relTup);
+
+ return relname;
+}
+
+/*
+ * Securized versions SetSchemaVariable
+ */
+void
+SetSchemaVariableSecure(Oid varid, Datum value, bool isNull,
+ Oid typid, int32 typmod,
+ int16 typlen, bool typbyval)
+{
+ AclResult aclresult;
+
+ /* Check permissions */
+ aclresult = pg_class_aclcheck(varid, GetUserId(), ACL_UPDATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, get_schemavar_name(varid));
+
+ SetSchemaVariable(varid, value, isNull, typid, typmod, typlen, typbyval);
+}
+
+/*
+ * Cast datum
+ */
+static Datum
+datumCast(Datum value,
+ Oid target_typid, int target_typmod,
+ Oid source_typid, int source_typmod)
+{
+ CoercionPathType cpathtype;
+ Oid cfuncid;
+ Datum result = (Datum) 0;
+ bool is_binary_cast = false;
+
+ if (target_typid != source_typid)
+ {
+ cpathtype = find_coercion_pathway(target_typid, source_typid,
+ COERCION_EXPLICIT,
+ &cfuncid);
+
+ if (cpathtype == COERCION_PATH_NONE)
+ elog(ERROR, "could not find cast from %s to %s",
+ format_type_be(source_typid),
+ format_type_be(target_typid));
+
+ if (cpathtype == COERCION_PATH_RELABELTYPE)
+ {
+ result = value;
+ is_binary_cast = true;
+ }
+ else if (cpathtype == COERCION_PATH_COERCEVIAIO)
+ {
+ Oid outfunc;
+ Oid infunc;
+ Oid ioparam;
+ bool isVarlena;
+ char *str;
+
+ getTypeOutputInfo(source_typid, &outfunc, &isVarlena);
+ str = OidOutputFunctionCall(outfunc, value);
+
+ getTypeInputInfo(target_typid, &infunc, &ioparam);
+ result = OidInputFunctionCall(infunc, str, ioparam, -1);
+ }
+ else if (cpathtype == COERCION_PATH_FUNC)
+ {
+ result = OidFunctionCall3(cfuncid,
+ value,
+ Int32GetDatum(target_typmod),
+ BoolGetDatum(false));
+ }
+ }
+ else
+ {
+ result = value;
+ is_binary_cast = true;
+ }
+
+ if (target_typmod < 1 || (target_typmod == source_typmod && is_binary_cast))
+ return result;
+
+ cpathtype = find_typmod_coercion_function(target_typid, &cfuncid);
+ if (cpathtype == COERCION_PATH_FUNC)
+ {
+ result = OidFunctionCall3(cfuncid,
+ result,
+ Int32GetDatum(target_typmod),
+ BoolGetDatum(false));
+ }
+
+ return result;
+}
+
+Datum
+GetSchemaVariable(Oid varid, bool *isNull,
+ Oid typid, int32 typmod,
+ int16 typlen, bool typbyval)
+{
+ Assert(varid != InvalidOid);
+
+ if (schemavarhashtab != NULL)
+ {
+ SchemaVar var;
+ bool found;
+
+ var = (SchemaVar) hash_search(schemavarhashtab,
+ &varid, HASH_FIND, &found);
+
+ if (found && !var->isnull)
+ {
+ Datum result;
+
+ result = datumCast(var->value, typid, typmod,
+ var->typid, var->typmod);
+ *isNull = false;
+
+ if (result != var->value)
+ return result;
+ else
+ return datumCopy(result, typbyval, typlen);
+ }
+ }
+
+ /*
+ * This implementation is simple, because default expressions
+ * are not supported. With support of default expression, there
+ * should be insert schema variable into cache. Not supported yet,
+ * so do just simply work.
+ */
+ *isNull = true;
+ return (Datum) 0;
+}
+
+/*
+ * Securized version of GetSchemaVariable
+ */
+Datum
+GetSchemaVariableSecure(Oid varid, bool *isNull,
+ Oid typid, int32 typmod,
+ int16 typlen, bool typbyval)
+{
+ AclResult aclresult;
+
+ /* Check permissions */
+ aclresult = pg_class_aclcheck(varid, GetUserId(), ACL_SELECT);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, get_schemavar_name(varid));
+
+ return GetSchemaVariable(varid, isNull, typid, typmod, typlen, typbyval);
+}
+
+/*
+ * V1 function API
+ *
+ * void set_schema_variable(var regclass, value anyelement);
+ * anyelement get_schema_variable(var regclass, expected_type anyelement)
+ *
+ */
+Datum
+set_schema_variable(PG_FUNCTION_ARGS)
+{
+ Oid varid;
+ Datum value;
+ bool isNull;
+ Oid typid;
+ int16 typlen;
+ bool typbyval;
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("null value not allowed for variable identity")));
+
+ varid = PG_GETARG_OID(0);
+
+ if (!PG_ARGISNULL(1))
+ {
+ value = PG_GETARG_DATUM(1);
+ isNull = false;
+ }
+ else
+ {
+ value = (Datum) 0;
+ isNull = true;
+ }
+
+ typid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (typid == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+
+ get_typlenbyval(typid, &typlen, &typbyval);
+ SetSchemaVariableSecure(varid, value, isNull, typid, -1, typlen, typbyval);
+
+ PG_RETURN_VOID();
+}
+
+Datum
+get_schema_variable(PG_FUNCTION_ARGS)
+{
+ Oid varid;
+ Oid typid;
+ int16 typlen;
+ bool typbyval;
+ bool isNull;
+ Datum result;
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("null value not allowed for variable identity")));
+
+ varid = PG_GETARG_OID(0);
+
+ typid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (typid == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+
+ get_typlenbyval(typid, &typlen, &typbyval);
+ result = GetSchemaVariableSecure(varid, &isNull, typid, -1, typlen, typbyval);
+
+ if (isNull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/*
+ * Results:
+ *=
+
+1. The schema variables are similar to temporary tables - but the data are not saved
+ in 8KB blocks, so new storage for Pg storage manager should be created.
+
+2. We should to work with typmod, so pg_attribute entry should be created anytime.
+
+3. A risk of collisions of variable and table name will be reduced, when variables
+ and tables cannot to have same name.
+
+4. If schema variables are pg_class based, then some current syntax has sense
+
+ INSERT INTO schema.variable SELECT xxx
+ maybe (but it is not consistent with PostgreSQL SQL, but consistent with PLpgSQL):
+ SELECT * INTO schema.variable FROM xxx
+
+5. LET cmd can be implemented as CMD (like INSERT, UPDATE, DELETE) or Utility (like
+ CreateTableAsSelect). Prefer first option, because there can be prepared, can be
+ used together with EXPLAIN, etc.
+
+ Expected form:
+ LET foo = (SELECT id FROM boo WHERE some = 'hello');
+
+ so possibility to run EXPLAIN LET .. has enough benefit
+*/
\ No newline at end of file
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index c6eb3ebacf..53ea890517 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -33,6 +33,7 @@
#include "access/nbtree.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
+#include "commands/schemavar.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -723,6 +724,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
Param *param = (Param *) node;
ParamListInfo params;
+ AclResult aclresult;
switch (param->paramkind)
{
@@ -730,6 +732,23 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.opcode = EEOP_PARAM_EXEC;
scratch.d.param.paramid = param->paramid;
scratch.d.param.paramtype = param->paramtype;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ case PARAM_SCHEMA_VARIABLE:
+ /* Check permission to read schema variable */
+ aclresult = pg_class_aclcheck(param->paramid, GetUserId(), ACL_SELECT);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, get_schemavar_name(param->paramid));
+
+ scratch.opcode = EEOP_PARAM_SCHEMA_VARIABLE;
+ scratch.d.param.paramid = param->paramid;
+ scratch.d.param.paramtype = param->paramtype;
+ scratch.d.param.paramtypmod = param->paramtypmod;
+
+ get_typlenbyval(param->paramtype,
+ &scratch.d.param.paramtyplen,
+ &scratch.d.param.paramtypbyval);
+
ExprEvalPushStep(state, &scratch);
break;
case PARAM_EXTERN:
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index f646fd9c51..7a3b283039 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/schemavar.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -350,6 +351,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_SCHEMA_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1031,6 +1033,23 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_SCHEMA_VARIABLE)
+ {
+ Datum d;
+ bool isnull;
+
+ d = GetSchemaVariable(op->d.param.paramid, &isnull,
+ op->d.param.paramtype,
+ -1,
+ op->d.param.paramtyplen,
+ op->d.param.paramtypbyval);
+
+ *op->resvalue = d;
+ *op->resnull = isnull;
+
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_PARAM_CALLBACK)
{
/* allow an extension module to supply a PARAM_EXTERN value */
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 410921cc40..a1ae732ae5 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -210,6 +210,7 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
switch (queryDesc->operation)
{
case CMD_SELECT:
+ case CMD_LET:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
@@ -1119,6 +1120,36 @@ CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
errmsg("cannot change TOAST relation \"%s\"",
RelationGetRelationName(resultRel))));
break;
+ case RELKIND_VARIABLE:
+
+ /* Only LET statement is allowed */
+ if (operation != CMD_LET)
+ {
+ switch (operation)
+ {
+ case CMD_INSERT:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot insert into variable \"%s\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ case CMD_UPDATE:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot update variable \"%s\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ case CMD_DELETE:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot delete from variable \"%s\"",
+ RelationGetRelationName(resultRel))));
+ default:
+ elog(ERROR, "unrecognized CmdType: %d", (int) operation);
+ break;
+ }
+ }
+ break;
case RELKIND_VIEW:
/*
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 2a8ecbd830..f8e478aa42 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -39,6 +39,7 @@
#include "access/htup_details.h"
#include "access/xact.h"
+#include "commands/schemavar.h"
#include "commands/trigger.h"
#include "executor/execPartition.h"
#include "executor/executor.h"
@@ -68,6 +69,7 @@ static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate);
static TupleConversionMap *tupconv_map_for_subplan(ModifyTableState *node,
int whichplan);
+
/*
* Verify that the tuples to be produced by INSERT or UPDATE match the
* target relation's rowtype
@@ -1568,6 +1570,81 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
}
+
+
+
+/* ----------------------------------------------------------------
+ * ExecLet
+ *
+ * For LET, we have to update target variable,
+ * Returns NULL, there are not RETURNING clause.
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+ExecLet(ModifyTableState *mtstate,
+ TupleTableSlot *slot,
+ EState *estate,
+ bool canSetTag)
+{
+ HeapTuple tuple;
+ ResultRelInfo *resultRelInfo;
+ Relation resultRelationDesc;
+ TupleDesc tupdesc;
+ bool isnull = true;
+ Datum value;
+ Form_pg_attribute attr = NULL;
+ Oid varid;
+
+ if (slot != NULL && !slot->tts_isempty)
+ {
+ tuple = slot->tts_tuple;
+ tupdesc = slot->tts_tupleDescriptor;
+
+ Assert(tupdesc != NULL);
+
+ /* should be checked before */
+ if (tupdesc->natts != 1)
+ elog(ERROR, "unexpected number of attributes");
+
+ attr = TupleDescAttr(tupdesc, 0);
+
+ if (!slot->tts_isnull[0])
+ {
+ isnull = false;
+ value = slot->tts_values[0];
+ }
+ }
+
+ /*
+ * Now, es_result_relation_info is empty, but can be initialized
+ * to structure of used schema variable.
+ */
+ resultRelInfo = estate->es_result_relation_info;
+ resultRelationDesc = resultRelInfo->ri_RelationDesc;
+ varid = resultRelationDesc->rd_id;
+
+ if (!isnull)
+ {
+ /* expecting so variable and expression are equal */
+ SetSchemaVariable(varid, value, isnull,
+ attr->atttypid, -1,
+ attr->attlen, attr->attbyval);
+ }
+ else
+ {
+ SetSchemaVariable(varid, (Datum) 0, true,
+ InvalidOid, -1, -1, false);
+ }
+
+ if (canSetTag)
+ {
+ Assert(estate->es_processed == 0);
+ (estate->es_processed)++;
+ }
+
+ return NULL;
+}
+
/*
* Process BEFORE EACH STATEMENT triggers
*/
@@ -1598,6 +1675,9 @@ fireBSTriggers(ModifyTableState *node)
case CMD_DELETE:
ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
break;
+ case CMD_LET:
+ /* there are no trigger */
+ break;
default:
elog(ERROR, "unknown operation");
break;
@@ -1652,6 +1732,9 @@ fireASTriggers(ModifyTableState *node)
ExecASDeleteTriggers(node->ps.state, resultRelInfo,
node->mt_transition_capture);
break;
+ case CMD_LET:
+ /* variables has not triggers */
+ break;
default:
elog(ERROR, "unknown operation");
break;
@@ -2056,6 +2139,9 @@ ExecModifyTable(PlanState *pstate)
&node->mt_epqstate, estate,
NULL, true, node->canSetTag);
break;
+ case CMD_LET:
+ slot = ExecLet(node, slot, estate, node->canSetTag);
+ break;
default:
elog(ERROR, "unknown operation");
break;
@@ -2562,6 +2648,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
break;
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_LET:
junk_filter_needed = true;
break;
default:
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 9fc4431b80..310bc3f2c7 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -2404,6 +2404,9 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
else
res = SPI_OK_UPDATE;
break;
+ case CMD_LET:
+ res = SPI_OK_UTILITY;
+ break;
default:
return SPI_ERROR_OPUNKNOWN;
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index fd3001c493..6d57e01179 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3055,6 +3055,17 @@ _copySelectStmt(const SelectStmt *from)
return newnode;
}
+static LetStmt *
+_copyLetStmt(const LetStmt *from)
+{
+ LetStmt *newnode = makeNode(LetStmt);
+
+ COPY_NODE_FIELD(variable);
+ COPY_NODE_FIELD(selectStmt);
+
+ return newnode;
+}
+
static SetOperationStmt *
_copySetOperationStmt(const SetOperationStmt *from)
{
@@ -5090,6 +5101,9 @@ copyObjectImpl(const void *from)
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
+ case T_LetStmt:
+ retval = _copyLetStmt(from);
+ break;
case T_SetOperationStmt:
retval = _copySetOperationStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 7d2aa1a2d3..928cf63092 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1067,6 +1067,15 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
return true;
}
+static bool
+_equalLetStmt(const LetStmt *a, const LetStmt *b)
+{
+ COMPARE_NODE_FIELD(variable);
+ COMPARE_NODE_FIELD(selectStmt);
+
+ return true;
+}
+
static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
@@ -3227,6 +3236,9 @@ equal(const void *a, const void *b)
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
break;
+ case T_LetStmt:
+ retval = _equalLetStmt(a, b);
+ break;
case T_SetOperationStmt:
retval = _equalSetOperationStmt(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 6c76c41ebe..8d24818c9f 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3444,6 +3444,16 @@ raw_expression_tree_walker(Node *node,
return true;
}
break;
+ case T_LetStmt:
+ {
+ LetStmt *stmt = (LetStmt *) node;
+
+ if (walker(stmt->variable, context))
+ return true;
+ if (walker(stmt->selectStmt, context))
+ return true;
+ }
+ break;
case T_A_Expr:
{
A_Expr *expr = (A_Expr *) node;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 89f27ce0eb..f4d8756487 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1251,12 +1251,15 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
* PARAM_EXEC Params listed in safe_param_ids, meaning they could be
* either generated within the worker or can be computed in master and
* then their value can be passed to the worker.
+ * PARAM_SCHEMA_VARIABLE params are newer changed by workers, so they can be
+ * safe.
*/
else if (IsA(node, Param))
{
Param *param = (Param *) node;
- if (param->paramkind == PARAM_EXTERN)
+ if (param->paramkind == PARAM_EXTERN ||
+ param->paramkind == PARAM_SCHEMA_VARIABLE)
return false;
if (param->paramkind != PARAM_EXEC ||
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index e7b2bc7e73..f22eab422e 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -42,6 +42,7 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
#include "utils/rel.h"
@@ -54,6 +55,7 @@ static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
static List *transformInsertRow(ParseState *pstate, List *exprlist,
List *stmtcols, List *icolumns, List *attrnos,
bool strip_indirection);
+static Query *transformLetStmt(ParseState *pstate, LetStmt *stmt);
static OnConflictExpr *transformOnConflictClause(ParseState *pstate,
OnConflictClause *onConflictClause);
static int count_rowexpr_columns(ParseState *pstate, Node *expr);
@@ -263,6 +265,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
+ case T_LetStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
@@ -300,6 +303,10 @@ transformStmt(ParseState *pstate, Node *parseTree)
}
break;
+ case T_LetStmt:
+ result = transformLetStmt(pstate, (LetStmt *) parseTree);
+ break;
+
/*
* Special cases
*/
@@ -358,6 +365,7 @@ analyze_requires_snapshot(RawStmt *parseTree)
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
+ case T_LetStmt:
result = true;
break;
@@ -1532,6 +1540,207 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
return qry;
}
+/*
+ * transformLetStmt -
+ * transform an Let Statement
+ */
+static Query *
+transformLetStmt(ParseState *pstate, LetStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ List *exprList = NIL;
+ List *exprListCoer = NIL;
+ List *sub_rtable;
+ List *sub_namespace;
+ RangeTblEntry *rte;
+ RangeTblRef *rtr;
+ ListCell *lc;
+ AclMode targetPerms;
+ ParseState *sub_pstate;
+ Query *selectQuery;
+ int i = 0;
+
+ Relation rd;
+ Oid vartypid = InvalidOid;
+
+ /* There can't be any outer WITH to worry about */
+ Assert(pstate->p_ctenamespace == NIL);
+
+ qry->commandType = CMD_LET;
+ pstate->p_is_let = true;
+
+ /*
+ * If a non-nil rangetable/namespace was passed in, and we are doing
+ * INSERT/SELECT, arrange to pass the rangetable/namespace down to the
+ * SELECT. This can only happen if we are inside a CREATE RULE, and in
+ * that case we want the rule's OLD and NEW rtable entries to appear as
+ * part of the SELECT's rtable, not as outer references for it. (Kluge!)
+ * The SELECT's joinlist is not affected however. We must do this before
+ * adding the target table to the INSERT's rtable.
+ */
+ sub_rtable = pstate->p_rtable;
+ pstate->p_rtable = NIL;
+ sub_namespace = pstate->p_namespace;
+ pstate->p_namespace = NIL;
+
+ targetPerms = ACL_UPDATE;
+ qry->resultRelation = setTargetTable(pstate, stmt->variable,
+ false, false, targetPerms);
+
+ rd = pstate->p_target_relation;
+ vartypid = rd->rd_rel->reloftype;
+
+ /*
+ * We make the sub-pstate a child of the outer pstate so that it can
+ * see any Param definitions supplied from above. Since the outer
+ * pstate's rtable and namespace are presently empty, there are no
+ * side-effects of exposing names the sub-SELECT shouldn't be able to
+ * see.
+ */
+ sub_pstate = make_parsestate(pstate);
+
+ /*
+ * Process the source SELECT.
+ *
+ * It is important that this be handled just like a standalone SELECT;
+ * otherwise the behavior of SELECT within INSERT might be different
+ * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
+ * bugs of just that nature...)
+ *
+ * The sole exception is that we prevent resolving unknown-type
+ * outputs as TEXT. This does not change the semantics since if the
+ * column type matters semantically, it would have been resolved to
+ * something else anyway. Doing this lets us resolve such outputs as
+ * the target column's type, which we handle below.
+ */
+ sub_pstate->p_rtable = sub_rtable;
+ sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */
+ sub_pstate->p_namespace = sub_namespace;
+ sub_pstate->p_resolve_unknowns = false;
+
+ selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
+
+ free_parsestate(sub_pstate);
+
+ /* The grammar should have produced a SELECT */
+ if (!IsA(selectQuery, Query) ||
+ selectQuery->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in LET ... SELECT");
+
+ /*
+ * Make the source be a subquery in the LET's rangetable, and add
+ * it to the LET's joinlist.
+ */
+ rte = addRangeTableEntryForSubquery(pstate,
+ selectQuery,
+ makeAlias("*SELECT*", NIL),
+ false,
+ false);
+ rtr = makeNode(RangeTblRef);
+ /* assume new rte is at end */
+ rtr->rtindex = list_length(pstate->p_rtable);
+ Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
+ pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
+
+ /*----------
+ * Generate an expression list for the LET that selects all the
+ * non-resjunk columns from the subquery. (LET's tlist must be
+ * separate from the subquery's tlist because we may add datatype
+ * coercions, etc.)
+ *----------
+ */
+ exprList = NIL;
+ foreach(lc, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+ Expr *expr;
+
+ if (tle->resjunk)
+ continue;
+ if (tle->expr &&
+ (IsA(tle->expr, Const) ||IsA(tle->expr, Param)) &&
+ exprType((Node *) tle->expr) == UNKNOWNOID)
+ expr = tle->expr;
+ else
+ {
+ Var *var = makeVarFromTargetEntry(rtr->rtindex, tle);
+
+ var->location = exprLocation((Node *) tle->expr);
+ expr = (Expr *) var;
+ }
+ exprList = lappend(exprList, expr);
+ }
+
+ /*
+ * Because supports only scalar variables, we can only simple
+ * transformations and checks here.
+ */
+ if (list_length(exprList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expression is not scalar value"),
+ parser_errposition(pstate,
+ exprLocation((Node *) exprList))));
+
+ exprListCoer = NIL;
+ foreach(lc, exprList)
+ {
+ Node *orig_expr = (Node*) lfirst(lc);
+ Oid exprtypid = exprType((Node *) orig_expr);
+ Expr *expr;
+
+ expr = (Expr *)
+ coerce_to_target_type(pstate,
+ orig_expr, exprtypid,
+ vartypid, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+
+ if (expr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable \"%s\" is of type %s"
+ " but expression is of type %s",
+ RelationGetRelationName(rd),
+ format_type_be(vartypid),
+ format_type_be(exprtypid)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *) orig_expr))));
+
+ exprListCoer = lappend(exprListCoer, expr);
+ }
+
+ /*
+ * Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
+ */
+ rte = pstate->p_target_rangetblentry;
+ qry->targetList = NIL;
+ foreach(lc, exprList)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ tle = makeTargetEntry(expr,
+ i + 1,
+ FigureColname((Node *)expr),
+ false);
+ qry->targetList = lappend(qry->targetList, tle);
+ }
+
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+ qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+
+ assign_query_collations(pstate, qry);
+
+ return qry;
+}
+
/*
* transformSetOperationStmt -
* transforms a set-operations tree
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5329432f25..d2a264d1e1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -257,8 +257,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSchemaVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -268,7 +268,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DropTransformStmt
DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
- ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
+ LetStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -646,7 +646,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+ LEADING LEAKPROOF LEAST LEFT LET LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -682,8 +682,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -873,6 +873,7 @@ stmt :
| CreatePLangStmt
| CreateSchemaStmt
| CreateSeqStmt
+ | CreateSchemaVarStmt
| CreateStmt
| CreateSubscriptionStmt
| CreateStatsStmt
@@ -914,6 +915,7 @@ stmt :
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
+ | LetStmt
| LockStmt
| NotifyStmt
| PrepareStmt
@@ -1374,6 +1376,7 @@ schema_stmt:
CreateStmt
| IndexStmt
| CreateSeqStmt
+ | CreateSchemaVarStmt
| CreateTrigStmt
| GrantStmt
| ViewStmt
@@ -1802,7 +1805,12 @@ DiscardStmt:
n->target = DISCARD_SEQUENCES;
$$ = (Node *) n;
}
-
+ | DISCARD VARIABLES
+ {
+ DiscardStmt *n = makeNode(DiscardStmt);
+ n->target = DISCARD_VARIABLES;
+ $$ = (Node *) n;
+ }
;
@@ -4267,6 +4275,34 @@ NumericOnly_list: NumericOnly { $$ = list_make1($1); }
| NumericOnly_list ',' NumericOnly { $$ = lappend($1, $3); }
;
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE VARIABLE seqname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSchemaVarStmt:
+ CREATE OptTemp VARIABLE qualified_name opt_as Typename
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $4->relpersistence = $2;
+ n->variable = $4;
+ n->typeName = $6;
+ n->if_not_exists = false;
+ $$ = (Node *)n;
+ }
+ | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $7->relpersistence = $2;
+ n->variable = $7;
+ n->typeName = $9;
+ n->if_not_exists = true;
+ $$ = (Node *)n;
+ }
+ ;
+
/*****************************************************************************
*
* QUERIES :
@@ -6315,6 +6351,7 @@ drop_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name_list */
@@ -6584,6 +6621,7 @@ comment_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -6722,6 +6760,7 @@ security_label_type_any_name:
| TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -7047,6 +7086,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| FOREIGN DATA_P WRAPPER name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7159,6 +7206,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
| ALL FUNCTIONS IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7341,6 +7396,7 @@ defacl_privilege_target:
| FUNCTIONS { $$ = OBJECT_FUNCTION; }
| ROUTINES { $$ = OBJECT_FUNCTION; }
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
;
@@ -10645,7 +10701,8 @@ ExplainableStmt:
| CreateAsStmt
| CreateMatViewStmt
| RefreshMatViewStmt
- | ExecuteStmt /* by default all are $$=$1 */
+ | ExecuteStmt
+ | LetStmt /* by default all are $$=$1 */
;
explain_option_list:
@@ -10702,7 +10759,8 @@ PreparableStmt:
SelectStmt
| InsertStmt
| UpdateStmt
- | DeleteStmt /* by default all are $$=$1 */
+ | DeleteStmt
+ | LetStmt /* by default all are $$=$1 */
;
/*****************************************************************************
@@ -11101,6 +11159,30 @@ opt_hold: /* EMPTY */ { $$ = 0; }
| WITHOUT HOLD { $$ = 0; }
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * LET STATEMENTS
+ *
+ *****************************************************************************/
+LetStmt: LET qualified_name '=' a_expr
+ {
+ LetStmt *n = makeNode(LetStmt);
+ SelectStmt *select = makeNode(SelectStmt);
+ ResTarget *res = makeNode(ResTarget);
+
+ res->name = NULL;
+ res->indirection = NIL;
+ res->val = (Node *) $4;
+ res->location = @4;
+ select->targetList = list_make1(res);
+ n->variable = $2;
+ n->selectStmt = (Node *) select;
+
+ $$ = (Node *) n;
+ }
+ ;
+
/*****************************************************************************
*
* QUERY:
@@ -15056,6 +15138,7 @@ unreserved_keyword:
| LARGE_P
| LAST_P
| LEAKPROOF
+ | LET
| LEVEL
| LISTEN
| LOAD
@@ -15202,6 +15285,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index b2f5e46e3b..cbf757d059 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -509,6 +509,7 @@ static Node *
transformColumnRef(ParseState *pstate, ColumnRef *cref)
{
Node *node = NULL;
+ Node *variable = NULL;
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
@@ -749,6 +750,70 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break;
}
+ /*
+ * Try to identify column ref as variable. Possible variants are
+ *
+ * A .. variable name
+ * A.B .. qualified variable name
+ */
+ switch (list_length(cref->fields))
+ {
+ case 1:
+ {
+ Node *field1 = (Node *) linitial(cref->fields);
+
+ if (IsA(field1, String))
+ {
+ char *varname = strVal(field1);
+
+ /* Try to identify as an unqualified column */
+ variable = toSchemaVariable(pstate,
+ NULL, varname,
+ cref->location);
+ }
+ break;
+ }
+ case 2:
+ {
+ Node *field1 = (Node *) linitial(cref->fields);
+ Node *field2 = (Node *) lsecond(cref->fields);
+
+ if (IsA(field1, String) && IsA(field2, String))
+ {
+ char *nspname = strVal(field1);
+ char *varname = strVal(field2);
+
+ /* Try to identify as an unqualified column */
+ variable = toSchemaVariable(pstate,
+ nspname, varname,
+ cref->location);
+ }
+ break;
+ }
+ default:
+
+ /*
+ * There can be another variants, more when composite variables
+ * will be supported. Currently only scalars are supported, so
+ * there are not necessary to solve other questions.
+ *
+ * do nothing
+ */
+ break;
+ }
+
+ if (variable != NULL)
+ {
+ if (node != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+
+ node = variable;
+ }
+
/*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 2625da5327..f7d9a0c939 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1150,6 +1150,7 @@ parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode)
setup_parser_errposition_callback(&pcbstate, pstate, relation->location);
rel = heap_openrv_extended(relation, lockmode, true);
+
if (rel == NULL)
{
if (relation->schemaname)
@@ -1180,6 +1181,24 @@ parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode)
relation->relname)));
}
}
+
+ /*
+ * RELKIND_VARIABLE can be used only in LET command.
+ * Probably this check can be done elsewhere, but here I
+ * have a used relation and parse state together first time.
+ */
+ if (rel->rd_rel->relkind == RELKIND_VARIABLE && !pstate->p_is_let)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an schema variable",
+ RelationGetRelationName(rel))));
+
+ if (pstate->p_is_let && rel->rd_rel->relkind != RELKIND_VARIABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an schema variable",
+ RelationGetRelationName(rel))));
+
cancel_parser_errposition_callback(&pcbstate);
return rel;
}
@@ -3360,3 +3379,42 @@ isQueryUsingTempRelation_walker(Node *node, void *context)
isQueryUsingTempRelation_walker,
context);
}
+
+/*
+ * Try to replace ColumnRef by Param related to variable
+ */
+Node *
+toSchemaVariable(ParseState *pstate, char *nspname, char *varname, int location)
+{
+ Oid varid;
+ Param *param = NULL;
+
+ varid = RangeVarGetRelid(makeRangeVar(nspname, varname, -1), NoLock, true);
+ if (OidIsValid(varid))
+ {
+ HeapTuple tp;
+ Form_pg_class vartup;
+
+ /* now, type info for schema variable is collected */
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(varid));
+ if (HeapTupleIsValid(tp))
+ {
+ vartup = (Form_pg_class) GETSTRUCT(tp);
+
+ if (vartup->relkind == RELKIND_VARIABLE)
+ {
+ param = makeNode(Param);
+ param->paramkind = PARAM_SCHEMA_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = vartup->reloftype;
+ param->paramtypmod = -1;
+ param->paramcollid = get_typcollation(param->paramtype);
+ param->location = location;
+ }
+
+ ReleaseSysCache(tp);
+ }
+ }
+
+ return (Node *) param;
+}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 1d35815fcf..49ea3b6e8b 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -106,6 +106,7 @@ typedef struct
List *views; /* CREATE VIEW items */
List *indexes; /* CREATE INDEX items */
List *triggers; /* CREATE TRIGGER items */
+ List *variables; /* CREATE VARIABLE items */
List *grants; /* GRANT items */
} CreateSchemaStmtContext;
@@ -3178,6 +3179,7 @@ transformCreateSchemaStmt(CreateSchemaStmt *stmt)
cxt.views = NIL;
cxt.indexes = NIL;
cxt.triggers = NIL;
+ cxt.variables = NIL;
cxt.grants = NIL;
/*
@@ -3243,6 +3245,14 @@ transformCreateSchemaStmt(CreateSchemaStmt *stmt)
}
break;
+ case T_CreateSchemaVarStmt:
+ {
+ CreateSchemaVarStmt *elp = (CreateSchemaVarStmt *) element;
+
+ setSchemaName(cxt.schemaname, &elp->variable->schemaname);
+ cxt.variables = lappend(cxt.variables, element);
+ }
+
case T_GrantStmt:
cxt.grants = lappend(cxt.grants, element);
break;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 32e3798972..9b0d84a5a5 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3337,7 +3337,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
rt_entry_relation,
parsetree->resultRelation, NULL);
}
- else if (event == CMD_DELETE)
+ else if (event == CMD_DELETE || event == CMD_LET)
{
/* Nothing to do here */
}
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 66cc5c35c6..34ddb79a3d 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -193,6 +193,10 @@ ProcessQuery(PlannedStmt *plan,
"DELETE " UINT64_FORMAT,
queryDesc->estate->es_processed);
break;
+ case CMD_LET:
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "LET ");
+ break;
default:
strcpy(completionTag, "???");
break;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 3abe7d6155..27a21c48da 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavar.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
@@ -112,6 +113,7 @@ CommandIsReadOnly(PlannedStmt *pstmt)
case CMD_DELETE:
return false;
case CMD_UTILITY:
+ case CMD_LET:
/* For now, treat all utility commands as read/write */
return false;
default:
@@ -177,6 +179,7 @@ check_xact_readonly(Node *parsetree)
case T_CreateSchemaStmt:
case T_CreateSeqStmt:
case T_CreateStmt:
+ case T_CreateSchemaVarStmt:
case T_CreateTableAsStmt:
case T_RefreshMatViewStmt:
case T_CreateTableSpaceStmt:
@@ -1474,6 +1477,10 @@ ProcessUtilitySlow(ParseState *pstate,
address = AlterSequence(pstate, (AlterSeqStmt *) parsetree);
break;
+ case T_CreateSchemaVarStmt:
+ address = DefineSchemaVariable(pstate, (CreateSchemaVarStmt *) parsetree);
+ break;
+
case T_CreateTableAsStmt:
address = ExecCreateTableAs((CreateTableAsStmt *) parsetree,
queryString, params, queryEnv,
@@ -2095,6 +2102,10 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ case T_LetStmt:
+ tag = "LET";
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
@@ -2259,6 +2270,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_INDEX:
tag = "DROP INDEX";
break;
+ case OBJECT_VARIABLE:
+ tag = "DROP VARIABLE";
+ break;
case OBJECT_TYPE:
tag = "DROP TYPE";
break;
@@ -2513,6 +2527,10 @@ CreateCommandTag(Node *parsetree)
tag = "ALTER SEQUENCE";
break;
+ case T_CreateSchemaVarStmt:
+ tag = "CREATE VARIABLE";
+ break;
+
case T_DoStmt:
tag = "DO";
break;
@@ -2630,6 +2648,9 @@ CreateCommandTag(Node *parsetree)
case DISCARD_SEQUENCES:
tag = "DISCARD SEQUENCES";
break;
+ case DISCARD_VARIABLES:
+ tag = "DISCARD VARIABLES";
+ break;
default:
tag = "???";
}
@@ -2834,6 +2855,9 @@ CreateCommandTag(Node *parsetree)
case CMD_DELETE:
tag = "DELETE";
break;
+ case CMD_LET:
+ tag = "LET";
+ break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
@@ -2952,6 +2976,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ case T_LetStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
@@ -3405,6 +3433,7 @@ GetCommandLogLevel(Node *parsetree)
switch (stmt->commandType)
{
case CMD_SELECT:
+ case CMD_LET:
lev = LOGSTMT_ALL;
break;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 0cfc297b65..fcd695836a 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -808,6 +808,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c5f5a1ca3f..ba592be4ae 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -41,6 +41,7 @@
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
+#include "commands/schemavar.h"
#include "common/keywords.h"
#include "executor/spi.h"
#include "funcapi.h"
@@ -379,6 +380,7 @@ static void get_update_query_targetlist_def(Query *query, List *targetList,
deparse_context *context,
RangeTblEntry *rte);
static void get_delete_query_def(Query *query, deparse_context *context);
+static void get_let_query_def(Query *query, deparse_context *context);
static void get_utility_query_def(Query *query, deparse_context *context);
static void get_basic_select_query(Query *query, deparse_context *context,
TupleDesc resultDesc);
@@ -4926,6 +4928,10 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
get_delete_query_def(query, &context);
break;
+ case CMD_LET:
+ get_let_query_def(query, &context);
+ break;
+
case CMD_NOTHING:
appendStringInfoString(buf, "NOTHING");
break;
@@ -6134,6 +6140,58 @@ get_insert_query_def(Query *query, deparse_context *context)
}
}
+/* ----------
+ * get_let_query_def - Parse back an LET parsetree
+ * ----------
+ */
+static void
+get_let_query_def(Query *query, deparse_context *context)
+{
+ StringInfo buf = context->buf;
+ RangeTblEntry *select_rte = NULL;
+ RangeTblEntry *rte;
+ ListCell *l;
+
+ /*
+ * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
+ * single RTE for the SELECT or VALUES. Plain VALUES has neither.
+ */
+ foreach(l, query->rtable)
+ {
+ rte = (RangeTblEntry *) lfirst(l);
+
+ if (rte->rtekind == RTE_SUBQUERY)
+ {
+ if (select_rte)
+ elog(ERROR, "too many subquery RTEs in INSERT");
+ select_rte = rte;
+ }
+ }
+
+ /*
+ * Start the query with INSERT INTO relname
+ */
+ rte = rt_fetch(query->resultRelation, query->rtable);
+ Assert(rte->rtekind == RTE_RELATION);
+
+ if (PRETTY_INDENT(context))
+ {
+ context->indentLevel += PRETTYINDENT_STD;
+ appendStringInfoChar(buf, ' ');
+ }
+ appendStringInfo(buf, "LET %s ",
+ generate_relation_name(rte->relid, NIL));
+
+ appendStringInfo(buf, " = ");
+
+ if (select_rte)
+ {
+ /* Add the SELECT */
+ get_query_def(select_rte->subquery, buf, NIL, NULL,
+ context->prettyFlags, context->wrapColumn,
+ context->indentLevel);
+ }
+}
/* ----------
* get_update_query_def - Parse back an UPDATE parsetree
@@ -7208,6 +7266,13 @@ get_parameter(Param *param, deparse_context *context)
deparse_namespace *dpns;
ListCell *ancestor_cell;
+ if (param->paramkind == PARAM_SCHEMA_VARIABLE)
+ {
+ appendStringInfo(context->buf, "%s", get_schemavar_name(param->paramid));
+
+ return;
+ }
+
/*
* If it's a PARAM_EXEC parameter, try to locate the expression from which
* the parameter was computed. Note that failing to find a referent isn't
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 3560318749..ad0030c0dd 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -794,6 +794,7 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
case 'i':
case 's':
case 'E':
+ case 'V':
success = listTables(&cmd[1], pattern, show_verbose, show_system);
break;
case 'r':
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 466a78004b..c272de2baa 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1683,6 +1683,42 @@ describeOneTableDetails(const char *schemaname,
retval = true;
goto error_return; /* not an error, just return early */
}
+ else if (tableinfo.relkind == RELKIND_VARIABLE)
+ {
+ PGresult *res = NULL;
+ printQueryOpt myopt = pset.popt;
+
+ printfPQExpBuffer(&buf,
+ "SELECT pg_catalog.format_type(reloftype, NULL) AS \"%s\"\n"
+ "FROM pg_catalog.pg_class\n"
+ "WHERE oid = '%s';",
+ gettext_noop("Type"),
+ oid);
+
+ res = PSQLexec(buf.data);
+ if (!res)
+ goto error_return;
+
+ /* Did we get anything? */
+ if (PQntuples(res) == 0)
+ {
+ if (!pset.quiet)
+ psql_error("Did not find any variable with OID %s.\n", oid);
+ goto error_return;
+ }
+
+ printfPQExpBuffer(&title, _("Schema variable \"%s.%s\""),
+ schemaname, relationname);
+
+ myopt.title = title.data;
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+ PQclear(res);
+
+ retval = true;
+ goto error_return; /* not an error, just return early */
+ }
/*
* Get column info
@@ -3365,6 +3401,7 @@ listDbRoleSettings(const char *pattern, const char *pattern2)
* m - materialized views
* s - sequences
* E - foreign table (Note: different from 'f', the relkind value)
+ * V - schema variable
* (any order of the above is fine)
*/
bool
@@ -3376,6 +3413,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
bool showMatViews = strchr(tabtypes, 'm') != NULL;
bool showSeq = strchr(tabtypes, 's') != NULL;
bool showForeign = strchr(tabtypes, 'E') != NULL;
+ bool showVariables = strchr(tabtypes, 'V') != NULL;
PQExpBufferData buf;
PGresult *res;
@@ -3383,8 +3421,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
static const bool translate_columns[] = {false, false, true, false, false, false, false};
/* If tabtypes is empty, we default to \dtvmsE (but see also command.c) */
- if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign))
- showTables = showViews = showMatViews = showSeq = showForeign = true;
+ if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign || showVariables))
+ showTables = showViews = showMatViews = showSeq = showForeign = showVariables = true;
initPQExpBuffer(&buf);
@@ -3405,6 +3443,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
" WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
" WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
" WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+ " WHEN " CppAsString2(RELKIND_VARIABLE) " THEN '%s'"
" END as \"%s\",\n"
" pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
gettext_noop("Schema"),
@@ -3418,6 +3457,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
gettext_noop("foreign table"),
gettext_noop("table"), /* partitioned table */
gettext_noop("index"), /* partitioned index */
+ gettext_noop("schema variable"),
gettext_noop("Type"),
gettext_noop("Owner"));
@@ -3471,6 +3511,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL */
if (showForeign)
appendPQExpBufferStr(&buf, CppAsString2(RELKIND_FOREIGN_TABLE) ",");
+ if (showVariables)
+ appendPQExpBufferStr(&buf, CppAsString2(RELKIND_VARIABLE) ",");
appendPQExpBufferStr(&buf, "''"); /* dummy */
appendPQExpBufferStr(&buf, ")\n");
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..c5f107d814 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -60,7 +60,7 @@ extern bool listTSTemplates(const char *pattern, bool verbose);
/* \l */
extern bool listAllDbs(const char *pattern, bool verbose);
-/* \dt, \di, \ds, \dS, etc. */
+/* \dt, \di, \ds, \dS, \dvar etc. */
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
/* \dD */
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 702e742af4..2da50f7290 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
@@ -257,6 +257,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[S+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dV[S+] [PATTERN] list schema variables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 8bc4a194a5..ba5f6b0832 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -679,6 +679,20 @@ static const SchemaQuery Query_for_list_of_statistics = {
NULL
};
+static const SchemaQuery Query_for_list_of_variables = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ "c.relkind IN ('V')",
+ /* viscondition */
+ "pg_catalog.pg_table_is_visible(c.oid)",
+ /* namespace */
+ "c.relnamespace",
+ /* result */
+ "pg_catalog.quote_ident(c.relname)",
+ /* qualresult */
+ NULL
+};
/*
* Queries to get lists of names of various kinds of things, possibly
@@ -1108,6 +1122,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
{"USER MAPPING FOR", NULL, NULL},
+ {"VARIABLE", NULL, &Query_for_list_of_variables},
{"VIEW", NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
@@ -1460,7 +1475,7 @@ psql_completion(const char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
- "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LET", "LISTEN", "LOAD", "LOCK",
"MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
"RESET", "REVOKE", "ROLLBACK",
@@ -1479,7 +1494,7 @@ psql_completion(const char *text, int start, int end)
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+ "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", "\\dvar",
"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
"\\endif", "\\errverbose", "\\ev",
"\\f",
@@ -2684,6 +2699,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
COMPLETE_WITH_LIST2("GROUP", "ROLE");
+/* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
+ /* Complete CREATE VARIABLE <name> with AS */
+ else if (TailMatches3("CREATE", "VARIABLE", MatchAny))
+ COMPLETE_WITH_CONST("AS");
+ /* Complete CREATE VARIABLE <name> with AS types*/
+ else if (TailMatches4("CREATE", "VARIABLE", MatchAny, "AS"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE VIEW <name> with AS */
else if (TailMatches3("CREATE", "VIEW", MatchAny))
@@ -2839,6 +2862,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP VARIABLE */
+ else if (Matches2("DROP", "VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ else if (Matches3("DROP", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -2849,14 +2878,14 @@ psql_completion(const char *text, int start, int end)
* Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "ANALYZE", "VERBOSE");
+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "ANALYZE", "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "ANALYZE"))
- COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "VERBOSE");
+ COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "VERBOSE") ||
Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
- COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
+ COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "LET");
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
@@ -2965,6 +2994,7 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'ALL ROUTINES IN SCHEMA'"
" UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
" UNION SELECT 'ALL TABLES IN SCHEMA'"
+ " UNION SELECT 'ALL VARIABLES IN SCHEMA'"
" UNION SELECT 'DATABASE'"
" UNION SELECT 'DOMAIN'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
@@ -2978,14 +3008,16 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'SEQUENCE'"
" UNION SELECT 'TABLE'"
" UNION SELECT 'TABLESPACE'"
- " UNION SELECT 'TYPE'");
+ " UNION SELECT 'TYPE'"
+ " UNION SELECT 'VARIABLE'");
}
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
- COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA",
+ COMPLETE_WITH_LIST6("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
- "TABLES IN SCHEMA");
+ "TABLES IN SCHEMA",
+ "VARIABLES IN SCHEMA");
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
@@ -3015,6 +3047,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
else if (TailMatches1("TABLE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
+ else if (TailMatches1("VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
else if (TailMatches1("TABLESPACE"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches1("TYPE"))
@@ -3171,7 +3205,7 @@ psql_completion(const char *text, int start, int end)
/* PREPARE xx AS */
else if (Matches3("PREPARE", MatchAny, "AS"))
- COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
+ COMPLETE_WITH_LIST5("SELECT", "UPDATE", "INSERT", "DELETE FROM", "LET");
/*
* PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -3390,6 +3424,14 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
COMPLETE_WITH_CONST("=");
+/* LET --- can be inside EXPLAIN, PREPARE etc */
+ /* If prev. word is LET suggest a list of variables */
+ else if (TailMatches1("LET"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ /* Complete LET <variable> with "=" */
+ else if (TailMatches2("LET", MatchAny))
+ COMPLETE_WITH_CONST("=");
+
/* USER MAPPING */
else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 26b1866c69..c5146fc138 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -167,6 +167,7 @@ DESCR("");
#define RELKIND_FOREIGN_TABLE 'f' /* foreign table */
#define RELKIND_PARTITIONED_TABLE 'p' /* partitioned table */
#define RELKIND_PARTITIONED_INDEX 'I' /* partitioned index */
+#define RELKIND_VARIABLE 'V' /* schema variable */
#define RELPERSISTENCE_PERMANENT 'p' /* regular table */
#define RELPERSISTENCE_UNLOGGED 'u' /* unlogged permanent table */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 11b306037d..13232d7a43 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -71,5 +71,6 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* PG_DEFAULT_ACL_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f01648c961..600d3d5849 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5533,6 +5533,12 @@ DESCR("list of files in the WAL directory");
DATA(insert OID = 5028 ( satisfies_hash_partition PGNSP PGUID 12 1 0 2276 0 f f f f f f i s 4 0 16 "26 23 23 2276" _null_ "{i,i,i,v}" _null_ _null_ _null_ satisfies_hash_partition _null_ _null_ _null_ ));
DESCR("hash partition CHECK constraint");
+/* schema variables function interface */
+DATA(insert OID = 6122 ( get_schema_variable PGNSP PGUID 12 1 0 0 0 f f f f f f v r 2 0 2283 "2205 2283" _null_ _null_ _null_ _null_ _null_ get_schema_variable _null_ _null_ _null_ ));
+DESCR("returns value of schema variable");
+DATA(insert OID = 6123 ( set_schema_variable PGNSP PGUID 12 1 0 0 0 f f f f f f v r 2 0 2278 "2205 2283" _null_ _null_ _null_ _null_ _null_ set_schema_variable _null_ _null_ _null_ ));
+DESCR("returns value of schema variable");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/commands/schemavar.h b/src/include/commands/schemavar.h
new file mode 100644
index 0000000000..6f65b1f1d3
--- /dev/null
+++ b/src/include/commands/schemavar.h
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavar.h
+ * prototypes for schemavar.c.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/schemavar.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SCHEMAVAR_H
+#define SCHEMAVAR_H
+
+#include "catalog/objectaddress.h"
+#include "nodes/parsenodes.h"
+#include "parser/parse_node.h"
+
+extern ObjectAddress DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *var);
+
+extern void ResetSchemaVariablesCache(void);
+
+extern char *get_schemavar_name(Oid varid);
+
+extern void SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod, int16 typlen, bool typbyval);
+extern Datum GetSchemaVariable(Oid varid, bool *isNull, Oid typid, int32 typmod, int16 typlen, bool typbyval);
+extern void SetSchemaVariableSecure(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod, int16 typlen, bool typbyval);
+extern Datum GetSchemaVariableSecure(Oid varid, bool *isNull, Oid typid, int32 typmod, int16 typlen, bool typbyval);
+
+#endif
\ No newline at end of file
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 117fc892f4..a282f1e4e0 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_SCHEMA_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -342,11 +343,14 @@ typedef struct ExprEvalStep
TupleDesc argdesc;
} nulltest_row;
- /* for EEOP_PARAM_EXEC/EXTERN */
+ /* for EEOP_PARAM_EXEC/EXTERN/VARIABLE */
struct
{
- int paramid; /* numeric ID for parameter */
- Oid paramtype; /* OID of parameter's datatype */
+ int paramid; /* numeric ID for parameter */
+ Oid paramtype; /* OID of parameter's datatype */
+ int32 paramtypmod; /* typmod of param (not used yet) */
+ int16 paramtyplen; /* expected length */
+ bool paramtypbyval; /* is passed by value */
} param;
/* for EEOP_PARAM_CALLBACK */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 74b094a9c3..2f4986099d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -344,6 +344,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSchemaVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
@@ -415,6 +416,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
+ T_LetStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -657,6 +659,7 @@ typedef enum CmdType
CMD_UPDATE, /* update stmt */
CMD_INSERT, /* insert stmt */
CMD_DELETE,
+ CMD_LET,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
CMD_NOTHING /* dummy command for instead nothing rules
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 76a73b2a37..7ed5c61f5f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1486,6 +1486,14 @@ typedef struct UpdateStmt
WithClause *withClause; /* WITH clause */
} UpdateStmt;
+typedef struct LetStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* relation to insert into */
+ Node *selectStmt; /* the source SELECT/VALUES, or NULL */
+} LetStmt;
+
+
/* ----------------------
* Select Statement
*
@@ -1663,6 +1671,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -2475,6 +2484,18 @@ typedef struct AlterSeqStmt
bool missing_ok; /* skip error if a role is missing? */
} AlterSeqStmt;
+/* ----------------------
+ * Create VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSchemaVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the variable type */
+ bool if_not_exists; /* just do nothing if it already exists? */
+} CreateSchemaVarStmt;
+
/* ----------------------
* Create {Aggregate|Operator|Type} Statement
* ----------------------
@@ -3204,7 +3225,8 @@ typedef enum DiscardMode
DISCARD_ALL,
DISCARD_PLANS,
DISCARD_SEQUENCES,
- DISCARD_TEMP
+ DISCARD_TEMP,
+ DISCARD_VARIABLES
} DiscardMode;
typedef struct DiscardStmt
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..b366471940 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -229,13 +229,17 @@ typedef struct Const
* of the `paramid' field contain the SubLink's subLinkId, and
* the low-order 16 bits contain the column number. (This type
* of Param is also converted to PARAM_EXEC during planning.)
+ *
+ * PARAM_SCHEMA_VARIABLE: The parameter is a access to schema variable
+ * paramid holds varid.
*/
typedef enum ParamKind
{
PARAM_EXTERN,
PARAM_EXEC,
PARAM_SUBLINK,
- PARAM_MULTIEXPR
+ PARAM_MULTIEXPR,
+ PARAM_SCHEMA_VARIABLE
} ParamKind;
typedef struct Param
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 26af944e03..3971d7478b 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -229,6 +229,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("let", LET, UNRESERVED_KEYWORD)
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -430,6 +431,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 4e96fa7907..18ba221180 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -134,6 +134,8 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
* to process them like UPDATE. (Note this can change intra-statement, for
* cases like INSERT ON CONFLICT UPDATE.)
*
+ * p_is_let: true to process assignment expressions like LET.
+ *
* p_windowdefs: list of WindowDefs representing WINDOW and OVER clauses.
* We collect these while transforming expressions and then transform them
* afterwards (so that any resjunk tlist items needed for the sort/group
@@ -183,6 +185,7 @@ struct ParseState
Relation p_target_relation; /* INSERT/UPDATE/DELETE target rel */
RangeTblEntry *p_target_rangetblentry; /* target rel's RTE */
bool p_is_insert; /* process assignment like INSERT not UPDATE */
+ bool p_is_let; /* process assignment LET stmt */
List *p_windowdefs; /* raw representations of window clauses */
ParseExprKind p_expr_kind; /* what kind of expression we're parsing */
int p_next_resno; /* next targetlist resno to assign */
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index b9792acdae..760aaed9a8 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -129,4 +129,6 @@ extern Oid attnumTypeId(Relation rd, int attid);
extern Oid attnumCollationId(Relation rd, int attid);
extern bool isQueryUsingTempRelation(Query *query);
+extern Node *toSchemaVariable(ParseState *pstate, char *nspname, char *varname, int location);
+
#endif /* PARSE_RELATION_H */
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f4d4be8d0d..d0737a9e4b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -166,6 +166,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_SELECT|ACL_UPDATE)
/* operation codes for pg_*_aclmask */
typedef enum
diff --git a/src/test/regress/expected/schema_variables.out b/src/test/regress/expected/schema_variables.out
new file mode 100644
index 0000000000..ad700c15d8
--- /dev/null
+++ b/src/test/regress/expected/schema_variables.out
@@ -0,0 +1,236 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+-- should to fail
+CREATE VARIABLE var2 AS pg_class;
+ERROR: Composite types are not allowed as variable type.
+DROP VARIABLE var1, var2;
+-- functional interface, attention typmod is not stored
+CREATE VARIABLE var1 AS numeric(10,1);
+SELECT set_schema_variable('var1', 333);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 333
+(1 row)
+
+SELECT set_schema_variable('var1', 333::integer);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 333
+(1 row)
+
+SELECT set_schema_variable('var1', '333.55'::text);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 333.55
+(1 row)
+
+SELECT get_schema_variable('var1', null::int);
+ get_schema_variable
+---------------------
+ 334
+(1 row)
+
+SELECT get_schema_variable('var1', null::text);
+ get_schema_variable
+---------------------
+ 333.55
+(1 row)
+
+-- access rights test
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT set_schema_variable('var1', '1000'::text);
+ERROR: permission denied for schema variable var1
+SELECT get_schema_variable('var1', null::numeric);
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT SELECT ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT set_schema_variable('var1', '1000'::text);
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 333.55
+(1 row)
+
+SET ROLE TO DEFAULT;
+GRANT UPDATE ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to work
+SELECT set_schema_variable('var1', '1000'::text);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 1000
+(1 row)
+
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE VARIABLE var AS integer;
+SELECT set_schema_variable('public.var', 1234);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT public.var;
+ var
+------
+ 1234
+(1 row)
+
+DO $$
+BEGIN
+ RAISE NOTICE 'public.var is = %', public.var;
+END;
+$$;
+NOTICE: public.var is = 1234
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var;
+$$ LANGUAGE sql SECURITY DEFINER;
+SELECT secure_var();
+ secure_var
+------------
+ 1234
+(1 row)
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var;
+ERROR: permission denied for schema variable var
+-- should to work;
+SELECT secure_var();
+ secure_var
+------------
+ 1234
+(1 row)
+
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: (g.v = var)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var;
+SELECT * FROM schema_var_view;
+ var
+------
+ 1234
+(1 row)
+
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ var
+-----
+
+(1 row)
+
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+------------------------------------------------------
+ Let on public.var1
+ -> Result
+ Output: '3.14159265358979'::double precision
+(3 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+SELECT inc(1);
+ inc
+-----
+ 1
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 2
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 3
+(1 row)
+
+SELECT inc(1) FROM generate_series(1,10);
+ inc
+-----
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+(10 rows)
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP ROLE var_test_role;
+DROP VARIABLE var CASCADE;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index ad9434fb87..33fe7ee476 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml schema_variables
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 27cd49845e..22c4cac7ce 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -187,3 +187,4 @@ test: hash_part
test: indexing
test: event_trigger
test: stats
+test: schema_variables
diff --git a/src/test/regress/sql/schema_variables.sql b/src/test/regress/sql/schema_variables.sql
new file mode 100644
index 0000000000..9ee9e174f9
--- /dev/null
+++ b/src/test/regress/sql/schema_variables.sql
@@ -0,0 +1,139 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+
+-- should to fail
+CREATE VARIABLE var2 AS pg_class;
+
+DROP VARIABLE var1, var2;
+
+-- functional interface, attention typmod is not stored
+CREATE VARIABLE var1 AS numeric(10,1);
+SELECT set_schema_variable('var1', 333);
+SELECT get_schema_variable('var1', null::numeric);
+
+SELECT set_schema_variable('var1', 333::integer);
+SELECT get_schema_variable('var1', null::numeric);
+
+SELECT set_schema_variable('var1', '333.55'::text);
+SELECT get_schema_variable('var1', null::numeric);
+SELECT get_schema_variable('var1', null::int);
+SELECT get_schema_variable('var1', null::text);
+
+-- access rights test
+
+CREATE ROLE var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT set_schema_variable('var1', '1000'::text);
+SELECT get_schema_variable('var1', null::numeric);
+
+SET ROLE TO DEFAULT;
+
+GRANT SELECT ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT set_schema_variable('var1', '1000'::text);
+-- should to work
+SELECT get_schema_variable('var1', null::numeric);
+
+SET ROLE TO DEFAULT;
+
+GRANT UPDATE ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to work
+SELECT set_schema_variable('var1', '1000'::text);
+SELECT get_schema_variable('var1', null::numeric);
+
+SET ROLE TO DEFAULT;
+
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+
+CREATE VARIABLE var AS integer;
+
+SELECT set_schema_variable('public.var', 1234);
+
+SELECT public.var;
+
+DO $$
+BEGIN
+ RAISE NOTICE 'public.var is = %', public.var;
+END;
+$$;
+
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var;
+$$ LANGUAGE sql SECURITY DEFINER;
+
+SELECT secure_var();
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT public.var;
+
+-- should to work;
+SELECT secure_var();
+
+SET ROLE TO DEFAULT;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var;
+
+CREATE VIEW schema_var_view AS SELECT var;
+
+SELECT * FROM schema_var_view;
+
+\c -
+
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+
+LET var1 = pi();
+
+SELECT var1;
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+
+EXECUTE var_pp(100, 1.23456);
+
+SELECT var1;
+
+CREATE VARIABLE var3 AS int;
+
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT inc(1);
+SELECT inc(1);
+SELECT inc(1);
+
+SELECT inc(1) FROM generate_series(1,10);
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+LET var3 = 0;
+
+SET ROLE TO DEFAULT;
+
+DROP VIEW schema_var_view;
+
+DROP ROLE var_test_role;
+
+DROP VARIABLE var CASCADE;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index a42ff9794a..5f0bc22f6d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -421,6 +421,7 @@ CreateReplicationSlotCmd
CreateRoleStmt
CreateSchemaStmt
CreateSchemaStmtContext
+CreateSchemaVarStmt
CreateSeqStmt
CreateStatsStmt
CreateStmt
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-02-03 00:48 ` David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: David G. Johnston @ 2018-02-03 00:48 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
I've done a non-compilation documentation review, the diff from the poc
patch and the diff from master are attached.
Comments are inter-twined in the patch in xml comment format; though I
reiterate (some of?) them below.
On Fri, Feb 2, 2018 at 3:06 PM, Pavel Stehule <pavel.stehule@gmail.com>
wrote:
> Hi
>
> I wrote proof concept of schema variables. The patch is not nice, but the
> functionality is almost complete (for scalars only) and can be good enough
> for playing with this concept.
>
> I recap a goals (the order is random):
>
> 1. feature like PL/SQL package variables (with similar content life cycle)
> 2. available from any PL used by PostgreSQL, data can be shared between
> different PL
> 3. possibility to store short life data in fast secured storage
>
The generic use of the word secure here bothers me. I'm taking it to be
"protected by grant/revoke"-based privileges; plus session-locality.
4. possibility to pass parameters and results to/from anonymous blocks
> 5. session variables with possibility to process static code check
>
What does "process static code check" means here?
> 6. multiple API available from different environments - SQL commands, SQL
> functions, internal functions
>
I made the public aspect of this explicit in the CREATE VARIABLE doc
(though as noted below it probably belongs in section II)
> 7. data are stored in binary form
>
Thoughts during my review:
There is, for me, a cognitive dissonance between "schema variable" and
"variable value" - I'm partial to the later. Since we use "setting" for
GUCs the term variable here hopefully wouldn't cause ambiguity...
I've noticed that we don't seem to have or enforce any policy on how to
communicate "SQL standards compatibility" to the user...
We are missing the ability to alter ownership (or at least its
undocumented), and if that brings into existing ALTER VARIABLE we should
probably add ALTER TYPE TO new_type USING (cast) for completeness.
Its left for the reader to presume that because these are schema
"relations" that namespace resolution via search_path works the same as any
other relation.
I think I've answered my own question regarding DISCARD in that "variables"
discards values while if TEMP is in effect all temp variables are dropped.
Examples abound though it doesn't feel like too much: but saying "The usage
is very simple:" before giving the example in the function section seems to
be outside of our general style. A better preamble than "An example:"
would be nice but the example is so simple I could not think of anything
worth writing.
Its worth considering how both:
https://www.postgresql.org/docs/10/static/ddl.html
and
https://www.postgresql.org/docs/10/static/queries.html
could be updated to incorporate the broad picture of schema variables, with
examples, and leave the reference (SQL and functions) sections mainly
relegated to syntax and reminders.
A moderate number of lines changed are for typos and minor grammar edits.
David J.
Attachments:
[application/octet-stream] schema-variables-poc--dgj-response-diff.patch (11.5K, ../../CAKFQuwa00-4HTujbnYNy_OdZ2OfjUO3AX4R91DbQ1wPBZegCfg@mail.gmail.com/3-schema-variables-poc--dgj-response-diff.patch)
download | inline diff:
commit fb39d8d2dc798ddd44611e349dedc0a8d41b35c6
Author: David G. Johnston (DU) <davidj@dealeruplift.com>
Date: Sat Feb 3 00:11:56 2018 +0000
respose to poc
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 5031cd4d70..36e5e482a7 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15743,10 +15743,10 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
</sect1>
<sect1 id="functions-schemavar">
- <title>Functions for access to schema variables</title>
+ <title>Schema Variable Functions</title>
<indexterm zone="functions-schemavar">
- <primary>Functions for access to schema variables</primary>
+ <primary>Schema Variable Functions</primary>
<secondary>functions</secondary>
</indexterm>
@@ -15760,10 +15760,12 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
<para>
These functions allow reading and writing schema variables values.
+ If the schema variable referenced does not exist (created using <xref linkend="sql-createvariable"/>)
+ these functions will (do something...).
</para>
-
+<!-- I'm preferential to get_variable_value and set_variable_value -->
<table id="functions-schemavar-tab">
- <title>Functions for access to chema variables</title>
+ <title>Functions for access to schema variables</title>
<tgroup cols="4">
<thead>
<row>
@@ -15773,14 +15775,13 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
<entry>Description</entry>
</row>
</thead>
-
<tbody>
<row>
<entry><literal><function>get_schema_variable(<parameter>variable</parameter>, <parameter>expected type</parameter>)</function></literal></entry>
<entry><type>regclass</type>, <type>anyelement</type></entry>
<entry><type>anyelement</type></entry>
<entry>
- Returns value of schema variables coverted to expected type.
+ Returns value of schema variable converted to expected type.
</entry>
</row>
@@ -15789,16 +15790,16 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
<entry><type>regclass</type>, <type>anyelement</type></entry>
<entry><type>void</type></entry>
<entry>
- Set a value of schema variable. Value is converted to type of schema variable.
+ Sets the value of schema variable to value, after converting the input to the correct type.
</entry>
</row>
</tbody>
</tgroup>
</table>
-
+ An example:
<para>
- The usage is very simple:
+
<programlisting>
CREATE TEMP VARIABLE foo AS numeric;
SELECT set_schema_variable('foo', 345.445);
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
index 037fa087f5..c06b0e7517 100644
--- a/doc/src/sgml/ref/create_variable.sgml
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -16,7 +16,7 @@ PostgreSQL documentation
<refnamediv>
<refname>CREATE VARIABLE</refname>
- <refpurpose>define a new schema secure typed variable</refpurpose>
+ <refpurpose>define a new permissioned typed schema variable</refpurpose>
</refnamediv>
<refsynopsisdiv>
@@ -24,32 +24,39 @@ PostgreSQL documentation
CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ]
</synopsis>
</refsynopsisdiv>
+<!-- a multiple variable version of this might be useful...
+ name data_type [, name data_type] -->
<refsect1>
<title>Description</title>
<para>
<command>CREATE VARIABLE</command> creates a new schema variable.
- These variables are memory only non transactional, but typed and
- secure. The access is controlled by rights defined by command
- <command>GRANT</command> and command <command>REVOKE</command>.
+ These variables are scalar typed, non-transactional, and, like relations,
+ exist within a schema with access controlled via
+ <command>GRANT</command> and <command>REVOKE</command>.
</para>
<para>
- The schema variable is initialized to NULL value. The content of
- variable is lost when session is destroyed.
+ The value of a schema variable is session-local. Retrieving
+ a variable's value will return NULL unless its value has been set
+ to something else in the current session.
</para>
<para>
- The schema variable can be any scalar only.
- type.
+ Retrieval is done via the <function>get_schema_variable</function>dunxrion or the SQL
+ command <command>SELECT</command>. Setting of values is done via the
+ <function>set_schema_variable</function> function or the SQL command
+ <command>LET</command>.
+ Notably, while schema variables are in many ways a kind of table you cannot use
+ <command>UPDATE</command> on them.
</para>
<para>
- After a variable is created, you use the special functions
- <function>get_schema_variables</function>, <function>set_schema_variables</function>.
- type.
- </para>
+ For purposes of name uniqueness relation-like objects (e.g., tables, indexes)
+ within the same schema are considered. i.e., you cannot give a table and a
+ schema variable the same name. This is a consequence of them being treated
+ like relations for purposes of <command>SELECT</command>.
</refsect1>
<refsect1>
@@ -60,10 +67,9 @@ CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceab
<term><literal>IF NOT EXISTS</literal></term>
<listitem>
<para>
- Do not throw an error if a relation with the same name already exists.
- A notice is issued in this case. Note that there is no guarantee that
- the existing relation is anything like the variable that would have
- been created - it might not even be a variable.
+ Do not throw an error if the name already exists. A notice is issued in this case.
+ Note that type of the variable is not considered, nor could it be since the namespace
+ searched contains non-variable objects.
</para>
</listitem>
</varlistentry>
@@ -81,7 +87,7 @@ CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceab
<term><replaceable class="parameter">data_type</replaceable></term>
<listitem>
<para>
- The name (optionally schema-qualified) of the data type ofvariable to be created.
+ The name (optionally schema-qualified) of the data type of the variable to be created.
</para>
</listitem>
</varlistentry>
@@ -107,7 +113,7 @@ CREATE VARIABLE var1 AS integer;
</para>
<para>
- Set a value of this variable:
+ Set this variable's value; then retrieve it converted to numeric.
<programlisting>
CREATE VARIABLE
postgres=# select set_schema_variable('var1', 10);
@@ -129,7 +135,8 @@ postgres=# select get_schema_variable('var1', null::numeric);
<title>Compatibility</title>
<para>
- <command>CREATE VARIABLE</command> is PostgreSQL feature
+ <command>CREATE VARIABLE</command> is a PostgreSQL feature.
+ <!-- The choice of wording here seems to be left to personal preference... -->
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index b348c02e0b..395453bba0 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -79,7 +79,8 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES}
<term><literal>VARIABLES</literal></term>
<listitem>
<para>
- Releases content of all schema variables in current session.
+ Sets the value of all schema variables to NULL.
+ <!-- What happens to temporary schema variables -->
</para>
</listitem>
</varlistentry>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
index f6c2e46476..06130fd510 100644
--- a/doc/src/sgml/ref/drop_variable.sgml
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -29,8 +29,9 @@ DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [,
<title>Description</title>
<para>
- <command>DROP VARIABLE</command> removes schema variable.
+ <command>DROP VARIABLE</command> removes a schema variable.
A variable can only be dropped by its owner or a superuser.
+ <!-- this would suggest that we need an alter variable owner to command -->
</para>
</refsect1>
@@ -75,6 +76,8 @@ DROP VARIABLE var1;
<para>
<command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ <!-- create variable is a "PostgreSQL feature",
+ this is a "proprietary PostgreSQL command" ... -->
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index 7dde54ce0f..006364ebe5 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -173,6 +173,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
foreign servers,
large objects,
schemas,
+ schema variables,
or tablespaces.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
@@ -210,6 +211,8 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
For sequences, this privilege also allows the use of the
<function>currval</function> function.
For large objects, this privilege allows the object to be read.
+ For schema variables, this privilege allows the <function>get_schema_variable</function>
+ to read the variable's value.
</para>
</listitem>
</varlistentry>
@@ -245,6 +248,9 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
<function>setval</function> functions.
For large objects, this privilege allows writing or truncating the
object.
+ For schema variables, this privilege allows <command>LET</command>
+ and <function>set_schema_variable</function> to modify the schema variable's
+ value.
</para>
</listitem>
</varlistentry>
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
index b040b5e1fe..e8bf3f6dd4 100644
--- a/doc/src/sgml/ref/let.sgml
+++ b/doc/src/sgml/ref/let.sgml
@@ -16,7 +16,7 @@ PostgreSQL documentation
<refnamediv>
<refname>LET</refname>
- <refpurpose>change a schema variable</refpurpose>
+ <refpurpose>change a schema variable's value</refpurpose>
</refnamediv>
<refsynopsisdiv>
@@ -29,7 +29,7 @@ LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable
<title>Description</title>
<para>
- The <command>LET</command> command sets specified schema variable.
+ The <command>LET</command> command updates the specified schema variable' value.
</para>
</refsect1>
@@ -42,7 +42,7 @@ LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable
<term><literal>schema_variable</literal></term>
<listitem>
<para>
- Specifies that the name of schema variable.
+ The name of schema variable.
</para>
</listitem>
</varlistentry>
@@ -51,7 +51,7 @@ LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable
<term><literal>sql expression</literal></term>
<listitem>
<para>
- Any SQL expression.
+ An SQL expression, the result is cast to the schema variable's type.
</para>
</listitem>
</varlistentry>
@@ -71,6 +71,8 @@ LET myvar = (SELECT sum(val) FROM tab);
<title>Compatibility</title>
<para>
+ <!-- this feels like it needs to be more specific,
+ but I don't know enough to make it so -->
<literal>LET</literal> extends syntax defined in the SQL
standard. The standard knows <literal>SET</literal> command,
that is used for different purpouse in PostgreSQL.
[application/octet-stream] schema-variables-poc--dgj-response-full.patch (120.6K, ../../CAKFQuwa00-4HTujbnYNy_OdZ2OfjUO3AX4R91DbQ1wPBZegCfg@mail.gmail.com/4-schema-variables-poc--dgj-response-full.patch)
download | inline diff:
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 71e20f2740..fbf78e602d 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1813,7 +1813,8 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
<literal>m</literal> = materialized view,
<literal>c</literal> = composite type,
<literal>f</literal> = foreign table,
- <literal>p</literal> = partitioned table
+ <literal>p</literal> = partitioned table,
+ <literal>V</literal> = schema variable
</entry>
</row>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 487c7ff750..36e5e482a7 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15742,6 +15742,83 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
</sect1>
+ <sect1 id="functions-schemavar">
+ <title>Schema Variable Functions</title>
+
+ <indexterm zone="functions-schemavar">
+ <primary>Schema Variable Functions</primary>
+ <secondary>functions</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>get_schema_variable</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>set_schema_variable</primary>
+ </indexterm>
+
+ <para>
+ These functions allow reading and writing schema variables values.
+ If the schema variable referenced does not exist (created using <xref linkend="sql-createvariable"/>)
+ these functions will (do something...).
+ </para>
+<!-- I'm preferential to get_variable_value and set_variable_value -->
+ <table id="functions-schemavar-tab">
+ <title>Functions for access to schema variables</title>
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Function</entry>
+ <entry>Argument Type</entry>
+ <entry>Return Type</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal><function>get_schema_variable(<parameter>variable</parameter>, <parameter>expected type</parameter>)</function></literal></entry>
+ <entry><type>regclass</type>, <type>anyelement</type></entry>
+ <entry><type>anyelement</type></entry>
+ <entry>
+ Returns value of schema variable converted to expected type.
+ </entry>
+ </row>
+
+ <row>
+ <entry><literal><function>set_schema_variable(<parameter>variable</parameter>, <parameter>value</parameter>)</function></literal></entry>
+ <entry><type>regclass</type>, <type>anyelement</type></entry>
+ <entry><type>void</type></entry>
+ <entry>
+ Sets the value of schema variable to value, after converting the input to the correct type.
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ An example:
+ <para>
+
+<programlisting>
+CREATE TEMP VARIABLE foo AS numeric;
+SELECT set_schema_variable('foo', 345.445);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT get_schema_variable('foo', null::numeric);
+
+ get_schema_variable
+---------------------
+ 345.445
+(1 row)
+</programlisting>
+ </para>
+
+ </sect1>
+
<sect1 id="functions-info">
<title>System Information Functions</title>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 22e6893211..1d34f72bdd 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -99,6 +99,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createType SYSTEM "create_type.sgml">
<!ENTITY createUser SYSTEM "create_user.sgml">
<!ENTITY createUserMapping SYSTEM "create_user_mapping.sgml">
+<!ENTITY createVariable SYSTEM "create_variable.sgml">
<!ENTITY createView SYSTEM "create_view.sgml">
<!ENTITY deallocate SYSTEM "deallocate.sgml">
<!ENTITY declare SYSTEM "declare.sgml">
@@ -147,6 +148,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropType SYSTEM "drop_type.sgml">
<!ENTITY dropUser SYSTEM "drop_user.sgml">
<!ENTITY dropUserMapping SYSTEM "drop_user_mapping.sgml">
+<!ENTITY dropVariable SYSTEM "drop_variable.sgml">
<!ENTITY dropView SYSTEM "drop_view.sgml">
<!ENTITY end SYSTEM "end.sgml">
<!ENTITY execute SYSTEM "execute.sgml">
@@ -155,6 +157,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY grant SYSTEM "grant.sgml">
<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
<!ENTITY insert SYSTEM "insert.sgml">
+<!ENTITY let SYSTEM "let.sgml">
<!ENTITY listen SYSTEM "listen.sgml">
<!ENTITY load SYSTEM "load.sgml">
<!ENTITY lock SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
new file mode 100644
index 0000000000..c06b0e7517
--- /dev/null
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -0,0 +1,151 @@
+<!--
+doc/src/sgml/ref/create_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-createvariable">
+ <indexterm zone="sql-createvariable">
+ <primary>CREATE VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE VARIABLE</refname>
+ <refpurpose>define a new permissioned typed schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ]
+</synopsis>
+ </refsynopsisdiv>
+<!-- a multiple variable version of this might be useful...
+ name data_type [, name data_type] -->
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> creates a new schema variable.
+ These variables are scalar typed, non-transactional, and, like relations,
+ exist within a schema with access controlled via
+ <command>GRANT</command> and <command>REVOKE</command>.
+ </para>
+
+ <para>
+ The value of a schema variable is session-local. Retrieving
+ a variable's value will return NULL unless its value has been set
+ to something else in the current session.
+ </para>
+
+ <para>
+ Retrieval is done via the <function>get_schema_variable</function>dunxrion or the SQL
+ command <command>SELECT</command>. Setting of values is done via the
+ <function>set_schema_variable</function> function or the SQL command
+ <command>LET</command>.
+ Notably, while schema variables are in many ways a kind of table you cannot use
+ <command>UPDATE</command> on them.
+ </para>
+
+ <para>
+ For purposes of name uniqueness relation-like objects (e.g., tables, indexes)
+ within the same schema are considered. i.e., you cannot give a table and a
+ schema variable the same name. This is a consequence of them being treated
+ like relations for purposes of <command>SELECT</command>.
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF NOT EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the name already exists. A notice is issued in this case.
+ Note that type of the variable is not considered, nor could it be since the namespace
+ searched contains non-variable objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the data type of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Use <command>DROP VARIABLE</command> to remove a variable.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Create an integer variable <literal>var1</literal>:
+<programlisting>
+CREATE VARIABLE var1 AS integer;
+</programlisting>
+ </para>
+
+ <para>
+ Set this variable's value; then retrieve it converted to numeric.
+<programlisting>
+CREATE VARIABLE
+postgres=# select set_schema_variable('var1', 10);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+postgres=# select get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 10
+(1 row)
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> is a PostgreSQL feature.
+ <!-- The choice of wording here seems to be left to personal preference... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index 6b909b7232..395453bba0 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
+DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES}
</synopsis>
</refsynopsisdiv>
@@ -76,6 +76,16 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
</varlistentry>
<varlistentry>
+ <term><literal>VARIABLES</literal></term>
+ <listitem>
+ <para>
+ Sets the value of all schema variables to NULL.
+ <!-- What happens to temporary schema variables -->
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>ALL</literal></term>
<listitem>
<para>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
new file mode 100644
index 0000000000..06130fd510
--- /dev/null
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -0,0 +1,92 @@
+<!--
+doc/src/sgml/ref/drop_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropvariable">
+ <indexterm zone="sql-dropvariable">
+ <primary>DROP VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP VARIABLE</refname>
+ <refpurpose>remove a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP VARIABLE</command> removes a schema variable.
+ A variable can only be dropped by its owner or a superuser.
+ <!-- this would suggest that we need an alter variable owner to command -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the variable does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To remove the schema variable <literal>var1</literal>:
+
+<programlisting>
+DROP VARIABLE var1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ <!-- create variable is a "PostgreSQL feature",
+ this is a "proprietary PostgreSQL command" ... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index ff64c7a3ba..006364ebe5 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -79,6 +79,12 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE <replaceable>type_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+GRANT { { SELECT | UPDATE }
+ [, ...] | ALL [ PRIVILEGES ] }
+ ON { VARIABLE <replaceable class="parameter">variable_name</replaceable> [, ...]
+ | ALL VARIABLES IN SCHEMA <replaceable class="parameter">schema_name</replaceable> [, ...] }
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
[ GROUP ] <replaceable class="parameter">role_name</replaceable>
@@ -167,6 +173,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
foreign servers,
large objects,
schemas,
+ schema variables,
or tablespaces.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
@@ -204,6 +211,8 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
For sequences, this privilege also allows the use of the
<function>currval</function> function.
For large objects, this privilege allows the object to be read.
+ For schema variables, this privilege allows the <function>get_schema_variable</function>
+ to read the variable's value.
</para>
</listitem>
</varlistentry>
@@ -239,6 +248,9 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
<function>setval</function> functions.
For large objects, this privilege allows writing or truncating the
object.
+ For schema variables, this privilege allows <command>LET</command>
+ and <function>set_schema_variable</function> to modify the schema variable's
+ value.
</para>
</listitem>
</varlistentry>
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
new file mode 100644
index 0000000000..e8bf3f6dd4
--- /dev/null
+++ b/doc/src/sgml/ref/let.sgml
@@ -0,0 +1,90 @@
+<!--
+doc/src/sgml/ref/let.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-let">
+ <indexterm zone="sql-let">
+ <primary>LET</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>LET</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>LET</refname>
+ <refpurpose>change a schema variable's value</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable class="parameter">sql_expression</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ The <command>LET</command> command updates the specified schema variable' value.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>schema_variable</literal></term>
+ <listitem>
+ <para>
+ The name of schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sql expression</literal></term>
+ <listitem>
+ <para>
+ An SQL expression, the result is cast to the schema variable's type.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Example:
+<programlisting>
+CREATE VARIABLE myvar AS integer;
+LET myvar = 10;
+LET myvar = (SELECT sum(val) FROM tab);
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <!-- this feels like it needs to be more specific,
+ but I don't know enough to make it so -->
+ <literal>LET</literal> extends syntax defined in the SQL
+ standard. The standard knows <literal>SET</literal> command,
+ that is used for different purpouse in PostgreSQL.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 7018202f14..73778f01f9 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -108,6 +108,14 @@ REVOKE [ GRANT OPTION FOR ]
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_name</replaceable> [, ...]
[ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { SELECT | UPDATE }
+ [, ...] | ALL [ PRIVILEGES ] }
+ ON { VARIABLE <replaceable class="parameter">variable_name</replaceable> [, ...]
+ | ALL VARIABLES IN SCHEMA <replaceable>schema_name</replaceable> [, ...] }
+ FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index d27fb414f7..b3f9fff511 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -127,6 +127,7 @@
&createType;
&createUser;
&createUserMapping;
+ &createVariable;
&createView;
&deallocate;
&declare;
@@ -175,6 +176,7 @@
&dropType;
&dropUser;
&dropUserMapping;
+ &dropVariable;
&dropView;
&end;
&execute;
@@ -183,6 +185,7 @@
&grant;
&importForeignSchema;
&insert;
+ &let;
&listen;
&load;
&lock;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1156627b9e..268534ea87 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -284,6 +284,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -506,6 +509,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -576,6 +583,7 @@ ExecGrantStmt_oids(InternalGrant *istmt)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_VARIABLE:
ExecGrant_Relation(istmt);
break;
case OBJECT_DATABASE:
@@ -645,6 +653,7 @@ objectNamesToOids(ObjectType objtype, List *objnames)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_VARIABLE:
foreach(cell, objnames)
{
RangeVar *relvar = (RangeVar *) lfirst(cell);
@@ -1021,6 +1030,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1218,6 +1231,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1444,6 +1463,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -3459,6 +3481,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3569,6 +3594,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3683,6 +3711,7 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
pg_attribute_aclmask(table_oid, attnum, roleid, mask, how);
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_VARIABLE:
return pg_class_aclmask(table_oid, roleid, mask, how);
case OBJECT_DATABASE:
return pg_database_aclmask(table_oid, roleid, mask, how);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 0f34f5381a..558e641d56 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -291,6 +291,7 @@ heap_create(const char *relname,
switch (relkind)
{
case RELKIND_VIEW:
+ case RELKIND_VARIABLE:
case RELKIND_COMPOSITE_TYPE:
case RELKIND_FOREIGN_TABLE:
case RELKIND_PARTITIONED_TABLE:
@@ -1067,7 +1068,9 @@ heap_create_with_catalog(const char *relname,
if (existing_relid != InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_TABLE),
- errmsg("relation \"%s\" already exists", relname)));
+ errmsg("%s \"%s\" already exists",
+ relkind == RELKIND_VARIABLE ? "variable" : "relation",
+ relname)));
/*
* Since we are going to create a rowtype as well, also check for
@@ -1150,6 +1153,10 @@ heap_create_with_catalog(const char *relname,
relacl = get_user_default_acl(OBJECT_SEQUENCE, ownerid,
relnamespace);
break;
+ case RELKIND_VARIABLE:
+ relacl = get_user_default_acl(OBJECT_VARIABLE, ownerid,
+ relnamespace);
+ break;
default:
relacl = NULL;
break;
@@ -1181,7 +1188,8 @@ heap_create_with_catalog(const char *relname,
* Decide whether to create an array type over the relation's rowtype. We
* do not create any array types for system catalogs (ie, those made
* during initdb). We do not create them where the use of a relation as
- * such is an implementation detail: toast tables, sequences and indexes.
+ * such is an implementation detail: toast tables, sequences, indexes and
+ * variables.
*/
if (IsUnderPostmaster && (relkind == RELKIND_RELATION ||
relkind == RELKIND_VIEW ||
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 570e65affb..62479743c7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -521,6 +521,9 @@ static const struct object_type_map
"sequence", OBJECT_SEQUENCE
},
{
+ "variable", OBJECT_VARIABLE
+ },
+ {
"toast table", -1
}, /* unmapped */
{
@@ -824,6 +827,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
+ case OBJECT_VARIABLE:
address =
get_relation_by_qualified_name(objtype, castNode(List, object),
&relation, lockmode,
@@ -1260,6 +1264,14 @@ get_relation_by_qualified_name(ObjectType objtype, List *object,
errmsg("\"%s\" is not a foreign table",
RelationGetRelationName(relation))));
break;
+ case OBJECT_VARIABLE:
+ if (relation->rd_rel->relkind != RELKIND_VARIABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a schema variable",
+ RelationGetRelationName(relation))));
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
break;
@@ -1847,6 +1859,8 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2109,6 +2123,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_INDEX:
@@ -2233,6 +2248,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_INDEX:
case OBJECT_SEQUENCE:
case OBJECT_TABLE:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
@@ -3299,6 +3315,11 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new schemas belonging to role %s"),
GetUserNameFromId(defacl->defaclrole, false));
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfo(&buffer,
+ _("default privileges on new schema variables belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole, false));
+ break;
default:
/* shouldn't get here */
appendStringInfo(&buffer,
@@ -3502,6 +3523,10 @@ getRelationDescription(StringInfo buffer, Oid relid)
appendStringInfo(buffer, _("sequence %s"),
relname);
break;
+ case RELKIND_VARIABLE:
+ appendStringInfo(buffer, _("variable %s"),
+ relname);
+ break;
case RELKIND_TOASTVALUE:
appendStringInfo(buffer, _("toast table %s"),
relname);
@@ -4830,6 +4855,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on schema variables");
+ break;
}
if (objname)
@@ -5122,6 +5151,8 @@ get_relkind_objtype(char relkind)
return OBJECT_INDEX;
case RELKIND_SEQUENCE:
return OBJECT_SEQUENCE;
+ case RELKIND_VARIABLE:
+ return OBJECT_VARIABLE;
case RELKIND_VIEW:
return OBJECT_VIEW;
case RELKIND_MATVIEW:
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..5747272c9a 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,7 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
- schemacmds.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
+ schemacmds.o schemavar.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
tablecmds.o tablespace.o trigger.o tsearchcmds.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index b3933df9af..71e5aad852 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1484,6 +1484,9 @@ BeginCopy(ParseState *pstate,
Assert(query->utilityStmt == NULL);
+ /* Don't expect LET stmt here, is not possible to do write it */
+ Assert(query->commandType != CMD_LET);
+
/*
* Similarly the grammar doesn't enforce the presence of a RETURNING
* clause, but this is required here.
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 353ec990af..33db47e634 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -18,6 +18,7 @@
#include "commands/async.h"
#include "commands/discard.h"
#include "commands/prepare.h"
+#include "commands/schemavar.h"
#include "commands/sequence.h"
#include "utils/guc.h"
#include "utils/portal.h"
@@ -25,7 +26,7 @@
static void DiscardAll(bool isTopLevel);
/*
- * DISCARD { ALL | SEQUENCES | TEMP | PLANS }
+ * DISCARD { ALL | SEQUENCES | TEMP | PLANS | VARIABLES}
*/
void
DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
@@ -48,6 +49,10 @@ DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
ResetTempTableNamespace();
break;
+ case DISCARD_VARIABLES:
+ ResetSchemaVariablesCache();
+ break;
+
default:
elog(ERROR, "unrecognized DISCARD target: %d", stmt->target);
}
@@ -75,4 +80,5 @@ DiscardAll(bool isTopLevel)
ResetPlanCache();
ResetTempTableNamespace();
ResetSequenceCaches();
+ ResetSchemaVariablesCache();
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 549c7ea51d..c8e2b822e1 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -126,6 +126,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"TEXT SEARCH TEMPLATE", true},
{"TYPE", true},
{"USER MAPPING", true},
+ {"VARIABLE", true},
{"VIEW", true},
{NULL, false}
};
@@ -1124,6 +1125,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -2222,6 +2224,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2304,6 +2308,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 41cd47e8bc..11c8257fca 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -893,6 +893,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
case CMD_DELETE:
pname = operation = "Delete";
break;
+ case CMD_LET:
+ pname = operation = "Let";
+ break;
default:
pname = "???";
break;
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b1556a..a69471e926 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_LET:
/* OK */
break;
default:
diff --git a/src/backend/commands/schemavar.c b/src/backend/commands/schemavar.c
new file mode 100644
index 0000000000..cb803fab0c
--- /dev/null
+++ b/src/backend/commands/schemavar.c
@@ -0,0 +1,663 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavar.c
+ * PostgreSQL session variable support code.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/schemavar.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/objectaddress.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_type.h"
+#include "commands/tablecmds.h"
+#include "commands/schemavar.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_type.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/hsearch.h"
+#include "utils/inval.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/syscache.h"
+
+/*
+ * This schema variable cache mixes the cache and storages behave. That is not
+ * correct and it is problematic, when variable is removed. The own storage
+ * based on storage manager can be implemented, RelFileNode can be defined and
+ * mechanism based on PendingRelDelete struct can be used. This is a argument
+ * for implementation schema variables based on pg_class.
+ * Alternative solution can be detection of schema changes and recheck at and
+ * of transaction.
+ */
+typedef struct SchemaVarData
+{
+ Oid varid; /* pg_class OID of this sequence (hash key) */
+ Oid typid; /* OID of the data type */
+ int32 typmod;
+ int16 typlen;
+ bool typbyval;
+ bool isnull;
+ bool freeval;
+ Datum value;
+} SchemaVarData;
+
+typedef SchemaVarData *SchemaVar;
+
+static HTAB *schemavarhashtab = NULL; /* hash table for session variables */
+static MemoryContext SchemaVarMemoryContext = NULL;
+
+static Datum datumCast(Datum value,
+ Oid target_typid, int target_typmod,
+ Oid source_typid, int source_typmod);
+
+static bool first_time = true;
+static bool cache_is_valid = true;
+
+static void InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
+
+/* just mark cache to recheck */
+static void
+InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ /*
+ * because this cache holds values of schema variables, then
+ * the content cannot be removed in this momemt. We should to
+ * wait on transaction end.
+ */
+ cache_is_valid = false;
+}
+
+/*
+ * Wait on commit or rollback and clean values that miss entry in system
+ * catalog. It is temporary solution (although it is working). Storage manager
+ * based solution will be better, but it is not necessary for this PoC.
+ *
+ * removes uncommitted or dropped schema variables, so event can be ignored.
+ */
+static void
+recheck_schema_variables(XactEvent event, void *arg)
+{
+ HASH_SEQ_STATUS status;
+ SchemaVar var;
+
+ if (cache_is_valid || schemavarhashtab == NULL || !IsTransactionState())
+ return;
+
+ hash_seq_init(&status, schemavarhashtab);
+
+ while ((var = (SchemaVar) hash_seq_search(&status)) != NULL)
+ {
+ HeapTuple tp = InvalidOid;
+
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(var->varid));
+ if (!HeapTupleIsValid(tp))
+ {
+ elog(DEBUG1, "variable %d is removed from cache", var->varid);
+
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (hash_search(schemavarhashtab,
+ (void *) &var->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(ERROR, "hash table corrupted");
+ }
+ else
+ ReleaseSysCache(tp);
+ }
+ cache_is_valid = true;
+}
+
+/*
+ * DefineSessionVariable
+ * Creates a new variable related relation
+ */
+ObjectAddress
+DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *var)
+{
+ CreateStmt *stmt = makeNode(CreateStmt);
+ Oid typoid;
+ Oid varoid;
+ ObjectAddress address;
+
+ /*
+ * If if_not_exists was given and a relation with the same name already
+ * exists, bail out. (Note: we needn't check this when not if_not_exists,
+ * because DefineRelation will complain anyway.)
+ */
+ if (var->if_not_exists)
+ {
+ RangeVarGetAndCheckCreationNamespace(var->variable, NoLock, &varoid);
+ if (OidIsValid(varoid))
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_TABLE),
+ errmsg("variable \"%s\" already exists, skipping",
+ var->variable->relname)));
+ return InvalidObjectAddress;
+ }
+ }
+
+ typoid = LookupTypeNameOid(pstate, var->typeName, false);
+
+ /*
+ * Don't allow composite types and arrays. The left expression of
+ * LET statement is simple in this moment (don't allow record field
+ * or array field specification). Without this support we should
+ * not to support non scalars ever.
+ */
+ if (type_is_rowtype(typoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("Composite types are not allowed as variable type.")));
+
+ if (get_base_element_type(typoid) != InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("Schema variables cannot be a array.")));
+
+ if (get_typtype(typoid) == TYPTYPE_PSEUDO)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable cannot be %s",
+ format_type_be(varoid))));
+
+ stmt->tableElts = NIL;
+ stmt->relation = var->variable;
+ stmt->inhRelations = NIL;
+ stmt->constraints = NIL;
+ stmt->options = NIL;
+ stmt->oncommit = ONCOMMIT_NOOP;
+ stmt->tablespacename = NULL;
+ stmt->if_not_exists = var->if_not_exists;
+
+ /*
+ * Use reloftype attribute. This attribute should be composite type for
+ * tables, but there are no reason to apply this rule for variables. Can
+ * be changed later with composite type support. In this moment I don't
+ * play with it, because I would not allow queries like:
+ * SELECT schemavar FROM schemavar, because there is semantic colission
+ * with SELECT schemavar. Users expects composite value (one attribute)
+ * from first query, but scalar from second query. This schisma can be
+ * solved by disallowing SELECT . FROM schemavar for scalar variables.
+ *
+ * On second hand - without additional fields, just with reloftype is
+ * not possible to store typmod. So all variables can be typmod less.
+ * Is not possible to store default expressions. So final design should
+ * be based on aux composite types for scalar variables.
+ *
+ * Theoretically, there can be used a reltype and reloftype together.
+ * reloftype will be scalar, and reltype will be composite one field
+ * row type. When reloftype = reltype, then schema variable is based
+ * on composite type, else schema variable is of scalar type.
+ */
+ stmt->ofTypename = var->typeName;
+
+ address = DefineRelation(stmt, RELKIND_VARIABLE, InvalidOid, NULL, NULL);
+ Assert(address.objectId != InvalidOid);
+
+ return address;
+}
+
+/*
+ * Implementation of schemavar cache. It is question if it should be in this place, or
+ * it should be storage related or cache related place? But for this moment (PoC) it
+ * can be here. Cache is implemented as hash table with own memory context.
+ */
+
+/*
+ * Create the hash table for storing schema variables
+ */
+static void
+create_schemavar_hashtable(void)
+{
+ HASHCTL ctl;
+
+ /* set callbacks */
+ if (first_time)
+ {
+
+ CacheRegisterSyscacheCallback(RELOID,
+ InvalidateSchemaVarCacheCallback,
+ (Datum) 0);
+ RegisterXactCallback(recheck_schema_variables, NULL);
+
+ first_time = false;
+ }
+
+ /* needs own long life memory context */
+ if (SchemaVarMemoryContext == NULL)
+ {
+ SchemaVarMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "schema variables",
+ ALLOCSET_START_SMALL_SIZES);
+ }
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(SchemaVarData);
+ ctl.hcxt = SchemaVarMemoryContext;
+
+ schemavarhashtab = hash_create("Schema variables", 64, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+ cache_is_valid = true;
+}
+
+/*
+ * Fast drop complete content of schema variables
+ */
+void
+ResetSchemaVariablesCache(void)
+{
+ if (schemavarhashtab)
+ {
+ hash_destroy(schemavarhashtab);
+ schemavarhashtab = NULL;
+ }
+
+ if (SchemaVarMemoryContext != NULL)
+ {
+ MemoryContextReset(SchemaVarMemoryContext);
+ }
+}
+
+/*
+ * Copy datum value to schema variables cache place
+ */
+static void
+SetValue(SchemaVar var,
+ Datum value, bool isNull,
+ Oid typid, int32 typmod)
+{
+ /* release previously stored value */
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (!isNull)
+ {
+ MemoryContext oldcxt;
+
+ /*
+ * cast the value if conversion is necessary.
+ * Expecting: current context is short context.
+ *
+ * QUESTION: how much should be this cast tolerant/strict?
+ */
+ if (var->typid != typid || var->typmod != typmod)
+ {
+ value = datumCast(value,
+ var->typid, var->typmod,
+ typid, typmod);
+ }
+
+ var->isnull = false;
+
+ oldcxt = MemoryContextSwitchTo(SchemaVarMemoryContext);
+
+ var->value = datumCopy(value, var->typbyval, var->typlen);
+ if (var->value != value)
+ var->freeval = true;
+
+ MemoryContextSwitchTo(oldcxt);
+ }
+ else
+ {
+ var->value = (Datum) 0;
+ var->isnull = true;
+ }
+}
+
+/*
+ * Access functions to schema variables.
+ */
+void
+SetSchemaVariable(Oid varid, Datum value, bool isNull,
+ Oid typid, int32 typmod,
+ int16 typlen, bool typbyval)
+{
+ SchemaVar var;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ {
+ /* don't init hashtable for NULL values */
+ if (isNull)
+ return;
+
+ create_schemavar_hashtable();
+ }
+
+ var = (SchemaVar) hash_search(schemavarhashtab, &varid, HASH_ENTER, &found);
+ if (!found)
+ {
+ HeapTuple tp;
+ Form_pg_class vartup;
+
+ var->value = (Datum) 0;
+ var->isnull = true;
+ var->freeval = false;
+
+ /* now, type info for schema variable is collected */
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup faild for variable %u", varid);
+
+ vartup = (Form_pg_class) GETSTRUCT(tp);
+ var->typid = vartup->reloftype;
+
+ /* typmod is not saved */
+ var->typmod = -1;
+
+ ReleaseSysCache(tp);
+
+ get_typlenbyval(var->typid, &var->typlen, &var->typbyval);
+ }
+
+ SetValue(var, value, isNull, typid, typmod);
+}
+
+/*
+ * Returns variable name
+ */
+char *
+get_schemavar_name(Oid varid)
+{
+ HeapTuple relTup;
+ Form_pg_class relForm;
+ char *nspname;
+ char *relname;
+
+ relTup = SearchSysCache1(RELOID,
+ ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(relTup))
+ elog(ERROR, "cache lookup failed for schema variable %u", varid);
+ relForm = (Form_pg_class) GETSTRUCT(relTup);
+
+ /* Qualify the name if not visible in search path */
+ if (RelationIsVisible(varid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(relForm->relnamespace);
+
+ relname = quote_qualified_identifier(nspname, NameStr(relForm->relname));
+
+ ReleaseSysCache(relTup);
+
+ return relname;
+}
+
+/*
+ * Securized versions SetSchemaVariable
+ */
+void
+SetSchemaVariableSecure(Oid varid, Datum value, bool isNull,
+ Oid typid, int32 typmod,
+ int16 typlen, bool typbyval)
+{
+ AclResult aclresult;
+
+ /* Check permissions */
+ aclresult = pg_class_aclcheck(varid, GetUserId(), ACL_UPDATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, get_schemavar_name(varid));
+
+ SetSchemaVariable(varid, value, isNull, typid, typmod, typlen, typbyval);
+}
+
+/*
+ * Cast datum
+ */
+static Datum
+datumCast(Datum value,
+ Oid target_typid, int target_typmod,
+ Oid source_typid, int source_typmod)
+{
+ CoercionPathType cpathtype;
+ Oid cfuncid;
+ Datum result = (Datum) 0;
+ bool is_binary_cast = false;
+
+ if (target_typid != source_typid)
+ {
+ cpathtype = find_coercion_pathway(target_typid, source_typid,
+ COERCION_EXPLICIT,
+ &cfuncid);
+
+ if (cpathtype == COERCION_PATH_NONE)
+ elog(ERROR, "could not find cast from %s to %s",
+ format_type_be(source_typid),
+ format_type_be(target_typid));
+
+ if (cpathtype == COERCION_PATH_RELABELTYPE)
+ {
+ result = value;
+ is_binary_cast = true;
+ }
+ else if (cpathtype == COERCION_PATH_COERCEVIAIO)
+ {
+ Oid outfunc;
+ Oid infunc;
+ Oid ioparam;
+ bool isVarlena;
+ char *str;
+
+ getTypeOutputInfo(source_typid, &outfunc, &isVarlena);
+ str = OidOutputFunctionCall(outfunc, value);
+
+ getTypeInputInfo(target_typid, &infunc, &ioparam);
+ result = OidInputFunctionCall(infunc, str, ioparam, -1);
+ }
+ else if (cpathtype == COERCION_PATH_FUNC)
+ {
+ result = OidFunctionCall3(cfuncid,
+ value,
+ Int32GetDatum(target_typmod),
+ BoolGetDatum(false));
+ }
+ }
+ else
+ {
+ result = value;
+ is_binary_cast = true;
+ }
+
+ if (target_typmod < 1 || (target_typmod == source_typmod && is_binary_cast))
+ return result;
+
+ cpathtype = find_typmod_coercion_function(target_typid, &cfuncid);
+ if (cpathtype == COERCION_PATH_FUNC)
+ {
+ result = OidFunctionCall3(cfuncid,
+ result,
+ Int32GetDatum(target_typmod),
+ BoolGetDatum(false));
+ }
+
+ return result;
+}
+
+Datum
+GetSchemaVariable(Oid varid, bool *isNull,
+ Oid typid, int32 typmod,
+ int16 typlen, bool typbyval)
+{
+ Assert(varid != InvalidOid);
+
+ if (schemavarhashtab != NULL)
+ {
+ SchemaVar var;
+ bool found;
+
+ var = (SchemaVar) hash_search(schemavarhashtab,
+ &varid, HASH_FIND, &found);
+
+ if (found && !var->isnull)
+ {
+ Datum result;
+
+ result = datumCast(var->value, typid, typmod,
+ var->typid, var->typmod);
+ *isNull = false;
+
+ if (result != var->value)
+ return result;
+ else
+ return datumCopy(result, typbyval, typlen);
+ }
+ }
+
+ /*
+ * This implementation is simple, because default expressions
+ * are not supported. With support of default expression, there
+ * should be insert schema variable into cache. Not supported yet,
+ * so do just simply work.
+ */
+ *isNull = true;
+ return (Datum) 0;
+}
+
+/*
+ * Securized version of GetSchemaVariable
+ */
+Datum
+GetSchemaVariableSecure(Oid varid, bool *isNull,
+ Oid typid, int32 typmod,
+ int16 typlen, bool typbyval)
+{
+ AclResult aclresult;
+
+ /* Check permissions */
+ aclresult = pg_class_aclcheck(varid, GetUserId(), ACL_SELECT);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, get_schemavar_name(varid));
+
+ return GetSchemaVariable(varid, isNull, typid, typmod, typlen, typbyval);
+}
+
+/*
+ * V1 function API
+ *
+ * void set_schema_variable(var regclass, value anyelement);
+ * anyelement get_schema_variable(var regclass, expected_type anyelement)
+ *
+ */
+Datum
+set_schema_variable(PG_FUNCTION_ARGS)
+{
+ Oid varid;
+ Datum value;
+ bool isNull;
+ Oid typid;
+ int16 typlen;
+ bool typbyval;
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("null value not allowed for variable identity")));
+
+ varid = PG_GETARG_OID(0);
+
+ if (!PG_ARGISNULL(1))
+ {
+ value = PG_GETARG_DATUM(1);
+ isNull = false;
+ }
+ else
+ {
+ value = (Datum) 0;
+ isNull = true;
+ }
+
+ typid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (typid == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+
+ get_typlenbyval(typid, &typlen, &typbyval);
+ SetSchemaVariableSecure(varid, value, isNull, typid, -1, typlen, typbyval);
+
+ PG_RETURN_VOID();
+}
+
+Datum
+get_schema_variable(PG_FUNCTION_ARGS)
+{
+ Oid varid;
+ Oid typid;
+ int16 typlen;
+ bool typbyval;
+ bool isNull;
+ Datum result;
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("null value not allowed for variable identity")));
+
+ varid = PG_GETARG_OID(0);
+
+ typid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (typid == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+
+ get_typlenbyval(typid, &typlen, &typbyval);
+ result = GetSchemaVariableSecure(varid, &isNull, typid, -1, typlen, typbyval);
+
+ if (isNull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/*
+ * Results:
+ *=
+
+1. The schema variables are similar to temporary tables - but the data are not saved
+ in 8KB blocks, so new storage for Pg storage manager should be created.
+
+2. We should to work with typmod, so pg_attribute entry should be created anytime.
+
+3. A risk of collisions of variable and table name will be reduced, when variables
+ and tables cannot to have same name.
+
+4. If schema variables are pg_class based, then some current syntax has sense
+
+ INSERT INTO schema.variable SELECT xxx
+ maybe (but it is not consistent with PostgreSQL SQL, but consistent with PLpgSQL):
+ SELECT * INTO schema.variable FROM xxx
+
+5. LET cmd can be implemented as CMD (like INSERT, UPDATE, DELETE) or Utility (like
+ CreateTableAsSelect). Prefer first option, because there can be prepared, can be
+ used together with EXPLAIN, etc.
+
+ Expected form:
+ LET foo = (SELECT id FROM boo WHERE some = 'hello');
+
+ so possibility to run EXPLAIN LET .. has enough benefit
+*/
\ No newline at end of file
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index c6eb3ebacf..53ea890517 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -33,6 +33,7 @@
#include "access/nbtree.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
+#include "commands/schemavar.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -723,6 +724,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
Param *param = (Param *) node;
ParamListInfo params;
+ AclResult aclresult;
switch (param->paramkind)
{
@@ -732,6 +734,23 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
break;
+ case PARAM_SCHEMA_VARIABLE:
+ /* Check permission to read schema variable */
+ aclresult = pg_class_aclcheck(param->paramid, GetUserId(), ACL_SELECT);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, get_schemavar_name(param->paramid));
+
+ scratch.opcode = EEOP_PARAM_SCHEMA_VARIABLE;
+ scratch.d.param.paramid = param->paramid;
+ scratch.d.param.paramtype = param->paramtype;
+ scratch.d.param.paramtypmod = param->paramtypmod;
+
+ get_typlenbyval(param->paramtype,
+ &scratch.d.param.paramtyplen,
+ &scratch.d.param.paramtypbyval);
+
+ ExprEvalPushStep(state, &scratch);
+ break;
case PARAM_EXTERN:
/*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index f646fd9c51..7a3b283039 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/schemavar.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -350,6 +351,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_SCHEMA_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1031,6 +1033,23 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_SCHEMA_VARIABLE)
+ {
+ Datum d;
+ bool isnull;
+
+ d = GetSchemaVariable(op->d.param.paramid, &isnull,
+ op->d.param.paramtype,
+ -1,
+ op->d.param.paramtyplen,
+ op->d.param.paramtypbyval);
+
+ *op->resvalue = d;
+ *op->resnull = isnull;
+
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_PARAM_CALLBACK)
{
/* allow an extension module to supply a PARAM_EXTERN value */
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 410921cc40..a1ae732ae5 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -210,6 +210,7 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
switch (queryDesc->operation)
{
case CMD_SELECT:
+ case CMD_LET:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
@@ -1119,6 +1120,36 @@ CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
errmsg("cannot change TOAST relation \"%s\"",
RelationGetRelationName(resultRel))));
break;
+ case RELKIND_VARIABLE:
+
+ /* Only LET statement is allowed */
+ if (operation != CMD_LET)
+ {
+ switch (operation)
+ {
+ case CMD_INSERT:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot insert into variable \"%s\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ case CMD_UPDATE:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot update variable \"%s\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ case CMD_DELETE:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot delete from variable \"%s\"",
+ RelationGetRelationName(resultRel))));
+ default:
+ elog(ERROR, "unrecognized CmdType: %d", (int) operation);
+ break;
+ }
+ }
+ break;
case RELKIND_VIEW:
/*
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 2a8ecbd830..f8e478aa42 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -39,6 +39,7 @@
#include "access/htup_details.h"
#include "access/xact.h"
+#include "commands/schemavar.h"
#include "commands/trigger.h"
#include "executor/execPartition.h"
#include "executor/executor.h"
@@ -68,6 +69,7 @@ static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate);
static TupleConversionMap *tupconv_map_for_subplan(ModifyTableState *node,
int whichplan);
+
/*
* Verify that the tuples to be produced by INSERT or UPDATE match the
* target relation's rowtype
@@ -1568,6 +1570,81 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
}
+
+
+
+/* ----------------------------------------------------------------
+ * ExecLet
+ *
+ * For LET, we have to update target variable,
+ * Returns NULL, there are not RETURNING clause.
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+ExecLet(ModifyTableState *mtstate,
+ TupleTableSlot *slot,
+ EState *estate,
+ bool canSetTag)
+{
+ HeapTuple tuple;
+ ResultRelInfo *resultRelInfo;
+ Relation resultRelationDesc;
+ TupleDesc tupdesc;
+ bool isnull = true;
+ Datum value;
+ Form_pg_attribute attr = NULL;
+ Oid varid;
+
+ if (slot != NULL && !slot->tts_isempty)
+ {
+ tuple = slot->tts_tuple;
+ tupdesc = slot->tts_tupleDescriptor;
+
+ Assert(tupdesc != NULL);
+
+ /* should be checked before */
+ if (tupdesc->natts != 1)
+ elog(ERROR, "unexpected number of attributes");
+
+ attr = TupleDescAttr(tupdesc, 0);
+
+ if (!slot->tts_isnull[0])
+ {
+ isnull = false;
+ value = slot->tts_values[0];
+ }
+ }
+
+ /*
+ * Now, es_result_relation_info is empty, but can be initialized
+ * to structure of used schema variable.
+ */
+ resultRelInfo = estate->es_result_relation_info;
+ resultRelationDesc = resultRelInfo->ri_RelationDesc;
+ varid = resultRelationDesc->rd_id;
+
+ if (!isnull)
+ {
+ /* expecting so variable and expression are equal */
+ SetSchemaVariable(varid, value, isnull,
+ attr->atttypid, -1,
+ attr->attlen, attr->attbyval);
+ }
+ else
+ {
+ SetSchemaVariable(varid, (Datum) 0, true,
+ InvalidOid, -1, -1, false);
+ }
+
+ if (canSetTag)
+ {
+ Assert(estate->es_processed == 0);
+ (estate->es_processed)++;
+ }
+
+ return NULL;
+}
+
/*
* Process BEFORE EACH STATEMENT triggers
*/
@@ -1598,6 +1675,9 @@ fireBSTriggers(ModifyTableState *node)
case CMD_DELETE:
ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
break;
+ case CMD_LET:
+ /* there are no trigger */
+ break;
default:
elog(ERROR, "unknown operation");
break;
@@ -1652,6 +1732,9 @@ fireASTriggers(ModifyTableState *node)
ExecASDeleteTriggers(node->ps.state, resultRelInfo,
node->mt_transition_capture);
break;
+ case CMD_LET:
+ /* variables has not triggers */
+ break;
default:
elog(ERROR, "unknown operation");
break;
@@ -2056,6 +2139,9 @@ ExecModifyTable(PlanState *pstate)
&node->mt_epqstate, estate,
NULL, true, node->canSetTag);
break;
+ case CMD_LET:
+ slot = ExecLet(node, slot, estate, node->canSetTag);
+ break;
default:
elog(ERROR, "unknown operation");
break;
@@ -2562,6 +2648,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
break;
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_LET:
junk_filter_needed = true;
break;
default:
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 9fc4431b80..310bc3f2c7 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -2404,6 +2404,9 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
else
res = SPI_OK_UPDATE;
break;
+ case CMD_LET:
+ res = SPI_OK_UTILITY;
+ break;
default:
return SPI_ERROR_OPUNKNOWN;
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index bafe0d1071..bf250aafbb 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3056,6 +3056,17 @@ _copySelectStmt(const SelectStmt *from)
return newnode;
}
+static LetStmt *
+_copyLetStmt(const LetStmt *from)
+{
+ LetStmt *newnode = makeNode(LetStmt);
+
+ COPY_NODE_FIELD(variable);
+ COPY_NODE_FIELD(selectStmt);
+
+ return newnode;
+}
+
static SetOperationStmt *
_copySetOperationStmt(const SetOperationStmt *from)
{
@@ -5091,6 +5102,9 @@ copyObjectImpl(const void *from)
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
+ case T_LetStmt:
+ retval = _copyLetStmt(from);
+ break;
case T_SetOperationStmt:
retval = _copySetOperationStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 02ca7d588c..709a686134 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1068,6 +1068,15 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
}
static bool
+_equalLetStmt(const LetStmt *a, const LetStmt *b)
+{
+ COMPARE_NODE_FIELD(variable);
+ COMPARE_NODE_FIELD(selectStmt);
+
+ return true;
+}
+
+static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
COMPARE_SCALAR_FIELD(op);
@@ -3228,6 +3237,9 @@ equal(const void *a, const void *b)
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
break;
+ case T_LetStmt:
+ retval = _equalLetStmt(a, b);
+ break;
case T_SetOperationStmt:
retval = _equalSetOperationStmt(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 6c76c41ebe..8d24818c9f 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3444,6 +3444,16 @@ raw_expression_tree_walker(Node *node,
return true;
}
break;
+ case T_LetStmt:
+ {
+ LetStmt *stmt = (LetStmt *) node;
+
+ if (walker(stmt->variable, context))
+ return true;
+ if (walker(stmt->selectStmt, context))
+ return true;
+ }
+ break;
case T_A_Expr:
{
A_Expr *expr = (A_Expr *) node;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 89f27ce0eb..f4d8756487 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1251,12 +1251,15 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
* PARAM_EXEC Params listed in safe_param_ids, meaning they could be
* either generated within the worker or can be computed in master and
* then their value can be passed to the worker.
+ * PARAM_SCHEMA_VARIABLE params are newer changed by workers, so they can be
+ * safe.
*/
else if (IsA(node, Param))
{
Param *param = (Param *) node;
- if (param->paramkind == PARAM_EXTERN)
+ if (param->paramkind == PARAM_EXTERN ||
+ param->paramkind == PARAM_SCHEMA_VARIABLE)
return false;
if (param->paramkind != PARAM_EXEC ||
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index e7b2bc7e73..f22eab422e 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -42,6 +42,7 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
#include "utils/rel.h"
@@ -54,6 +55,7 @@ static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
static List *transformInsertRow(ParseState *pstate, List *exprlist,
List *stmtcols, List *icolumns, List *attrnos,
bool strip_indirection);
+static Query *transformLetStmt(ParseState *pstate, LetStmt *stmt);
static OnConflictExpr *transformOnConflictClause(ParseState *pstate,
OnConflictClause *onConflictClause);
static int count_rowexpr_columns(ParseState *pstate, Node *expr);
@@ -263,6 +265,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
+ case T_LetStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
@@ -300,6 +303,10 @@ transformStmt(ParseState *pstate, Node *parseTree)
}
break;
+ case T_LetStmt:
+ result = transformLetStmt(pstate, (LetStmt *) parseTree);
+ break;
+
/*
* Special cases
*/
@@ -358,6 +365,7 @@ analyze_requires_snapshot(RawStmt *parseTree)
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
+ case T_LetStmt:
result = true;
break;
@@ -1533,6 +1541,207 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
}
/*
+ * transformLetStmt -
+ * transform an Let Statement
+ */
+static Query *
+transformLetStmt(ParseState *pstate, LetStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ List *exprList = NIL;
+ List *exprListCoer = NIL;
+ List *sub_rtable;
+ List *sub_namespace;
+ RangeTblEntry *rte;
+ RangeTblRef *rtr;
+ ListCell *lc;
+ AclMode targetPerms;
+ ParseState *sub_pstate;
+ Query *selectQuery;
+ int i = 0;
+
+ Relation rd;
+ Oid vartypid = InvalidOid;
+
+ /* There can't be any outer WITH to worry about */
+ Assert(pstate->p_ctenamespace == NIL);
+
+ qry->commandType = CMD_LET;
+ pstate->p_is_let = true;
+
+ /*
+ * If a non-nil rangetable/namespace was passed in, and we are doing
+ * INSERT/SELECT, arrange to pass the rangetable/namespace down to the
+ * SELECT. This can only happen if we are inside a CREATE RULE, and in
+ * that case we want the rule's OLD and NEW rtable entries to appear as
+ * part of the SELECT's rtable, not as outer references for it. (Kluge!)
+ * The SELECT's joinlist is not affected however. We must do this before
+ * adding the target table to the INSERT's rtable.
+ */
+ sub_rtable = pstate->p_rtable;
+ pstate->p_rtable = NIL;
+ sub_namespace = pstate->p_namespace;
+ pstate->p_namespace = NIL;
+
+ targetPerms = ACL_UPDATE;
+ qry->resultRelation = setTargetTable(pstate, stmt->variable,
+ false, false, targetPerms);
+
+ rd = pstate->p_target_relation;
+ vartypid = rd->rd_rel->reloftype;
+
+ /*
+ * We make the sub-pstate a child of the outer pstate so that it can
+ * see any Param definitions supplied from above. Since the outer
+ * pstate's rtable and namespace are presently empty, there are no
+ * side-effects of exposing names the sub-SELECT shouldn't be able to
+ * see.
+ */
+ sub_pstate = make_parsestate(pstate);
+
+ /*
+ * Process the source SELECT.
+ *
+ * It is important that this be handled just like a standalone SELECT;
+ * otherwise the behavior of SELECT within INSERT might be different
+ * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
+ * bugs of just that nature...)
+ *
+ * The sole exception is that we prevent resolving unknown-type
+ * outputs as TEXT. This does not change the semantics since if the
+ * column type matters semantically, it would have been resolved to
+ * something else anyway. Doing this lets us resolve such outputs as
+ * the target column's type, which we handle below.
+ */
+ sub_pstate->p_rtable = sub_rtable;
+ sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */
+ sub_pstate->p_namespace = sub_namespace;
+ sub_pstate->p_resolve_unknowns = false;
+
+ selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
+
+ free_parsestate(sub_pstate);
+
+ /* The grammar should have produced a SELECT */
+ if (!IsA(selectQuery, Query) ||
+ selectQuery->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in LET ... SELECT");
+
+ /*
+ * Make the source be a subquery in the LET's rangetable, and add
+ * it to the LET's joinlist.
+ */
+ rte = addRangeTableEntryForSubquery(pstate,
+ selectQuery,
+ makeAlias("*SELECT*", NIL),
+ false,
+ false);
+ rtr = makeNode(RangeTblRef);
+ /* assume new rte is at end */
+ rtr->rtindex = list_length(pstate->p_rtable);
+ Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
+ pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
+
+ /*----------
+ * Generate an expression list for the LET that selects all the
+ * non-resjunk columns from the subquery. (LET's tlist must be
+ * separate from the subquery's tlist because we may add datatype
+ * coercions, etc.)
+ *----------
+ */
+ exprList = NIL;
+ foreach(lc, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+ Expr *expr;
+
+ if (tle->resjunk)
+ continue;
+ if (tle->expr &&
+ (IsA(tle->expr, Const) ||IsA(tle->expr, Param)) &&
+ exprType((Node *) tle->expr) == UNKNOWNOID)
+ expr = tle->expr;
+ else
+ {
+ Var *var = makeVarFromTargetEntry(rtr->rtindex, tle);
+
+ var->location = exprLocation((Node *) tle->expr);
+ expr = (Expr *) var;
+ }
+ exprList = lappend(exprList, expr);
+ }
+
+ /*
+ * Because supports only scalar variables, we can only simple
+ * transformations and checks here.
+ */
+ if (list_length(exprList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expression is not scalar value"),
+ parser_errposition(pstate,
+ exprLocation((Node *) exprList))));
+
+ exprListCoer = NIL;
+ foreach(lc, exprList)
+ {
+ Node *orig_expr = (Node*) lfirst(lc);
+ Oid exprtypid = exprType((Node *) orig_expr);
+ Expr *expr;
+
+ expr = (Expr *)
+ coerce_to_target_type(pstate,
+ orig_expr, exprtypid,
+ vartypid, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+
+ if (expr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable \"%s\" is of type %s"
+ " but expression is of type %s",
+ RelationGetRelationName(rd),
+ format_type_be(vartypid),
+ format_type_be(exprtypid)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *) orig_expr))));
+
+ exprListCoer = lappend(exprListCoer, expr);
+ }
+
+ /*
+ * Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
+ */
+ rte = pstate->p_target_rangetblentry;
+ qry->targetList = NIL;
+ foreach(lc, exprList)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ tle = makeTargetEntry(expr,
+ i + 1,
+ FigureColname((Node *)expr),
+ false);
+ qry->targetList = lappend(qry->targetList, tle);
+ }
+
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+ qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+
+ assign_query_collations(pstate, qry);
+
+ return qry;
+}
+
+/*
* transformSetOperationStmt -
* transforms a set-operations tree
*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5329432f25..d2a264d1e1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -257,8 +257,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSchemaVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -268,7 +268,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DropTransformStmt
DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
- ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
+ LetStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -646,7 +646,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+ LEADING LEAKPROOF LEAST LEFT LET LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -682,8 +682,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -873,6 +873,7 @@ stmt :
| CreatePLangStmt
| CreateSchemaStmt
| CreateSeqStmt
+ | CreateSchemaVarStmt
| CreateStmt
| CreateSubscriptionStmt
| CreateStatsStmt
@@ -914,6 +915,7 @@ stmt :
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
+ | LetStmt
| LockStmt
| NotifyStmt
| PrepareStmt
@@ -1374,6 +1376,7 @@ schema_stmt:
CreateStmt
| IndexStmt
| CreateSeqStmt
+ | CreateSchemaVarStmt
| CreateTrigStmt
| GrantStmt
| ViewStmt
@@ -1802,7 +1805,12 @@ DiscardStmt:
n->target = DISCARD_SEQUENCES;
$$ = (Node *) n;
}
-
+ | DISCARD VARIABLES
+ {
+ DiscardStmt *n = makeNode(DiscardStmt);
+ n->target = DISCARD_VARIABLES;
+ $$ = (Node *) n;
+ }
;
@@ -4269,6 +4277,34 @@ NumericOnly_list: NumericOnly { $$ = list_make1($1); }
/*****************************************************************************
*
+ * QUERY :
+ * CREATE VARIABLE seqname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSchemaVarStmt:
+ CREATE OptTemp VARIABLE qualified_name opt_as Typename
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $4->relpersistence = $2;
+ n->variable = $4;
+ n->typeName = $6;
+ n->if_not_exists = false;
+ $$ = (Node *)n;
+ }
+ | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $7->relpersistence = $2;
+ n->variable = $7;
+ n->typeName = $9;
+ n->if_not_exists = true;
+ $$ = (Node *)n;
+ }
+ ;
+
+/*****************************************************************************
+ *
* QUERIES :
* CREATE [OR REPLACE] [TRUSTED] [PROCEDURAL] LANGUAGE ...
* DROP [PROCEDURAL] LANGUAGE ...
@@ -6315,6 +6351,7 @@ drop_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name_list */
@@ -6584,6 +6621,7 @@ comment_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -6722,6 +6760,7 @@ security_label_type_any_name:
| TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -7047,6 +7086,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| FOREIGN DATA_P WRAPPER name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7159,6 +7206,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
| ALL FUNCTIONS IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7341,6 +7396,7 @@ defacl_privilege_target:
| FUNCTIONS { $$ = OBJECT_FUNCTION; }
| ROUTINES { $$ = OBJECT_FUNCTION; }
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
;
@@ -10645,7 +10701,8 @@ ExplainableStmt:
| CreateAsStmt
| CreateMatViewStmt
| RefreshMatViewStmt
- | ExecuteStmt /* by default all are $$=$1 */
+ | ExecuteStmt
+ | LetStmt /* by default all are $$=$1 */
;
explain_option_list:
@@ -10702,7 +10759,8 @@ PreparableStmt:
SelectStmt
| InsertStmt
| UpdateStmt
- | DeleteStmt /* by default all are $$=$1 */
+ | DeleteStmt
+ | LetStmt /* by default all are $$=$1 */
;
/*****************************************************************************
@@ -11104,6 +11162,30 @@ opt_hold: /* EMPTY */ { $$ = 0; }
/*****************************************************************************
*
* QUERY:
+ * LET STATEMENTS
+ *
+ *****************************************************************************/
+LetStmt: LET qualified_name '=' a_expr
+ {
+ LetStmt *n = makeNode(LetStmt);
+ SelectStmt *select = makeNode(SelectStmt);
+ ResTarget *res = makeNode(ResTarget);
+
+ res->name = NULL;
+ res->indirection = NIL;
+ res->val = (Node *) $4;
+ res->location = @4;
+ select->targetList = list_make1(res);
+ n->variable = $2;
+ n->selectStmt = (Node *) select;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
* SELECT STATEMENTS
*
*****************************************************************************/
@@ -15056,6 +15138,7 @@ unreserved_keyword:
| LARGE_P
| LAST_P
| LEAKPROOF
+ | LET
| LEVEL
| LISTEN
| LOAD
@@ -15202,6 +15285,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index b2f5e46e3b..cbf757d059 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -509,6 +509,7 @@ static Node *
transformColumnRef(ParseState *pstate, ColumnRef *cref)
{
Node *node = NULL;
+ Node *variable = NULL;
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
@@ -750,6 +751,70 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
}
/*
+ * Try to identify column ref as variable. Possible variants are
+ *
+ * A .. variable name
+ * A.B .. qualified variable name
+ */
+ switch (list_length(cref->fields))
+ {
+ case 1:
+ {
+ Node *field1 = (Node *) linitial(cref->fields);
+
+ if (IsA(field1, String))
+ {
+ char *varname = strVal(field1);
+
+ /* Try to identify as an unqualified column */
+ variable = toSchemaVariable(pstate,
+ NULL, varname,
+ cref->location);
+ }
+ break;
+ }
+ case 2:
+ {
+ Node *field1 = (Node *) linitial(cref->fields);
+ Node *field2 = (Node *) lsecond(cref->fields);
+
+ if (IsA(field1, String) && IsA(field2, String))
+ {
+ char *nspname = strVal(field1);
+ char *varname = strVal(field2);
+
+ /* Try to identify as an unqualified column */
+ variable = toSchemaVariable(pstate,
+ nspname, varname,
+ cref->location);
+ }
+ break;
+ }
+ default:
+
+ /*
+ * There can be another variants, more when composite variables
+ * will be supported. Currently only scalars are supported, so
+ * there are not necessary to solve other questions.
+ *
+ * do nothing
+ */
+ break;
+ }
+
+ if (variable != NULL)
+ {
+ if (node != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+
+ node = variable;
+ }
+
+ /*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
* case that it has a conflicting interpretation of the ColumnRef. (If it
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 2625da5327..f7d9a0c939 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1150,6 +1150,7 @@ parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode)
setup_parser_errposition_callback(&pcbstate, pstate, relation->location);
rel = heap_openrv_extended(relation, lockmode, true);
+
if (rel == NULL)
{
if (relation->schemaname)
@@ -1180,6 +1181,24 @@ parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode)
relation->relname)));
}
}
+
+ /*
+ * RELKIND_VARIABLE can be used only in LET command.
+ * Probably this check can be done elsewhere, but here I
+ * have a used relation and parse state together first time.
+ */
+ if (rel->rd_rel->relkind == RELKIND_VARIABLE && !pstate->p_is_let)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an schema variable",
+ RelationGetRelationName(rel))));
+
+ if (pstate->p_is_let && rel->rd_rel->relkind != RELKIND_VARIABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an schema variable",
+ RelationGetRelationName(rel))));
+
cancel_parser_errposition_callback(&pcbstate);
return rel;
}
@@ -3360,3 +3379,42 @@ isQueryUsingTempRelation_walker(Node *node, void *context)
isQueryUsingTempRelation_walker,
context);
}
+
+/*
+ * Try to replace ColumnRef by Param related to variable
+ */
+Node *
+toSchemaVariable(ParseState *pstate, char *nspname, char *varname, int location)
+{
+ Oid varid;
+ Param *param = NULL;
+
+ varid = RangeVarGetRelid(makeRangeVar(nspname, varname, -1), NoLock, true);
+ if (OidIsValid(varid))
+ {
+ HeapTuple tp;
+ Form_pg_class vartup;
+
+ /* now, type info for schema variable is collected */
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(varid));
+ if (HeapTupleIsValid(tp))
+ {
+ vartup = (Form_pg_class) GETSTRUCT(tp);
+
+ if (vartup->relkind == RELKIND_VARIABLE)
+ {
+ param = makeNode(Param);
+ param->paramkind = PARAM_SCHEMA_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = vartup->reloftype;
+ param->paramtypmod = -1;
+ param->paramcollid = get_typcollation(param->paramtype);
+ param->location = location;
+ }
+
+ ReleaseSysCache(tp);
+ }
+ }
+
+ return (Node *) param;
+}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index d415d7180f..8c352f9293 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -106,6 +106,7 @@ typedef struct
List *views; /* CREATE VIEW items */
List *indexes; /* CREATE INDEX items */
List *triggers; /* CREATE TRIGGER items */
+ List *variables; /* CREATE VARIABLE items */
List *grants; /* GRANT items */
} CreateSchemaStmtContext;
@@ -3186,6 +3187,7 @@ transformCreateSchemaStmt(CreateSchemaStmt *stmt)
cxt.views = NIL;
cxt.indexes = NIL;
cxt.triggers = NIL;
+ cxt.variables = NIL;
cxt.grants = NIL;
/*
@@ -3251,6 +3253,14 @@ transformCreateSchemaStmt(CreateSchemaStmt *stmt)
}
break;
+ case T_CreateSchemaVarStmt:
+ {
+ CreateSchemaVarStmt *elp = (CreateSchemaVarStmt *) element;
+
+ setSchemaName(cxt.schemaname, &elp->variable->schemaname);
+ cxt.variables = lappend(cxt.variables, element);
+ }
+
case T_GrantStmt:
cxt.grants = lappend(cxt.grants, element);
break;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 66253fc3d3..47a9b211d8 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3337,7 +3337,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
rt_entry_relation,
parsetree->resultRelation, NULL);
}
- else if (event == CMD_DELETE)
+ else if (event == CMD_DELETE || event == CMD_LET)
{
/* Nothing to do here */
}
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 66cc5c35c6..34ddb79a3d 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -193,6 +193,10 @@ ProcessQuery(PlannedStmt *plan,
"DELETE " UINT64_FORMAT,
queryDesc->estate->es_processed);
break;
+ case CMD_LET:
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "LET ");
+ break;
default:
strcpy(completionTag, "???");
break;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 3abe7d6155..27a21c48da 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavar.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
@@ -112,6 +113,7 @@ CommandIsReadOnly(PlannedStmt *pstmt)
case CMD_DELETE:
return false;
case CMD_UTILITY:
+ case CMD_LET:
/* For now, treat all utility commands as read/write */
return false;
default:
@@ -177,6 +179,7 @@ check_xact_readonly(Node *parsetree)
case T_CreateSchemaStmt:
case T_CreateSeqStmt:
case T_CreateStmt:
+ case T_CreateSchemaVarStmt:
case T_CreateTableAsStmt:
case T_RefreshMatViewStmt:
case T_CreateTableSpaceStmt:
@@ -1474,6 +1477,10 @@ ProcessUtilitySlow(ParseState *pstate,
address = AlterSequence(pstate, (AlterSeqStmt *) parsetree);
break;
+ case T_CreateSchemaVarStmt:
+ address = DefineSchemaVariable(pstate, (CreateSchemaVarStmt *) parsetree);
+ break;
+
case T_CreateTableAsStmt:
address = ExecCreateTableAs((CreateTableAsStmt *) parsetree,
queryString, params, queryEnv,
@@ -2095,6 +2102,10 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ case T_LetStmt:
+ tag = "LET";
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
@@ -2259,6 +2270,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_INDEX:
tag = "DROP INDEX";
break;
+ case OBJECT_VARIABLE:
+ tag = "DROP VARIABLE";
+ break;
case OBJECT_TYPE:
tag = "DROP TYPE";
break;
@@ -2513,6 +2527,10 @@ CreateCommandTag(Node *parsetree)
tag = "ALTER SEQUENCE";
break;
+ case T_CreateSchemaVarStmt:
+ tag = "CREATE VARIABLE";
+ break;
+
case T_DoStmt:
tag = "DO";
break;
@@ -2630,6 +2648,9 @@ CreateCommandTag(Node *parsetree)
case DISCARD_SEQUENCES:
tag = "DISCARD SEQUENCES";
break;
+ case DISCARD_VARIABLES:
+ tag = "DISCARD VARIABLES";
+ break;
default:
tag = "???";
}
@@ -2834,6 +2855,9 @@ CreateCommandTag(Node *parsetree)
case CMD_DELETE:
tag = "DELETE";
break;
+ case CMD_LET:
+ tag = "LET";
+ break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
@@ -2952,6 +2976,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ case T_LetStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
@@ -3405,6 +3433,7 @@ GetCommandLogLevel(Node *parsetree)
switch (stmt->commandType)
{
case CMD_SELECT:
+ case CMD_LET:
lev = LOGSTMT_ALL;
break;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 0cfc297b65..fcd695836a 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -808,6 +808,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c5f5a1ca3f..ba592be4ae 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -41,6 +41,7 @@
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
+#include "commands/schemavar.h"
#include "common/keywords.h"
#include "executor/spi.h"
#include "funcapi.h"
@@ -379,6 +380,7 @@ static void get_update_query_targetlist_def(Query *query, List *targetList,
deparse_context *context,
RangeTblEntry *rte);
static void get_delete_query_def(Query *query, deparse_context *context);
+static void get_let_query_def(Query *query, deparse_context *context);
static void get_utility_query_def(Query *query, deparse_context *context);
static void get_basic_select_query(Query *query, deparse_context *context,
TupleDesc resultDesc);
@@ -4926,6 +4928,10 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
get_delete_query_def(query, &context);
break;
+ case CMD_LET:
+ get_let_query_def(query, &context);
+ break;
+
case CMD_NOTHING:
appendStringInfoString(buf, "NOTHING");
break;
@@ -6134,6 +6140,58 @@ get_insert_query_def(Query *query, deparse_context *context)
}
}
+/* ----------
+ * get_let_query_def - Parse back an LET parsetree
+ * ----------
+ */
+static void
+get_let_query_def(Query *query, deparse_context *context)
+{
+ StringInfo buf = context->buf;
+ RangeTblEntry *select_rte = NULL;
+ RangeTblEntry *rte;
+ ListCell *l;
+
+ /*
+ * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
+ * single RTE for the SELECT or VALUES. Plain VALUES has neither.
+ */
+ foreach(l, query->rtable)
+ {
+ rte = (RangeTblEntry *) lfirst(l);
+
+ if (rte->rtekind == RTE_SUBQUERY)
+ {
+ if (select_rte)
+ elog(ERROR, "too many subquery RTEs in INSERT");
+ select_rte = rte;
+ }
+ }
+
+ /*
+ * Start the query with INSERT INTO relname
+ */
+ rte = rt_fetch(query->resultRelation, query->rtable);
+ Assert(rte->rtekind == RTE_RELATION);
+
+ if (PRETTY_INDENT(context))
+ {
+ context->indentLevel += PRETTYINDENT_STD;
+ appendStringInfoChar(buf, ' ');
+ }
+ appendStringInfo(buf, "LET %s ",
+ generate_relation_name(rte->relid, NIL));
+
+ appendStringInfo(buf, " = ");
+
+ if (select_rte)
+ {
+ /* Add the SELECT */
+ get_query_def(select_rte->subquery, buf, NIL, NULL,
+ context->prettyFlags, context->wrapColumn,
+ context->indentLevel);
+ }
+}
/* ----------
* get_update_query_def - Parse back an UPDATE parsetree
@@ -7208,6 +7266,13 @@ get_parameter(Param *param, deparse_context *context)
deparse_namespace *dpns;
ListCell *ancestor_cell;
+ if (param->paramkind == PARAM_SCHEMA_VARIABLE)
+ {
+ appendStringInfo(context->buf, "%s", get_schemavar_name(param->paramid));
+
+ return;
+ }
+
/*
* If it's a PARAM_EXEC parameter, try to locate the expression from which
* the parameter was computed. Note that failing to find a referent isn't
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 3560318749..ad0030c0dd 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -794,6 +794,7 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
case 'i':
case 's':
case 'E':
+ case 'V':
success = listTables(&cmd[1], pattern, show_verbose, show_system);
break;
case 'r':
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 466a78004b..c272de2baa 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1683,6 +1683,42 @@ describeOneTableDetails(const char *schemaname,
retval = true;
goto error_return; /* not an error, just return early */
}
+ else if (tableinfo.relkind == RELKIND_VARIABLE)
+ {
+ PGresult *res = NULL;
+ printQueryOpt myopt = pset.popt;
+
+ printfPQExpBuffer(&buf,
+ "SELECT pg_catalog.format_type(reloftype, NULL) AS \"%s\"\n"
+ "FROM pg_catalog.pg_class\n"
+ "WHERE oid = '%s';",
+ gettext_noop("Type"),
+ oid);
+
+ res = PSQLexec(buf.data);
+ if (!res)
+ goto error_return;
+
+ /* Did we get anything? */
+ if (PQntuples(res) == 0)
+ {
+ if (!pset.quiet)
+ psql_error("Did not find any variable with OID %s.\n", oid);
+ goto error_return;
+ }
+
+ printfPQExpBuffer(&title, _("Schema variable \"%s.%s\""),
+ schemaname, relationname);
+
+ myopt.title = title.data;
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+ PQclear(res);
+
+ retval = true;
+ goto error_return; /* not an error, just return early */
+ }
/*
* Get column info
@@ -3365,6 +3401,7 @@ listDbRoleSettings(const char *pattern, const char *pattern2)
* m - materialized views
* s - sequences
* E - foreign table (Note: different from 'f', the relkind value)
+ * V - schema variable
* (any order of the above is fine)
*/
bool
@@ -3376,6 +3413,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
bool showMatViews = strchr(tabtypes, 'm') != NULL;
bool showSeq = strchr(tabtypes, 's') != NULL;
bool showForeign = strchr(tabtypes, 'E') != NULL;
+ bool showVariables = strchr(tabtypes, 'V') != NULL;
PQExpBufferData buf;
PGresult *res;
@@ -3383,8 +3421,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
static const bool translate_columns[] = {false, false, true, false, false, false, false};
/* If tabtypes is empty, we default to \dtvmsE (but see also command.c) */
- if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign))
- showTables = showViews = showMatViews = showSeq = showForeign = true;
+ if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign || showVariables))
+ showTables = showViews = showMatViews = showSeq = showForeign = showVariables = true;
initPQExpBuffer(&buf);
@@ -3405,6 +3443,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
" WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
" WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
" WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+ " WHEN " CppAsString2(RELKIND_VARIABLE) " THEN '%s'"
" END as \"%s\",\n"
" pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
gettext_noop("Schema"),
@@ -3418,6 +3457,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
gettext_noop("foreign table"),
gettext_noop("table"), /* partitioned table */
gettext_noop("index"), /* partitioned index */
+ gettext_noop("schema variable"),
gettext_noop("Type"),
gettext_noop("Owner"));
@@ -3471,6 +3511,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL */
if (showForeign)
appendPQExpBufferStr(&buf, CppAsString2(RELKIND_FOREIGN_TABLE) ",");
+ if (showVariables)
+ appendPQExpBufferStr(&buf, CppAsString2(RELKIND_VARIABLE) ",");
appendPQExpBufferStr(&buf, "''"); /* dummy */
appendPQExpBufferStr(&buf, ")\n");
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..c5f107d814 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -60,7 +60,7 @@ extern bool listTSTemplates(const char *pattern, bool verbose);
/* \l */
extern bool listAllDbs(const char *pattern, bool verbose);
-/* \dt, \di, \ds, \dS, etc. */
+/* \dt, \di, \ds, \dS, \dvar etc. */
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
/* \dD */
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 702e742af4..2da50f7290 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
@@ -257,6 +257,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[S+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dV[S+] [PATTERN] list schema variables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 8bc4a194a5..ba5f6b0832 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -679,6 +679,20 @@ static const SchemaQuery Query_for_list_of_statistics = {
NULL
};
+static const SchemaQuery Query_for_list_of_variables = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ "c.relkind IN ('V')",
+ /* viscondition */
+ "pg_catalog.pg_table_is_visible(c.oid)",
+ /* namespace */
+ "c.relnamespace",
+ /* result */
+ "pg_catalog.quote_ident(c.relname)",
+ /* qualresult */
+ NULL
+};
/*
* Queries to get lists of names of various kinds of things, possibly
@@ -1108,6 +1122,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
{"USER MAPPING FOR", NULL, NULL},
+ {"VARIABLE", NULL, &Query_for_list_of_variables},
{"VIEW", NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
@@ -1460,7 +1475,7 @@ psql_completion(const char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
- "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LET", "LISTEN", "LOAD", "LOCK",
"MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
"RESET", "REVOKE", "ROLLBACK",
@@ -1479,7 +1494,7 @@ psql_completion(const char *text, int start, int end)
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+ "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", "\\dvar",
"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
"\\endif", "\\errverbose", "\\ev",
"\\f",
@@ -2684,6 +2699,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
COMPLETE_WITH_LIST2("GROUP", "ROLE");
+/* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
+ /* Complete CREATE VARIABLE <name> with AS */
+ else if (TailMatches3("CREATE", "VARIABLE", MatchAny))
+ COMPLETE_WITH_CONST("AS");
+ /* Complete CREATE VARIABLE <name> with AS types*/
+ else if (TailMatches4("CREATE", "VARIABLE", MatchAny, "AS"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE VIEW <name> with AS */
else if (TailMatches3("CREATE", "VIEW", MatchAny))
@@ -2839,6 +2862,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP VARIABLE */
+ else if (Matches2("DROP", "VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ else if (Matches3("DROP", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -2849,14 +2878,14 @@ psql_completion(const char *text, int start, int end)
* Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "ANALYZE", "VERBOSE");
+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "ANALYZE", "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "ANALYZE"))
- COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "VERBOSE");
+ COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "VERBOSE") ||
Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
- COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
+ COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "LET");
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
@@ -2965,6 +2994,7 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'ALL ROUTINES IN SCHEMA'"
" UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
" UNION SELECT 'ALL TABLES IN SCHEMA'"
+ " UNION SELECT 'ALL VARIABLES IN SCHEMA'"
" UNION SELECT 'DATABASE'"
" UNION SELECT 'DOMAIN'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
@@ -2978,14 +3008,16 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'SEQUENCE'"
" UNION SELECT 'TABLE'"
" UNION SELECT 'TABLESPACE'"
- " UNION SELECT 'TYPE'");
+ " UNION SELECT 'TYPE'"
+ " UNION SELECT 'VARIABLE'");
}
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
- COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA",
+ COMPLETE_WITH_LIST6("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
- "TABLES IN SCHEMA");
+ "TABLES IN SCHEMA",
+ "VARIABLES IN SCHEMA");
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
@@ -3015,6 +3047,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
else if (TailMatches1("TABLE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
+ else if (TailMatches1("VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
else if (TailMatches1("TABLESPACE"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches1("TYPE"))
@@ -3171,7 +3205,7 @@ psql_completion(const char *text, int start, int end)
/* PREPARE xx AS */
else if (Matches3("PREPARE", MatchAny, "AS"))
- COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
+ COMPLETE_WITH_LIST5("SELECT", "UPDATE", "INSERT", "DELETE FROM", "LET");
/*
* PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -3390,6 +3424,14 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
COMPLETE_WITH_CONST("=");
+/* LET --- can be inside EXPLAIN, PREPARE etc */
+ /* If prev. word is LET suggest a list of variables */
+ else if (TailMatches1("LET"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ /* Complete LET <variable> with "=" */
+ else if (TailMatches2("LET", MatchAny))
+ COMPLETE_WITH_CONST("=");
+
/* USER MAPPING */
else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 26b1866c69..c5146fc138 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -167,6 +167,7 @@ DESCR("");
#define RELKIND_FOREIGN_TABLE 'f' /* foreign table */
#define RELKIND_PARTITIONED_TABLE 'p' /* partitioned table */
#define RELKIND_PARTITIONED_INDEX 'I' /* partitioned index */
+#define RELKIND_VARIABLE 'V' /* schema variable */
#define RELPERSISTENCE_PERMANENT 'p' /* regular table */
#define RELPERSISTENCE_UNLOGGED 'u' /* unlogged permanent table */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 11b306037d..13232d7a43 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -71,5 +71,6 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* PG_DEFAULT_ACL_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f01648c961..600d3d5849 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5533,6 +5533,12 @@ DESCR("list of files in the WAL directory");
DATA(insert OID = 5028 ( satisfies_hash_partition PGNSP PGUID 12 1 0 2276 0 f f f f f f i s 4 0 16 "26 23 23 2276" _null_ "{i,i,i,v}" _null_ _null_ _null_ satisfies_hash_partition _null_ _null_ _null_ ));
DESCR("hash partition CHECK constraint");
+/* schema variables function interface */
+DATA(insert OID = 6122 ( get_schema_variable PGNSP PGUID 12 1 0 0 0 f f f f f f v r 2 0 2283 "2205 2283" _null_ _null_ _null_ _null_ _null_ get_schema_variable _null_ _null_ _null_ ));
+DESCR("returns value of schema variable");
+DATA(insert OID = 6123 ( set_schema_variable PGNSP PGUID 12 1 0 0 0 f f f f f f v r 2 0 2278 "2205 2283" _null_ _null_ _null_ _null_ _null_ set_schema_variable _null_ _null_ _null_ ));
+DESCR("returns value of schema variable");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/commands/schemavar.h b/src/include/commands/schemavar.h
new file mode 100644
index 0000000000..6f65b1f1d3
--- /dev/null
+++ b/src/include/commands/schemavar.h
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavar.h
+ * prototypes for schemavar.c.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/schemavar.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SCHEMAVAR_H
+#define SCHEMAVAR_H
+
+#include "catalog/objectaddress.h"
+#include "nodes/parsenodes.h"
+#include "parser/parse_node.h"
+
+extern ObjectAddress DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *var);
+
+extern void ResetSchemaVariablesCache(void);
+
+extern char *get_schemavar_name(Oid varid);
+
+extern void SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod, int16 typlen, bool typbyval);
+extern Datum GetSchemaVariable(Oid varid, bool *isNull, Oid typid, int32 typmod, int16 typlen, bool typbyval);
+extern void SetSchemaVariableSecure(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod, int16 typlen, bool typbyval);
+extern Datum GetSchemaVariableSecure(Oid varid, bool *isNull, Oid typid, int32 typmod, int16 typlen, bool typbyval);
+
+#endif
\ No newline at end of file
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 117fc892f4..a282f1e4e0 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_SCHEMA_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -342,11 +343,14 @@ typedef struct ExprEvalStep
TupleDesc argdesc;
} nulltest_row;
- /* for EEOP_PARAM_EXEC/EXTERN */
+ /* for EEOP_PARAM_EXEC/EXTERN/VARIABLE */
struct
{
- int paramid; /* numeric ID for parameter */
- Oid paramtype; /* OID of parameter's datatype */
+ int paramid; /* numeric ID for parameter */
+ Oid paramtype; /* OID of parameter's datatype */
+ int32 paramtypmod; /* typmod of param (not used yet) */
+ int16 paramtyplen; /* expected length */
+ bool paramtypbyval; /* is passed by value */
} param;
/* for EEOP_PARAM_CALLBACK */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 74b094a9c3..2f4986099d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -344,6 +344,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSchemaVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
@@ -415,6 +416,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
+ T_LetStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -657,6 +659,7 @@ typedef enum CmdType
CMD_UPDATE, /* update stmt */
CMD_INSERT, /* insert stmt */
CMD_DELETE,
+ CMD_LET,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
CMD_NOTHING /* dummy command for instead nothing rules
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a16de289ba..d1d03c9cbe 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1488,6 +1488,14 @@ typedef struct UpdateStmt
WithClause *withClause; /* WITH clause */
} UpdateStmt;
+typedef struct LetStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* relation to insert into */
+ Node *selectStmt; /* the source SELECT/VALUES, or NULL */
+} LetStmt;
+
+
/* ----------------------
* Select Statement
*
@@ -1665,6 +1673,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -2478,6 +2487,18 @@ typedef struct AlterSeqStmt
} AlterSeqStmt;
/* ----------------------
+ * Create VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSchemaVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the variable type */
+ bool if_not_exists; /* just do nothing if it already exists? */
+} CreateSchemaVarStmt;
+
+/* ----------------------
* Create {Aggregate|Operator|Type} Statement
* ----------------------
*/
@@ -3206,7 +3227,8 @@ typedef enum DiscardMode
DISCARD_ALL,
DISCARD_PLANS,
DISCARD_SEQUENCES,
- DISCARD_TEMP
+ DISCARD_TEMP,
+ DISCARD_VARIABLES
} DiscardMode;
typedef struct DiscardStmt
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..b366471940 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -229,13 +229,17 @@ typedef struct Const
* of the `paramid' field contain the SubLink's subLinkId, and
* the low-order 16 bits contain the column number. (This type
* of Param is also converted to PARAM_EXEC during planning.)
+ *
+ * PARAM_SCHEMA_VARIABLE: The parameter is a access to schema variable
+ * paramid holds varid.
*/
typedef enum ParamKind
{
PARAM_EXTERN,
PARAM_EXEC,
PARAM_SUBLINK,
- PARAM_MULTIEXPR
+ PARAM_MULTIEXPR,
+ PARAM_SCHEMA_VARIABLE
} ParamKind;
typedef struct Param
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 26af944e03..3971d7478b 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -229,6 +229,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("let", LET, UNRESERVED_KEYWORD)
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -430,6 +431,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 4e96fa7907..18ba221180 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -134,6 +134,8 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
* to process them like UPDATE. (Note this can change intra-statement, for
* cases like INSERT ON CONFLICT UPDATE.)
*
+ * p_is_let: true to process assignment expressions like LET.
+ *
* p_windowdefs: list of WindowDefs representing WINDOW and OVER clauses.
* We collect these while transforming expressions and then transform them
* afterwards (so that any resjunk tlist items needed for the sort/group
@@ -183,6 +185,7 @@ struct ParseState
Relation p_target_relation; /* INSERT/UPDATE/DELETE target rel */
RangeTblEntry *p_target_rangetblentry; /* target rel's RTE */
bool p_is_insert; /* process assignment like INSERT not UPDATE */
+ bool p_is_let; /* process assignment LET stmt */
List *p_windowdefs; /* raw representations of window clauses */
ParseExprKind p_expr_kind; /* what kind of expression we're parsing */
int p_next_resno; /* next targetlist resno to assign */
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index b9792acdae..760aaed9a8 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -129,4 +129,6 @@ extern Oid attnumTypeId(Relation rd, int attid);
extern Oid attnumCollationId(Relation rd, int attid);
extern bool isQueryUsingTempRelation(Query *query);
+extern Node *toSchemaVariable(ParseState *pstate, char *nspname, char *varname, int location);
+
#endif /* PARSE_RELATION_H */
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f4d4be8d0d..d0737a9e4b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -166,6 +166,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_SELECT|ACL_UPDATE)
/* operation codes for pg_*_aclmask */
typedef enum
diff --git a/src/test/regress/expected/schema_variables.out b/src/test/regress/expected/schema_variables.out
new file mode 100644
index 0000000000..ad700c15d8
--- /dev/null
+++ b/src/test/regress/expected/schema_variables.out
@@ -0,0 +1,236 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+-- should to fail
+CREATE VARIABLE var2 AS pg_class;
+ERROR: Composite types are not allowed as variable type.
+DROP VARIABLE var1, var2;
+-- functional interface, attention typmod is not stored
+CREATE VARIABLE var1 AS numeric(10,1);
+SELECT set_schema_variable('var1', 333);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 333
+(1 row)
+
+SELECT set_schema_variable('var1', 333::integer);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 333
+(1 row)
+
+SELECT set_schema_variable('var1', '333.55'::text);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 333.55
+(1 row)
+
+SELECT get_schema_variable('var1', null::int);
+ get_schema_variable
+---------------------
+ 334
+(1 row)
+
+SELECT get_schema_variable('var1', null::text);
+ get_schema_variable
+---------------------
+ 333.55
+(1 row)
+
+-- access rights test
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT set_schema_variable('var1', '1000'::text);
+ERROR: permission denied for schema variable var1
+SELECT get_schema_variable('var1', null::numeric);
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT SELECT ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT set_schema_variable('var1', '1000'::text);
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 333.55
+(1 row)
+
+SET ROLE TO DEFAULT;
+GRANT UPDATE ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to work
+SELECT set_schema_variable('var1', '1000'::text);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT get_schema_variable('var1', null::numeric);
+ get_schema_variable
+---------------------
+ 1000
+(1 row)
+
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE VARIABLE var AS integer;
+SELECT set_schema_variable('public.var', 1234);
+ set_schema_variable
+---------------------
+
+(1 row)
+
+SELECT public.var;
+ var
+------
+ 1234
+(1 row)
+
+DO $$
+BEGIN
+ RAISE NOTICE 'public.var is = %', public.var;
+END;
+$$;
+NOTICE: public.var is = 1234
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var;
+$$ LANGUAGE sql SECURITY DEFINER;
+SELECT secure_var();
+ secure_var
+------------
+ 1234
+(1 row)
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var;
+ERROR: permission denied for schema variable var
+-- should to work;
+SELECT secure_var();
+ secure_var
+------------
+ 1234
+(1 row)
+
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: (g.v = var)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var;
+SELECT * FROM schema_var_view;
+ var
+------
+ 1234
+(1 row)
+
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ var
+-----
+
+(1 row)
+
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+------------------------------------------------------
+ Let on public.var1
+ -> Result
+ Output: '3.14159265358979'::double precision
+(3 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+SELECT inc(1);
+ inc
+-----
+ 1
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 2
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 3
+(1 row)
+
+SELECT inc(1) FROM generate_series(1,10);
+ inc
+-----
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+(10 rows)
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP ROLE var_test_role;
+DROP VARIABLE var CASCADE;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index ad9434fb87..33fe7ee476 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml schema_variables
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 27cd49845e..22c4cac7ce 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -187,3 +187,4 @@ test: hash_part
test: indexing
test: event_trigger
test: stats
+test: schema_variables
diff --git a/src/test/regress/sql/schema_variables.sql b/src/test/regress/sql/schema_variables.sql
new file mode 100644
index 0000000000..9ee9e174f9
--- /dev/null
+++ b/src/test/regress/sql/schema_variables.sql
@@ -0,0 +1,139 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+
+-- should to fail
+CREATE VARIABLE var2 AS pg_class;
+
+DROP VARIABLE var1, var2;
+
+-- functional interface, attention typmod is not stored
+CREATE VARIABLE var1 AS numeric(10,1);
+SELECT set_schema_variable('var1', 333);
+SELECT get_schema_variable('var1', null::numeric);
+
+SELECT set_schema_variable('var1', 333::integer);
+SELECT get_schema_variable('var1', null::numeric);
+
+SELECT set_schema_variable('var1', '333.55'::text);
+SELECT get_schema_variable('var1', null::numeric);
+SELECT get_schema_variable('var1', null::int);
+SELECT get_schema_variable('var1', null::text);
+
+-- access rights test
+
+CREATE ROLE var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT set_schema_variable('var1', '1000'::text);
+SELECT get_schema_variable('var1', null::numeric);
+
+SET ROLE TO DEFAULT;
+
+GRANT SELECT ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT set_schema_variable('var1', '1000'::text);
+-- should to work
+SELECT get_schema_variable('var1', null::numeric);
+
+SET ROLE TO DEFAULT;
+
+GRANT UPDATE ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to work
+SELECT set_schema_variable('var1', '1000'::text);
+SELECT get_schema_variable('var1', null::numeric);
+
+SET ROLE TO DEFAULT;
+
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+
+CREATE VARIABLE var AS integer;
+
+SELECT set_schema_variable('public.var', 1234);
+
+SELECT public.var;
+
+DO $$
+BEGIN
+ RAISE NOTICE 'public.var is = %', public.var;
+END;
+$$;
+
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var;
+$$ LANGUAGE sql SECURITY DEFINER;
+
+SELECT secure_var();
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT public.var;
+
+-- should to work;
+SELECT secure_var();
+
+SET ROLE TO DEFAULT;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var;
+
+CREATE VIEW schema_var_view AS SELECT var;
+
+SELECT * FROM schema_var_view;
+
+\c -
+
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+
+LET var1 = pi();
+
+SELECT var1;
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+
+EXECUTE var_pp(100, 1.23456);
+
+SELECT var1;
+
+CREATE VARIABLE var3 AS int;
+
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT inc(1);
+SELECT inc(1);
+SELECT inc(1);
+
+SELECT inc(1) FROM generate_series(1,10);
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+LET var3 = 0;
+
+SET ROLE TO DEFAULT;
+
+DROP VIEW schema_var_view;
+
+DROP ROLE var_test_role;
+
+DROP VARIABLE var CASCADE;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index d4765ce3b0..b0404c21a5 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -423,6 +423,7 @@ CreateReplicationSlotCmd
CreateRoleStmt
CreateSchemaStmt
CreateSchemaStmtContext
+CreateSchemaVarStmt
CreateSeqStmt
CreateStatsStmt
CreateStmt
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
@ 2018-02-03 06:58 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-02-03 06:58 UTC (permalink / raw)
To: David G. Johnston <david.g.johnston@gmail.com>; +Cc: Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi
2018-02-03 1:48 GMT+01:00 David G. Johnston <david.g.johnston@gmail.com>:
> I've done a non-compilation documentation review, the diff from the poc
> patch and the diff from master are attached.
>
> Comments are inter-twined in the patch in xml comment format; though I
> reiterate (some of?) them below.
>
> On Fri, Feb 2, 2018 at 3:06 PM, Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
>
>> Hi
>>
>> I wrote proof concept of schema variables. The patch is not nice, but the
>> functionality is almost complete (for scalars only) and can be good enough
>> for playing with this concept.
>>
>> I recap a goals (the order is random):
>>
>> 1. feature like PL/SQL package variables (with similar content life cycle)
>> 2. available from any PL used by PostgreSQL, data can be shared between
>> different PL
>> 3. possibility to store short life data in fast secured storage
>>
>
> The generic use of the word secure here bothers me. I'm taking it to be
> "protected by grant/revoke"-based privileges; plus session-locality.
>
I have not a problem with any other formulation.
>
> 4. possibility to pass parameters and results to/from anonymous blocks
>> 5. session variables with possibility to process static code check
>>
>
> What does "process static code check" means here?
>
It mean the possibility to check validity of code without code execution.
You can use plpgsql_check for example.
>
>
>> 6. multiple API available from different environments - SQL commands, SQL
>> functions, internal functions
>>
>
> I made the public aspect of this explicit in the CREATE VARIABLE doc
> (though as noted below it probably belongs in section II)
>
>
>> 7. data are stored in binary form
>>
>
> Thoughts during my review:
>
> There is, for me, a cognitive dissonance between "schema variable" and
> "variable value" - I'm partial to the later. Since we use "setting" for
> GUCs the term variable here hopefully wouldn't cause ambiguity...
>
The "schema" is important in this case. 1) it is a analogy to "package
variable", 2) not necessary, but probably often it will be used together
with PLpgSQL. There are variables too. "Session variables" doesn't well
specify the implementation. The session variables can be GUC, psql client
variables or some custom implementation in Postgres or package variables in
Oracle.
> I've noticed that we don't seem to have or enforce any policy on how to
> communicate "SQL standards compatibility" to the user...
>
> We are missing the ability to alter ownership (or at least its
> undocumented), and if that brings into existing ALTER VARIABLE we should
> probably add ALTER TYPE TO new_type USING (cast) for completeness.
>
good note. I didn't test it. I am not sure, what variants of ALTER should
be supported. Type of variables is interface. Probably we can allow to add
new field, but change type or remove field can break other object. So it
can be prohibited like we doesn't support ALTER on views. ALTERing is
another and pretty complex topic, and I don't think it is necessary to
solve it now. This feature can be valuable without ALTER support, and
nothing block later ALTER VARIABLE implementation.
This design allows lot of interesting features (that can be implemented
step by step)
1. support for default expression
2. support for constraints and maybe triggers
3. reset on transaction end
4. initialization of session start - via default expression or triggers it
can be way how to start code on session start.
>
> Its left for the reader to presume that because these are schema
> "relations" that namespace resolution via search_path works the same as any
> other relation.
>
> I think I've answered my own question regarding DISCARD in that
> "variables" discards values while if TEMP is in effect all temp variables
> are dropped.
>
DISCARD should to remove TEMP variables and should to remove content of all
variables.
>
> Examples abound though it doesn't feel like too much: but saying "The
> usage is very simple:" before giving the example in the function section
> seems to be outside of our general style. A better preamble than "An
> example:" would be nice but the example is so simple I could not think of
> anything worth writing.
>
This doc is just design frame. I invite any enhancing because this feature
can be difficult for some people, because mix persistent object with
temporal/session content - and term "variable" can be used in relation
algebra in different semantic. It is natural for people with stored
procedures experience - mainly with Oracle, but for any other can be little
bit difficult. I believe so there should be more practical examples -
related to RLS for example.
>
> Its worth considering how both:
>
> https://www.postgresql.org/docs/10/static/ddl.html
> and
> https://www.postgresql.org/docs/10/static/queries.html
>
> could be updated to incorporate the broad picture of schema variables,
> with examples, and leave the reference (SQL and functions) sections mainly
> relegated to syntax and reminders.
>
> A moderate number of lines changed are for typos and minor grammar edits.
>
Thank you very much
Regards
Pavel
> David J.
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-02-07 06:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-02-07 06:34 UTC (permalink / raw)
To: David G. Johnston <david.g.johnston@gmail.com>; +Cc: Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi
updated patch with your changes in documentation and pg_dump (initial)
support
Main issue of this patch is storage. We can reuse local buffers used for
temp tables. But it does allocation by 8KB and it creates temp files for
every object. That is too big overhead. Storing just in session memory is
too simple - then there should be lot of new code used, when variable will
be dropped.
I have ideas how to allow work with mix of scalar and composite types - so
it will be next step of this prototype.
Regards
Pavel
>
Attachments:
[application/octet-stream] schema-variables-poc-180207-01-diff (131.9K, ../../CAFj8pRCTsxzsxX1cWgX3cjqsSZb=gqOMd3-2FdbGz5a2+shGMg@mail.gmail.com/3-schema-variables-poc-180207-01-diff)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-03-08 18:00 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 06:49 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-03-08 18:00 UTC (permalink / raw)
To: David G. Johnston <david.g.johnston@gmail.com>; Pavel Luzanov <p.luzanov@postgrespro.ru>; +Cc: Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi
2018-02-07 7:34 GMT+01:00 Pavel Stehule <pavel.stehule@gmail.com>:
> Hi
>
> updated patch with your changes in documentation and pg_dump (initial)
> support
>
> Main issue of this patch is storage. We can reuse local buffers used for
> temp tables. But it does allocation by 8KB and it creates temp files for
> every object. That is too big overhead. Storing just in session memory is
> too simple - then there should be lot of new code used, when variable will
> be dropped.
>
> I have ideas how to allow work with mix of scalar and composite types - so
> it will be next step of this prototype.
>
> Regards
>
> Pavel
>
new update - rebased, + some initial support for composite values on right
side and custom types, arrays are supported too.
omega=# CREATE VARIABLE xx AS (a int, b numeric);
CREATE VARIABLE
omega=# LET xx = (10, 20)::xx;
LET
omega=# SELECT xx;
+---------+
| xx |
+---------+
| (10,20) |
+---------+
(1 row)
omega=# SELECT xx.a + xx.b;
+----------+
| ?column? |
+----------+
| 30 |
+----------+
(1 row)
omega=# \d xx
schema variable "public.xx"
+--------+---------+
| Column | Type |
+--------+---------+
| a | integer |
| b | numeric |
+--------+---------+
Regards
Pavel
Attachments:
[application/octet-stream] schema-variables-poc-180308-01-diff (164.0K, ../../CAFj8pRA6YWwV=sZj5iSgDUixr-S+u9W2+v0BJqqxHQDXS2oZww@mail.gmail.com/3-schema-variables-poc-180308-01-diff)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-03-12 06:49 ` Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 06:54 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Luzanov @ 2018-03-12 06:49 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; David G. Johnston <david.g.johnston@gmail.com>; +Cc: Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi,
I plan to make usability and feature test review in several days.
Is there any chances that it will work on replicas?
Such possibility is very helpful in generating reports.
Now, LET command produces an error:
ERROR: cannot execute LET in a read-only transaction
But if we say that variables are non-transactional ?
-----
Pavel Luzanov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On 08.03.2018 21:00, Pavel Stehule wrote:
> Hi
>
> 2018-02-07 7:34 GMT+01:00 Pavel Stehule <pavel.stehule@gmail.com
> <mailto:pavel.stehule@gmail.com>>:
>
> Hi
>
> updated patch with your changes in documentation and pg_dump
> (initial) support
>
> Main issue of this patch is storage. We can reuse local buffers
> used for temp tables. But it does allocation by 8KB and it creates
> temp files for every object. That is too big overhead. Storing
> just in session memory is too simple - then there should be lot of
> new code used, when variable will be dropped.
>
> I have ideas how to allow work with mix of scalar and composite
> types - so it will be next step of this prototype.
>
> Regards
>
> Pavel
>
>
> new update - rebased, + some initial support for composite values on
> right side and custom types, arrays are supported too.
>
> omega=# CREATE VARIABLE xx AS (a int, b numeric);
> CREATE VARIABLE
> omega=# LET xx = (10, 20)::xx;
> LET
> omega=# SELECT xx;
> +---------+
> | xx |
> +---------+
> | (10,20) |
> +---------+
> (1 row)
>
> omega=# SELECT xx.a + xx.b;
> +----------+
> | ?column? |
> +----------+
> | 30 |
> +----------+
> (1 row)
>
> omega=# \d xx
> schema variable "public.xx"
> +--------+---------+
> | Column | Type |
> +--------+---------+
> | a | integer |
> | b | numeric |
> +--------+---------+
>
>
> Regards
>
> Pavel
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 06:49 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
@ 2018-03-12 06:54 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 15:38 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-03-12 06:54 UTC (permalink / raw)
To: Pavel Luzanov <p.luzanov@postgrespro.ru>; +Cc: David G. Johnston <david.g.johnston@gmail.com>; Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2018-03-12 7:49 GMT+01:00 Pavel Luzanov <p.luzanov@postgrespro.ru>:
> Hi,
>
> I plan to make usability and feature test review in several days.
>
> Is there any chances that it will work on replicas?
> Such possibility is very helpful in generating reports.
> Now, LET command produces an error:
>
> ERROR: cannot execute LET in a read-only transaction
>
>
> But if we say that variables are non-transactional ?
>
sure, it should to work. Now, I am try to solve a issues on concept level -
the LET code is based on DML code base, so probably there is check for rw
transactions. But it is useless for LET command.
Regards
Pavel
>
> -----
> Pavel Luzanov
> Postgres Professional: http://www.postgrespro.com
> The Russian Postgres Company
>
> On 08.03.2018 21:00, Pavel Stehule wrote:
>
> Hi
>
> 2018-02-07 7:34 GMT+01:00 Pavel Stehule <pavel.stehule@gmail.com>:
>
>> Hi
>>
>> updated patch with your changes in documentation and pg_dump (initial)
>> support
>>
>> Main issue of this patch is storage. We can reuse local buffers used for
>> temp tables. But it does allocation by 8KB and it creates temp files for
>> every object. That is too big overhead. Storing just in session memory is
>> too simple - then there should be lot of new code used, when variable will
>> be dropped.
>>
>> I have ideas how to allow work with mix of scalar and composite types -
>> so it will be next step of this prototype.
>>
>> Regards
>>
>> Pavel
>>
>
> new update - rebased, + some initial support for composite values on right
> side and custom types, arrays are supported too.
>
> omega=# CREATE VARIABLE xx AS (a int, b numeric);
> CREATE VARIABLE
> omega=# LET xx = (10, 20)::xx;
> LET
> omega=# SELECT xx;
> +---------+
> | xx |
> +---------+
> | (10,20) |
> +---------+
> (1 row)
>
> omega=# SELECT xx.a + xx.b;
> +----------+
> | ?column? |
> +----------+
> | 30 |
> +----------+
> (1 row)
>
> omega=# \d xx
> schema variable "public.xx"
> +--------+---------+
> | Column | Type |
> +--------+---------+
> | a | integer |
> | b | numeric |
> +--------+---------+
>
>
> Regards
>
> Pavel
>
>
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 06:49 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 06:54 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-03-12 15:38 ` Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 16:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Luzanov @ 2018-03-12 15:38 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: David G. Johnston <david.g.johnston@gmail.com>; Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
On 12.03.2018 09:54, Pavel Stehule wrote:
>
> 2018-03-12 7:49 GMT+01:00 Pavel Luzanov <p.luzanov@postgrespro.ru
> <mailto:p.luzanov@postgrespro.ru>>:
>
>
> Is there any chances that it will work on replicas?
>
> ...
>
> sure, it should to work. Now, I am try to solve a issues on concept
> level - the LET code is based on DML code base, so probably there is
> check for rw transactions. But it is useless for LET command.
Very, very good!
As I understand, the work on this patch now in progress and it not in
commitfest.
Please explain what features of schema variables I can review now.
From first post of this thread the syntax of the CREATE VARIABLE command:
CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
[ DEFAULT expression ] [[NOT] NULL]
[ ON TRANSACTION END { RESET | DROP } ]
[ { VOLATILE | STABLE } ];
But in psql I see only:
\h create variable
Command: CREATE VARIABLE
Description: define a new permissioned typed schema variable
Syntax:
CREATE VARIABLE [ IF NOT EXISTS ] name [ AS ] data_type ]
I can include DEFAULT clause in CREATE VARIABLE command, but the value
not used:
postgres=# create variable i int default 0;
CREATE VARIABLE
postgres=# select i;
i
---
(1 row)
postgres=# \d+ i
schema variable "public.i"
Column | Type | Storage
--------+---------+---------
i | integer | plain
BTW, I found an error in handling of table aliases:
postgres=# create variable x text;
CREATE VARIABLE
postgres=# select * from pg_class AS x where x.relname = 'x';
ERROR: type text is not composite
It thinks that x.relname is an attribute of x variable instead of an
alias for pg_class table.
-----
Pavel Luzanov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 06:49 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 06:54 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 15:38 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
@ 2018-03-12 16:13 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-13 09:54 ` Re: proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-20 17:38 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-03-12 16:13 UTC (permalink / raw)
To: Pavel Luzanov <p.luzanov@postgrespro.ru>; +Cc: David G. Johnston <david.g.johnston@gmail.com>; Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2018-03-12 16:38 GMT+01:00 Pavel Luzanov <p.luzanov@postgrespro.ru>:
>
> On 12.03.2018 09:54, Pavel Stehule wrote:
>
>
> 2018-03-12 7:49 GMT+01:00 Pavel Luzanov <p.luzanov@postgrespro.ru>:
>
>>
>> Is there any chances that it will work on replicas?
>>
> ...
>
> sure, it should to work. Now, I am try to solve a issues on concept level
> - the LET code is based on DML code base, so probably there is check for rw
> transactions. But it is useless for LET command.
>
>
> Very, very good!
>
> As I understand, the work on this patch now in progress and it not in
> commitfest.
> Please explain what features of schema variables I can review now.
>
> From first post of this thread the syntax of the CREATE VARIABLE command:
> CREATE [TEMP] VARIABLE [IF NOT EXISTS] name AS type
> [ DEFAULT expression ] [[NOT] NULL]
> [ ON TRANSACTION END { RESET | DROP } ]
> [ { VOLATILE | STABLE } ];
>
Now, it is too early for review - it is in development. Some features are
not implemented yet - DEFAULTs, ON TRANSACTION END .., others has not sense
(what I know now VOLATILE, STABLE). Schema variables are passed as
parameters to query, so the behave is like any other params - it is STABLE
only.
>
> But in psql I see only:
> \h create variable
> Command: CREATE VARIABLE
> Description: define a new permissioned typed schema variable
> Syntax:
> CREATE VARIABLE [ IF NOT EXISTS ] name [ AS ] data_type ]
>
> I can include DEFAULT clause in CREATE VARIABLE command, but the value not
> used:
> postgres=# create variable i int default 0;
> CREATE VARIABLE
> postgres=# select i;
> i
> ---
>
> (1 row)
>
> postgres=# \d+ i
> schema variable "public.i"
> Column | Type | Storage
> --------+---------+---------
> i | integer | plain
>
>
defaults are not implemented yet
>
> BTW, I found an error in handling of table aliases:
>
> postgres=# create variable x text;
> CREATE VARIABLE
> postgres=# select * from pg_class AS x where x.relname = 'x';
> ERROR: type text is not composite
>
> It thinks that x.relname is an attribute of x variable instead of an alias
> for pg_class table.
>
>
It is not well handled collision. This should be detected and prohibited.
In this case, because x is scalar, then x.xx has not sense, and then it
should not be handled like variable. So the current design is not too
practical - it generates more collisions than it is necessary and still,
there are some errors.
Now, there is one important question - storage - Postgres stores all
objects to files - only memory storage is not designed yet. This is part,
where I need a help.
Regards
Pavel
>
> -----
> Pavel Luzanov
> Postgres Professional: http://www.postgrespro.com
> The Russian Postgres Company
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 06:49 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 06:54 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 15:38 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 16:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-03-13 09:54 ` Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-13 18:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Luzanov @ 2018-03-13 09:54 UTC (permalink / raw)
To: pgsql-hackers@postgresql.org
Pavel Stehule wrote
> Now, there is one important question - storage - Postgres stores all
> objects to files - only memory storage is not designed yet. This is part,
> where I need a help.
O, I do not feel confident in such questions.
May be some ideas you can get from extension with similar functionality:
https://github.com/postgrespro/pg_variables
-----
Pavel Luzanov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
--
Sent from: http://www.postgresql-archive.org/PostgreSQL-hackers-f1928748.html
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 06:49 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 06:54 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 15:38 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 16:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-13 09:54 ` Re: proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
@ 2018-03-13 18:44 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-03-13 18:44 UTC (permalink / raw)
To: Pavel Luzanov <p.luzanov@postgrespro.ru>; +Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2018-03-13 10:54 GMT+01:00 Pavel Luzanov <p.luzanov@postgrespro.ru>:
> Pavel Stehule wrote
> > Now, there is one important question - storage - Postgres stores all
> > objects to files - only memory storage is not designed yet. This is part,
> > where I need a help.
>
> O, I do not feel confident in such questions.
> May be some ideas you can get from extension with similar functionality:
> https://github.com/postgrespro/pg_variables
Unfortunately not - it doesn't implement this functionality
Regards
Pavel
>
>
> -----
> Pavel Luzanov
> Postgres Professional: http://www.postgrespro.com
> The Russian Postgres Company
>
>
>
> --
> Sent from: http://www.postgresql-archive.org/PostgreSQL-hackers-
> f1928748.html
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 06:49 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 06:54 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 15:38 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 16:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-03-20 17:38 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-21 05:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-03-20 17:38 UTC (permalink / raw)
To: Pavel Luzanov <p.luzanov@postgrespro.ru>; +Cc: David G. Johnston <david.g.johnston@gmail.com>; Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Hi
I am sending new update. The code is less ugly, and the current
functionality is +/- final for first stage. It should be good enough for
playing and testing this concept.
What is supported:
1. scalar, composite and array variables
2. composite can be defined on place or some composite type can be used
3. variable, or any field of variable, can have defined default value
4. variable is database object - the access rights are required
5. the values are stored in binary form with defined typmod
An usage is very simple:
postgres=# create variable foo as numeric default 0;
CREATE VARIABLE
postgres=# select foo;
┌─────┐
│ foo │
╞═════╡
│ 0 │
└─────┘
(1 row)
postgres=# let foo = pi();
LET
postgres=# select foo;
┌──────────────────┐
│ foo │
╞══════════════════╡
│ 3.14159265358979 │
└──────────────────┘
(1 row)
postgres=# create variable boo as (x numeric default 0, y numeric default
0);
CREATE VARIABLE
postgres=# let boo.x = 100;
LET
postgres=# select boo;
┌─────────┐
│ boo │
╞═════════╡
│ (100,0) │
└─────────┘
(1 row)
postgres=# select boo.x;
┌─────┐
│ x │
╞═════╡
│ 100 │
└─────┘
(1 row)
Please try it.
Regards
Pavel
Attachments:
[application/octet-stream] schema-variables-poc-180320-01-diff (174.0K, ../../CAFj8pRAHK-6fPiyBGtTCjm4EOXc5ixgjL5ji5933cV3sS58nrQ@mail.gmail.com/3-schema-variables-poc-180320-01-diff)
download
[application/octet-stream] schema_variables.out (8.6K, ../../CAFj8pRAHK-6fPiyBGtTCjm4EOXc5ixgjL5ji5933cV3sS58nrQ@mail.gmail.com/4-schema_variables.out)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 06:49 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 06:54 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 15:38 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 16:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-20 17:38 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-03-21 05:24 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-23 05:37 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-03-21 05:24 UTC (permalink / raw)
To: Pavel Luzanov <p.luzanov@postgrespro.ru>; +Cc: David G. Johnston <david.g.johnston@gmail.com>; Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2018-03-20 18:38 GMT+01:00 Pavel Stehule <pavel.stehule@gmail.com>:
> Hi
>
> I am sending new update. The code is less ugly, and the current
> functionality is +/- final for first stage. It should be good enough for
> playing and testing this concept.
>
> What is supported:
>
> 1. scalar, composite and array variables
> 2. composite can be defined on place or some composite type can be used
> 3. variable, or any field of variable, can have defined default value
> 4. variable is database object - the access rights are required
> 5. the values are stored in binary form with defined typmod
>
> An usage is very simple:
>
> postgres=# create variable foo as numeric default 0;
> CREATE VARIABLE
> postgres=# select foo;
> ┌─────┐
> │ foo │
> ╞═════╡
> │ 0 │
> └─────┘
> (1 row)
>
> postgres=# let foo = pi();
> LET
> postgres=# select foo;
> ┌──────────────────┐
> │ foo │
> ╞══════════════════╡
> │ 3.14159265358979 │
> └──────────────────┘
> (1 row)
>
> postgres=# create variable boo as (x numeric default 0, y numeric default
> 0);
> CREATE VARIABLE
> postgres=# let boo.x = 100;
> LET
> postgres=# select boo;
> ┌─────────┐
> │ boo │
> ╞═════════╡
> │ (100,0) │
> └─────────┘
> (1 row)
>
> postgres=# select boo.x;
> ┌─────┐
> │ x │
> ╞═════╡
> │ 100 │
> └─────┘
> (1 row)
>
> Please try it.
>
small fix - support for SQL functions
>
> Regards
>
> Pavel
>
Attachments:
[application/octet-stream] schema-variables-poc-180321-01-diff (174.3K, ../../CAFj8pRBStff3KBB3m005D8+mQc=3tFATB+D_ND9g_mARdO=aXA@mail.gmail.com/3-schema-variables-poc-180321-01-diff)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` Re: [HACKERS] proposal: schema variables David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 06:49 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 06:54 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 15:38 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 16:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-20 17:38 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-03-21 05:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-03-23 05:37 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-03-23 05:37 UTC (permalink / raw)
To: Pavel Luzanov <p.luzanov@postgrespro.ru>; +Cc: David G. Johnston <david.g.johnston@gmail.com>; Pavel Golub <pavel@gf.microolap.com>; PostgreSQL Hackers <pgsql-hackers@postgresql.org>
2018-03-21 6:24 GMT+01:00 Pavel Stehule <pavel.stehule@gmail.com>:
>
>
> 2018-03-20 18:38 GMT+01:00 Pavel Stehule <pavel.stehule@gmail.com>:
>
>> Hi
>>
>> I am sending new update. The code is less ugly, and the current
>> functionality is +/- final for first stage. It should be good enough for
>> playing and testing this concept.
>>
>> What is supported:
>>
>> 1. scalar, composite and array variables
>> 2. composite can be defined on place or some composite type can be used
>> 3. variable, or any field of variable, can have defined default value
>> 4. variable is database object - the access rights are required
>> 5. the values are stored in binary form with defined typmod
>>
>> An usage is very simple:
>>
>> postgres=# create variable foo as numeric default 0;
>> CREATE VARIABLE
>> postgres=# select foo;
>> ┌─────┐
>> │ foo │
>> ╞═════╡
>> │ 0 │
>> └─────┘
>> (1 row)
>>
>> postgres=# let foo = pi();
>> LET
>> postgres=# select foo;
>> ┌──────────────────┐
>> │ foo │
>> ╞══════════════════╡
>> │ 3.14159265358979 │
>> └──────────────────┘
>> (1 row)
>>
>> postgres=# create variable boo as (x numeric default 0, y numeric default
>> 0);
>> CREATE VARIABLE
>> postgres=# let boo.x = 100;
>> LET
>> postgres=# select boo;
>> ┌─────────┐
>> │ boo │
>> ╞═════════╡
>> │ (100,0) │
>> └─────────┘
>> (1 row)
>>
>> postgres=# select boo.x;
>> ┌─────┐
>> │ x │
>> ╞═════╡
>> │ 100 │
>> └─────┘
>> (1 row)
>>
>> Please try it.
>>
>
> small fix - support for SQL functions
>
>
the patch is in commit fest list https://commitfest.postgresql.org/18/1608/
Regards
Pavel
>
>> Regards
>>
>> Pavel
>>
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-06-27 10:21 ` Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Gilles Darold @ 2018-06-27 10:21 UTC (permalink / raw)
To: pgsql-hackers@lists.postgresql.org, Pavel Stehule <pavel.stehule@gmail.com>
Hi,
I'm reviewing the patch as it was flagged in the current commit fest.
Here are my feedback:
- The patch need to be rebased due to changes in file
src/sgml/catalogs.sgml
- Some compilation warning must be fixed:
analyze.c: In function ‘transformLetStmt’:
analyze.c:1568:17: warning: variable ‘rte’ set but not used
[-Wunused-but-set-variable]
RangeTblEntry *rte;
^~~
tab-complete.c:1268:21: warning: initialization from incompatible
pointer type [-Wincompatible-pointer-types]
{"VARIABLE", NULL, &Query_for_list_of_variables},
In the last warning a NULL is missing, should be written:
{"VARIABLE", NULL, NULL, &Query_for_list_of_variables},
- How about Peter's suggestion?:
"In DB2, the privileges for variables are named READ and WRITE.
That would make more sense to me than reusing the privilege names for
tables.
The patch use SELECT and UPDATE which make sense too for SELECT but
less for UPDATE.
- The implementation of "ALTER VARIABLE varname SET SCHEMA
schema_name;" is missing
- ALTER VARIABLE var1 OWNER TO gilles; ok but not documented and
missing in regression test
- ALTER VARIABLE var1 RENAME TO var2; ok but not documented and
missing in regression test
More generally I think that some comments must be rewritten, especially
those talking about a PoC. In documentation there is HTML comments that
can be removed.
Comment at end of file src/backend/commands/schemavar.c generate some
"indent with spaces" errors with git apply but perhaps the comment can
be entirely removed or undocumented details moved to the right place.
Otherwise all regression tests passed without issue and especially your
new regression tests about schema variables.
I have a patch rebased, let me known if you want me to post the new diff.
--
Gilles Darold
Consultant PostgreSQL
http://dalibo.com - http://dalibo.org
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
@ 2018-06-27 11:22 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-06-27 11:22 UTC (permalink / raw)
To: Gilles Darold <gilles.darold@dalibo.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
2018-06-27 12:21 GMT+02:00 Gilles Darold <gilles.darold@dalibo.com>:
> Hi,
>
> I'm reviewing the patch as it was flagged in the current commit fest. Here
> are my feedback:
>
> - The patch need to be rebased due to changes in file
> src/sgml/catalogs.sgml
>
> - Some compilation warning must be fixed:
>
> analyze.c: In function ‘transformLetStmt’:
> analyze.c:1568:17: warning: variable ‘rte’ set but not used
> [-Wunused-but-set-variable]
> RangeTblEntry *rte;
> ^~~
> tab-complete.c:1268:21: warning: initialization from incompatible pointer
> type [-Wincompatible-pointer-types]
> {"VARIABLE", NULL, &Query_for_list_of_variables},
>
> In the last warning a NULL is missing, should be written: {"VARIABLE",
> NULL, NULL, &Query_for_list_of_variables},
>
>
> - How about Peter's suggestion?:
> "In DB2, the privileges for variables are named READ and WRITE. That
> would make more sense to me than reusing the privilege names for tables.
>
The patch use SELECT and UPDATE which make sense too for SELECT but
> less for UPDATE.
>
> - The implementation of "ALTER VARIABLE varname SET SCHEMA schema_name;"
> is missing
>
> - ALTER VARIABLE var1 OWNER TO gilles; ok but not documented and missing
> in regression test
>
> - ALTER VARIABLE var1 RENAME TO var2; ok but not documented and missing
> in regression test
>
> More generally I think that some comments must be rewritten, especially
> those talking about a PoC. In documentation there is HTML comments that can
> be removed.
>
> Comment at end of file src/backend/commands/schemavar.c generate some
> "indent with spaces" errors with git apply but perhaps the comment can be
> entirely removed or undocumented details moved to the right place.
>
> Otherwise all regression tests passed without issue and especially your
> new regression tests about schema variables.
>
> I have a patch rebased, let me known if you want me to post the new diff.
>
I plan significant refactoring of this patch for next commitfest. There was
anotherstrong Peter's and Robert comments
1. The schema variables should to have own system table
2. The composite schema variables should to use explicitly defined
composite type
3. The memory management is not nice - transactional drop table with
content is implemented ugly.
I hope, so I can start on these issues next month.
Thank you for review - I'll recheck ALTER commands.
Regards
Pavel
>
> --
> Gilles Darold
> Consultant PostgreSQLhttp://dalibo.com - http://dalibo.org
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-06-27 17:15 ` Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 17:17 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 2 replies; 433+ messages in thread
From: Gilles Darold @ 2018-06-27 17:15 UTC (permalink / raw)
To: pgsql-hackers@lists.postgresql.org, Pavel Stehule <pavel.stehule@gmail.com>
Le 27/06/2018 à 13:22, Pavel Stehule a écrit :
> Hi
>
> 2018-06-27 12:21 GMT+02:00 Gilles Darold <gilles.darold@dalibo.com
> <mailto:gilles.darold@dalibo.com>>:
>
> Hi,
>
> I'm reviewing the patch as it was flagged in the current commit
> fest. Here are my feedback:
>
> - The patch need to be rebased due to changes in file
> src/sgml/catalogs.sgml
>
> - Some compilation warning must be fixed:
>
> analyze.c: In function ‘transformLetStmt’:
> analyze.c:1568:17: warning: variable ‘rte’ set but not used
> [-Wunused-but-set-variable]
> RangeTblEntry *rte;
> ^~~
> tab-complete.c:1268:21: warning: initialization from
> incompatible pointer type [-Wincompatible-pointer-types]
> {"VARIABLE", NULL, &Query_for_list_of_variables},
>
> In the last warning a NULL is missing, should be written:
> {"VARIABLE", NULL, NULL, &Query_for_list_of_variables},
>
>
> - How about Peter's suggestion?:
> "In DB2, the privileges for variables are named READ and
> WRITE. That would make more sense to me than reusing the privilege
> names for tables.
>
> The patch use SELECT and UPDATE which make sense too for
> SELECT but less for UPDATE.
>
> - The implementation of "ALTER VARIABLE varname SET SCHEMA
> schema_name;" is missing
>
> - ALTER VARIABLE var1 OWNER TO gilles; ok but not documented and
> missing in regression test
>
> - ALTER VARIABLE var1 RENAME TO var2; ok but not documented and
> missing in regression test
>
> More generally I think that some comments must be rewritten,
> especially those talking about a PoC. In documentation there is
> HTML comments that can be removed.
>
> Comment at end of file src/backend/commands/schemavar.c generate
> some "indent with spaces" errors with git apply but perhaps the
> comment can be entirely removed or undocumented details moved to
> the right place.
>
> Otherwise all regression tests passed without issue and especially
> your new regression tests about schema variables.
>
> I have a patch rebased, let me known if you want me to post the
> new diff.
>
>
> I plan significant refactoring of this patch for next commitfest.
> There was anotherstrong Peter's and Robert comments
>
> 1. The schema variables should to have own system table
> 2. The composite schema variables should to use explicitly defined
> composite type
> 3. The memory management is not nice - transactional drop table with
> content is implemented ugly.
>
> I hope, so I can start on these issues next month.
>
> Thank you for review - I'll recheck ALTER commands.
>
>
> Otherwise all regression tests passed without issue and especially
> your new regression tests about schema variables.
>
> I have a patch rebased, let me known if you want me to post the
> new diff.
>
>
> I plan significant refactoring of this patch for next commitfest.
> There was anotherstrong Peter's and Robert c
> Regards
Ok Pavel, I've changed the status to "Waiting for authors" so that no
one will make an other review until you send a new patch.
--
Gilles Darold
Consultant PostgreSQL
http://dalibo.com - http://dalibo.org
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
@ 2018-06-27 17:17 ` Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-06-27 17:17 UTC (permalink / raw)
To: Gilles Darold <gilles.darold@dalibo.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
2018-06-27 19:15 GMT+02:00 Gilles Darold <gilles.darold@dalibo.com>:
> Le 27/06/2018 à 13:22, Pavel Stehule a écrit :
>
> Hi
>
> 2018-06-27 12:21 GMT+02:00 Gilles Darold <gilles.darold@dalibo.com>:
>
>> Hi,
>>
>> I'm reviewing the patch as it was flagged in the current commit fest.
>> Here are my feedback:
>>
>> - The patch need to be rebased due to changes in file
>> src/sgml/catalogs.sgml
>>
>> - Some compilation warning must be fixed:
>>
>> analyze.c: In function ‘transformLetStmt’:
>> analyze.c:1568:17: warning: variable ‘rte’ set but not used
>> [-Wunused-but-set-variable]
>> RangeTblEntry *rte;
>> ^~~
>> tab-complete.c:1268:21: warning: initialization from incompatible pointer
>> type [-Wincompatible-pointer-types]
>> {"VARIABLE", NULL, &Query_for_list_of_variables},
>>
>> In the last warning a NULL is missing, should be written: {"VARIABLE",
>> NULL, NULL, &Query_for_list_of_variables},
>>
>>
>> - How about Peter's suggestion?:
>> "In DB2, the privileges for variables are named READ and WRITE. That
>> would make more sense to me than reusing the privilege names for tables.
>>
> The patch use SELECT and UPDATE which make sense too for SELECT but
>> less for UPDATE.
>>
>> - The implementation of "ALTER VARIABLE varname SET SCHEMA schema_name;"
>> is missing
>>
>> - ALTER VARIABLE var1 OWNER TO gilles; ok but not documented and missing
>> in regression test
>>
>> - ALTER VARIABLE var1 RENAME TO var2; ok but not documented and missing
>> in regression test
>>
>> More generally I think that some comments must be rewritten, especially
>> those talking about a PoC. In documentation there is HTML comments that can
>> be removed.
>>
>> Comment at end of file src/backend/commands/schemavar.c generate some
>> "indent with spaces" errors with git apply but perhaps the comment can be
>> entirely removed or undocumented details moved to the right place.
>>
>> Otherwise all regression tests passed without issue and especially your
>> new regression tests about schema variables.
>>
>> I have a patch rebased, let me known if you want me to post the new diff.
>>
>
> I plan significant refactoring of this patch for next commitfest. There
> was anotherstrong Peter's and Robert comments
>
> 1. The schema variables should to have own system table
> 2. The composite schema variables should to use explicitly defined
> composite type
> 3. The memory management is not nice - transactional drop table with
> content is implemented ugly.
>
> I hope, so I can start on these issues next month.
>
> Thank you for review - I'll recheck ALTER commands.
>
>>
>> Otherwise all regression tests passed without issue and especially your
>> new regression tests about schema variables.
>>
>> I have a patch rebased, let me known if you want me to post the new diff.
>>
>
> I plan significant refactoring of this patch for next commitfest. There
> was anotherstrong Peter's and Robert c
> Regards
>
>
> Ok Pavel, I've changed the status to "Waiting for authors" so that no one
> will make an other review until you send a new patch.
>
sure
Thank you
Pavel
>
> --
> Gilles Darold
> Consultant PostgreSQLhttp://dalibo.com - http://dalibo.org
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
@ 2018-08-08 20:29 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-08 20:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-08-08 20:29 UTC (permalink / raw)
To: Gilles Darold <gilles.darold@dalibo.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
2018-06-27 19:15 GMT+02:00 Gilles Darold <gilles.darold@dalibo.com>:
> Le 27/06/2018 à 13:22, Pavel Stehule a écrit :
>
> Hi
>
> 2018-06-27 12:21 GMT+02:00 Gilles Darold <gilles.darold@dalibo.com>:
>
>> Hi,
>>
>> I'm reviewing the patch as it was flagged in the current commit fest.
>> Here are my feedback:
>>
>> - The patch need to be rebased due to changes in file
>> src/sgml/catalogs.sgml
>>
>> - Some compilation warning must be fixed:
>>
>> analyze.c: In function ‘transformLetStmt’:
>> analyze.c:1568:17: warning: variable ‘rte’ set but not used
>> [-Wunused-but-set-variable]
>> RangeTblEntry *rte;
>> ^~~
>> tab-complete.c:1268:21: warning: initialization from incompatible pointer
>> type [-Wincompatible-pointer-types]
>> {"VARIABLE", NULL, &Query_for_list_of_variables},
>>
>> In the last warning a NULL is missing, should be written: {"VARIABLE",
>> NULL, NULL, &Query_for_list_of_variables},
>>
>>
>> - How about Peter's suggestion?:
>> "In DB2, the privileges for variables are named READ and WRITE. That
>> would make more sense to me than reusing the privilege names for tables.
>>
> The patch use SELECT and UPDATE which make sense too for SELECT but
>> less for UPDATE.
>>
>> - The implementation of "ALTER VARIABLE varname SET SCHEMA schema_name;"
>> is missing
>>
>> - ALTER VARIABLE var1 OWNER TO gilles; ok but not documented and missing
>> in regression test
>>
>> - ALTER VARIABLE var1 RENAME TO var2; ok but not documented and missing
>> in regression test
>>
>> More generally I think that some comments must be rewritten, especially
>> those talking about a PoC. In documentation there is HTML comments that can
>> be removed.
>>
>> Comment at end of file src/backend/commands/schemavar.c generate some
>> "indent with spaces" errors with git apply but perhaps the comment can be
>> entirely removed or undocumented details moved to the right place.
>>
>> Otherwise all regression tests passed without issue and especially your
>> new regression tests about schema variables.
>>
>> I have a patch rebased, let me known if you want me to post the new diff.
>>
>
> I plan significant refactoring of this patch for next commitfest. There
> was anotherstrong Peter's and Robert comments
>
> 1. The schema variables should to have own system table
> 2. The composite schema variables should to use explicitly defined
> composite type
> 3. The memory management is not nice - transactional drop table with
> content is implemented ugly.
>
> I hope, so I can start on these issues next month.
>
> Thank you for review - I'll recheck ALTER commands.
>
>>
>> Otherwise all regression tests passed without issue and especially your
>> new regression tests about schema variables.
>>
>> I have a patch rebased, let me known if you want me to post the new diff.
>>
>
> I plan significant refactoring of this patch for next commitfest. There
> was anotherstrong Peter's and Robert c
> Regards
>
>
> Ok Pavel, I've changed the status to "Waiting for authors" so that no one
> will make an other review until you send a new patch.
>
I am sending a new update of this feature. The functionality is +/- same
like previous patch, but a implementation is based on own catalog table.
I removed functions for manipulation with schema variables. These functions
can be added later simply. Now If we hold these functions, then we should
to solve often collision inside pg_proc.
Changes:
* own catalog - pg_variable
* the rights are renamed - READ|WRITE
* the code is cleaner
Regards
Pavel
>
> --
> Gilles Darold
> Consultant PostgreSQLhttp://dalibo.com - http://dalibo.org
>
>
Attachments:
[text/x-patch] schema-variables-180808-01.patch (190.5K, ../../CAFj8pRATM44F1ugXxTn6aofxOa=3DZbqOJ17=EVyG+CEzsRQvw@mail.gmail.com/3-schema-variables-180808-01.patch)
download | inline diff:
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3bb48d4ccf..0a7a932ef5 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -359,6 +359,11 @@
<entry><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link></entry>
<entry>mappings of users to foreign servers</entry>
</row>
+
+ <row>
+ <entry><link linkend="catalog-pg-variable"><structname>pg_variable</structname></link></entry>
+ <entry>schema variables</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -11255,7 +11260,6 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
-
<sect1 id="view-pg-views">
<title><structname>pg_views</structname></title>
@@ -11311,4 +11315,104 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
+ <sect1 id="catalog-pg-variable">
+ <title><structname>pg_variable</structname></title>
+
+ <indexterm zone="catalog-pg-variable">
+ <primary>pg_variable</primary>
+ </indexterm>
+
+ <para>
+ The table <structname>pg_variable</structname> holds metadata
+ of schema variables.
+ </para>
+
+ <table>
+ <title><structname>pg_views</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><structfield>oid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry></entry>
+ <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Name of the schema variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the namespace that contains this variable
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartype</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the data type of this variable.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartypmod</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ <structfield>vartypmod</structfield> records type-specific data
+ supplied at table creation time (for example, the maximum
+ length of a <type>varchar</type> column). It is passed to
+ type-specific input functions and length coercion functions.
+ The value will generally be -1 for types that do not need <structfield>vartypmod</structfield>.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>varowner</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>Owner of the variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>vardefexpr</structfield></entry>
+ <entry><type>pg_node_tree</type></entry>
+ <entry></entry>
+ <entry>The internal representation of the variable default value</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>
+ Access privileges; see
+ <xref linkend="sql-grant"/> and
+ <xref linkend="sql-revoke"/>
+ for details
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..f5aaf60233 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -99,6 +99,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createType SYSTEM "create_type.sgml">
<!ENTITY createUser SYSTEM "create_user.sgml">
<!ENTITY createUserMapping SYSTEM "create_user_mapping.sgml">
+<!ENTITY createVariable SYSTEM "create_variable.sgml">
<!ENTITY createView SYSTEM "create_view.sgml">
<!ENTITY deallocate SYSTEM "deallocate.sgml">
<!ENTITY declare SYSTEM "declare.sgml">
@@ -148,6 +149,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropUser SYSTEM "drop_user.sgml">
<!ENTITY dropUserMapping SYSTEM "drop_user_mapping.sgml">
<!ENTITY dropView SYSTEM "drop_view.sgml">
+<!ENTITY dropVariable SYSTEM "drop_variable.sgml">
<!ENTITY end SYSTEM "end.sgml">
<!ENTITY execute SYSTEM "execute.sgml">
<!ENTITY explain SYSTEM "explain.sgml">
@@ -155,6 +157,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY grant SYSTEM "grant.sgml">
<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
<!ENTITY insert SYSTEM "insert.sgml">
+<!ENTITY let SYSTEM "let.sgml">
<!ENTITY listen SYSTEM "listen.sgml">
<!ENTITY load SYSTEM "load.sgml">
<!ENTITY lock SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
new file mode 100644
index 0000000000..c8070051f5
--- /dev/null
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -0,0 +1,133 @@
+<!--
+doc/src/sgml/ref/create_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-createvariable">
+ <indexterm zone="sql-createvariable">
+ <primary>CREATE VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE VARIABLE</refname>
+ <refpurpose>define a new permissioned typed schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ]
+</synopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> creates a new schema variable.
+ These variables are scalar typed, non-transactional, and, like relations,
+ exist within a schema with access controlled via
+ <command>GRANT</command> and <command>REVOKE</command>.
+ </para>
+
+ <para>
+ The value of a schema variable is session-local. Retrieving
+ a variable's value will return NULL unless its value has been set
+ to something else in the current session.
+ </para>
+
+ <para>
+ Retrieval is done via the <function>get_schema_variable</function>dunxrion or the SQL
+ command <command>SELECT</command>. Setting of values is done via the
+ <function>set_schema_variable</function> function or the SQL command
+ <command>LET</command>.
+ Notably, while schema variables are in many ways a kind of table you cannot use
+ <command>UPDATE</command> on them.
+ </para>
+
+ <para>
+ For purposes of name uniqueness relation-like objects (e.g., tables, indexes)
+ within the same schema are considered. i.e., you cannot give a table and a
+ schema variable the same name. This is a consequence of them being treated
+ like relations for purposes of <command>SELECT</command>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF NOT EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the name already exists. A notice is issued in this case.
+ Note that type of the variable is not considered, nor could it be since the namespace
+ searched contains non-variable objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the data type of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Use <command>DROP VARIABLE</command> to remove a variable.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Create an integer variable <literal>var1</literal>:
+<programlisting>
+CREATE VARIABLE var1 AS integer;
+SELECT var1;
+</programlisting>
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> is a PostgreSQL feature.
+ <!-- The choice of wording here seems to be left to personal preference... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index 6b909b7232..d83ad811fd 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
+DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES }
</synopsis>
</refsynopsisdiv>
@@ -75,6 +75,17 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>VARIABLES</literal></term>
+ <listitem>
+ <para>
+ Resets the value of all schema variables. When variables
+ will be used later, then will be initialized again to
+ NULL or default value.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
new file mode 100644
index 0000000000..06130fd510
--- /dev/null
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -0,0 +1,92 @@
+<!--
+doc/src/sgml/ref/drop_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropvariable">
+ <indexterm zone="sql-dropvariable">
+ <primary>DROP VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP VARIABLE</refname>
+ <refpurpose>remove a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP VARIABLE</command> removes a schema variable.
+ A variable can only be dropped by its owner or a superuser.
+ <!-- this would suggest that we need an alter variable owner to command -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the variable does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To remove the schema variable <literal>var1</literal>:
+
+<programlisting>
+DROP VARIABLE var1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ <!-- create variable is a "PostgreSQL feature",
+ this is a "proprietary PostgreSQL command" ... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index ff64c7a3ba..a83920a7a1 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -79,6 +79,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE <replaceable>type_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
[ GROUP ] <replaceable class="parameter">role_name</replaceable>
@@ -167,6 +171,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
foreign servers,
large objects,
schemas,
+ schema variable
or tablespaces.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
@@ -385,6 +390,24 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>READ</literal></term>
+ <listitem>
+ <para>
+ Allows to read a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WRITE</literal></term>
+ <listitem>
+ <para>
+ Allows to set a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL PRIVILEGES</literal></term>
<listitem>
@@ -550,6 +573,8 @@ rolename=xxxx -- privileges granted to a role
C -- CREATE
c -- CONNECT
T -- TEMPORARY
+ S -- READ
+ w -- WRITE
arwdDxt -- ALL PRIVILEGES (for tables, varies for other objects)
* -- grant option for preceding privilege
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
new file mode 100644
index 0000000000..e8bf3f6dd4
--- /dev/null
+++ b/doc/src/sgml/ref/let.sgml
@@ -0,0 +1,90 @@
+<!--
+doc/src/sgml/ref/let.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-let">
+ <indexterm zone="sql-let">
+ <primary>LET</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>LET</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>LET</refname>
+ <refpurpose>change a schema variable's value</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable class="parameter">sql_expression</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ The <command>LET</command> command updates the specified schema variable' value.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>schema_variable</literal></term>
+ <listitem>
+ <para>
+ The name of schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sql expression</literal></term>
+ <listitem>
+ <para>
+ An SQL expression, the result is cast to the schema variable's type.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Example:
+<programlisting>
+CREATE VARIABLE myvar AS integer;
+LET myvar = 10;
+LET myvar = (SELECT sum(val) FROM tab);
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <!-- this feels like it needs to be more specific,
+ but I don't know enough to make it so -->
+ <literal>LET</literal> extends syntax defined in the SQL
+ standard. The standard knows <literal>SET</literal> command,
+ that is used for different purpouse in PostgreSQL.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 5317f8ccba..8435e05957 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -108,6 +108,12 @@ REVOKE [ GRANT OPTION FOR ]
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_name</replaceable> [, ...]
[ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { READ | WRITE } [, ...] | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..afcc69432d 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -127,6 +127,7 @@
&createType;
&createUser;
&createUserMapping;
+ &createVariable;
&createView;
&deallocate;
&declare;
@@ -175,6 +176,7 @@
&dropType;
&dropUser;
&dropUserMapping;
+ &dropVariable;
&dropView;
&end;
&execute;
@@ -183,6 +185,7 @@
&grant;
&importForeignSchema;
&insert;
+ &let;
&listen;
&load;
&lock;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 0865240f11..1f7c4d1223 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -19,7 +19,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_publication.o pg_range.o \
pg_db_role_setting.o pg_shdepend.o pg_subscription.o pg_type.o \
- storage.o toasting.o
+ pg_variable.o storage.o toasting.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
@@ -46,7 +46,7 @@ CATALOG_HEADERS := \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
- pg_subscription_rel.h
+ pg_subscription_rel.h pg_variable.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 578e4c6592..86917e15a8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -57,6 +57,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_transform.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
@@ -112,6 +113,7 @@ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
+static void ExecGrant_Variable(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
@@ -284,6 +286,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -507,6 +512,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -609,6 +618,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_VARIABLE:
+ ExecGrant_Variable(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -768,6 +780,16 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_VARIABLE:
+ foreach(cell, objnames)
+ {
+ RangeVar *varvar = (RangeVar *) lfirst(cell);
+ Oid relOid;
+
+ relOid = lookup_variable(varvar->schemaname, varvar->relname, false);
+ objects = lappend_oid(objects, relOid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -855,6 +877,31 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
heap_close(rel, AccessShareLock);
}
break;
+ case OBJECT_VARIABLE:
+ {
+ ScanKeyData key;
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&key,
+ Anum_pg_variable_varnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(namespaceId));
+
+ rel = heap_open(VariableRelationId, AccessShareLock);
+ scan = heap_beginscan_catalog(rel, 1, &key);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ }
+ break;
+
default:
/* should not happen */
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
@@ -1018,6 +1065,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1215,6 +1266,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1441,6 +1498,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -3266,6 +3326,129 @@ ExecGrant_Type(InternalGrant *istmt)
heap_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Variable(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_VARIABLE;
+
+ relation = heap_open(VariableRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid varId = lfirst_oid(cell);
+ Form_pg_variable pg_variable_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_variable];
+ bool nulls[Natts_pg_variable];
+ bool replaces[Natts_pg_variable];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for schema variables %u", varId);
+
+ pg_variable_tuple = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_variable_tuple->varowner;
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple, Anum_pg_variable_varacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_VARIABLE, ownerId);
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ oldmembers = NULL;
+ }
+ else
+ {
+ old_acl = DatumGetAclPCopy(aclDatum);
+ /* Get the roles mentioned in the existing ACL */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+ }
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ varId, grantorId, OBJECT_VARIABLE,
+ NameStr(pg_variable_tuple->varname),
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ */
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_variable_varacl - 1] = true;
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(varId, VariableRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(VariableRelationId, varId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3298,6 +3481,10 @@ string_to_privilege(const char *privname)
return ACL_CONNECT;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
+ if (strcmp(privname, "read") == 0)
+ return ACL_READ;
+ if (strcmp(privname, "write") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized privilege type \"%s\"", privname)));
@@ -3333,6 +3520,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3456,6 +3647,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3566,6 +3760,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3710,6 +3907,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
+ case OBJECT_VARIABLE:
+ return pg_variable_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objtype: %d",
(int) objtype);
@@ -4499,6 +4698,67 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a variable.
+ */
+AclMode
+pg_variable_aclmask(Oid var_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_variable varForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the type's tuple from pg_type
+ */
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable with OID %u does not exist",
+ var_oid)));
+ varForm = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Now get the type's owner and ACL from the tuple
+ */
+ ownerId = varForm->varowner;
+
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple,
+ Anum_pg_variable_varacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_VARIABLE, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+
/*
* Exported routine for checking a user's access privileges to a column
*
@@ -4744,6 +5004,18 @@ pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a variable
+ */
+AclResult
+pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
+{
+ if (pg_variable_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Ownership check for a relation (specified by OID).
*/
@@ -5361,6 +5633,33 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a schema variables (specified by OID).
+ */
+bool
+pg_variable_ownercheck(Oid db_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(db_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("variable with OID %u does not exist", db_oid)));
+
+ ownerId = ((Form_pg_variable) GETSTRUCT(tuple))->varowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+
/*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
@@ -5486,6 +5785,10 @@ get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
+ case OBJECT_VARIABLE:
+ defaclobjtype = DEFACLOBJ_VARIABLE;
+ break;
+
default:
return NULL;
}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 4f1d365357..782ddb1655 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -59,6 +59,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -67,6 +68,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
@@ -1280,6 +1282,10 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_VARIABLE:
+ RemoveVariableById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2537,6 +2543,9 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case VariableRelationId:
+ return OCLASS_VARIABLE;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 0f67a122ed..81aaf454a8 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -39,6 +39,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
@@ -755,6 +756,71 @@ RelationIsVisible(Oid relid)
return visible;
}
+/*
+ * VariableIsVisible
+ * Determine whether a variable (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified variable name".
+ */
+bool
+VariableIsVisible(Oid varid)
+{
+ HeapTuple vartup;
+ Form_pg_variable varform;
+ Oid varnamespace;
+ bool visible;
+
+ vartup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(vartup))
+ elog(ERROR, "cache lookup failed for schema variable %u", varid);
+ varform = (Form_pg_variable) GETSTRUCT(vartup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ varnamespace = varform->varnamespace;
+ if (varnamespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, varnamespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another relation of the same name earlier in the path. So
+ * we must do a slow check for conflicting relations.
+ */
+ char *varname = NameStr(varform->varname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == varnamespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (OidIsValid(get_varname_varid(varname, namespaceId)))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(vartup);
+
+ return visible;
+}
+
+
/*
* TypenameGetTypid
@@ -2776,6 +2842,202 @@ TSConfigIsVisible(Oid cfgid)
return visible;
}
+/*
+ * When we know a variable name, then we can find variable simply
+ */
+Oid
+lookup_variable(const char *nspname, const char *varname, bool missing_ok)
+{
+ Oid namespaceId;
+ Oid varoid = InvalidOid;
+ ListCell *l;
+
+ if (nspname)
+ {
+ namespaceId = LookupExplicitNamespace(nspname, missing_ok);
+ if (!OidIsValid(namespaceId))
+ return InvalidOid;
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+
+ if (OidIsValid(varoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(varoid) && !missing_ok)
+ {
+ if (nspname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\".\"%s\" does not exist",
+ nspname, varname)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\" does not exist",
+ varname)));
+ }
+
+ return varoid;
+}
+
+List *
+NamesFromList(List *names)
+{
+ ListCell *l;
+ List *result = NIL;
+
+ foreach(l, names)
+ {
+ Node *n = lfirst(l);
+
+ if (IsA(n, String))
+ {
+ result = lappend(result, n);
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * identify_variable
+ *
+ * Returns oid of not ambigonuous variable specified by qualified path
+ * or InvalidOid. When the path is ambigonuous, then not_uniq flag is
+ * is true.
+ */
+Oid
+identify_variable(List *names, char **attrname, bool *not_uniq)
+{
+ char *a = NULL;
+ char *b = NULL;
+ char *c = NULL;
+ char *d = NULL;
+ Oid varoid_without_attr;
+ Oid varoid_with_attr;
+
+ *not_uniq = false;
+
+ switch (list_length(names))
+ {
+ case 1:
+ a = strVal(linitial(names));
+ return lookup_variable(NULL, a, true);
+
+ case 2:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+
+ /*
+ * a.b can mean "schema"."variable" or "variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(a, b, true);
+ varoid_with_attr = lookup_variable(NULL, a, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = b;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 3:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+
+ /*
+ * a.b.c can mean "catalog"."schema"."variable" or "schema"."variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(b, c, true);
+ varoid_with_attr = lookup_variable(a, b, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = c;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 4:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+ d = strVal(lfourth(names));
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ *attrname = d;
+ return lookup_variable(b, c, true);
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(names))));
+ break;
+ }
+}
/*
* DeconstructQualifiedName
@@ -4416,3 +4678,14 @@ pg_is_other_temp_schema(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(isOtherTempNamespace(oid));
}
+
+Datum
+pg_variable_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ if (!SearchSysCacheExists1(VARIABLEOID, ObjectIdGetDatum(oid)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_BOOL(VariableIsVisible(oid));
+}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7db942dcba..cc3d415e61 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -58,6 +58,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -489,6 +490,18 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
OBJECT_STATISTIC_EXT,
true
+ },
+ {
+ VariableRelationId,
+ VariableObjectIndexId,
+ VARIABLEOID,
+ VARIABLENAMENSP,
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ Anum_pg_variable_varowner,
+ Anum_pg_variable_varacl,
+ OBJECT_VARIABLE,
+ true
}
};
@@ -714,6 +727,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_VARIABLE */
+ {
+ "schema variable", OBJECT_VARIABLE
}
};
@@ -739,6 +756,7 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
bool missing_ok);
static ObjectAddress get_object_address_type(ObjectType objtype,
TypeName *typename, bool missing_ok);
+static ObjectAddress get_object_address_variable(List *object, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object,
bool missing_ok);
static ObjectAddress get_object_address_opf_member(ObjectType objtype,
@@ -996,6 +1014,10 @@ get_object_address(ObjectType objtype, Node *object,
missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_VARIABLE:
+ address = get_object_address_variable(castNode(List, object), missing_ok);
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
@@ -1848,16 +1870,20 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type \"%c\"", objtype),
- errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
+ errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
DEFACLOBJ_RELATION,
DEFACLOBJ_SEQUENCE,
DEFACLOBJ_FUNCTION,
DEFACLOBJ_TYPE,
- DEFACLOBJ_NAMESPACE)));
+ DEFACLOBJ_NAMESPACE,
+ DEFACLOBJ_VARIABLE)));
}
/*
@@ -1942,6 +1968,24 @@ textarray_to_strvaluelist(ArrayType *arr)
return list;
}
+/*
+ * Find the ObjectAddress for a type or domain
+ */
+static ObjectAddress
+get_object_address_variable(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ char *nspname = NULL;
+ char *varname = NULL;
+
+ ObjectAddressSet(address, VariableRelationId, InvalidOid);
+
+ DeconstructQualifiedName(object, &nspname, &varname);
+ address.objectId = lookup_variable(nspname, varname, missing_ok);
+
+ return address;
+}
+
/*
* SQL-callable version of get_object_address
*/
@@ -2131,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_TABCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
+ case OBJECT_VARIABLE:
objnode = (Node *) name;
break;
case OBJECT_ACCESS_METHOD:
@@ -2415,6 +2460,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
break;
+ case OBJECT_VARIABLE:
+ if (!pg_variable_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
default:
elog(ERROR, "unrecognized object type: %d",
(int) objtype);
@@ -3157,6 +3207,32 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_VARIABLE:
+ {
+ char *nspname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ if (VariableIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ appendStringInfo(&buffer, _("schema variable %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(varform->varname)));
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_TSPARSER:
{
HeapTuple tup;
@@ -3422,6 +3498,16 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new schemas belonging to role %s"),
rolename);
break;
+ case DEFACLOBJ_VARIABLE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s"),
+ rolename);
+ break;
default:
/* shouldn't get here */
if (nspname)
@@ -4070,6 +4156,10 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_VARIABLE:
+ appendStringInfoString(&buffer, "schema variable");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4962,6 +5052,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on variables");
+ break;
}
if (objname)
@@ -5121,6 +5215,33 @@ getObjectIdentityParts(const ObjectAddress *object,
}
break;
+ case OCLASS_VARIABLE:
+ {
+ char *schema;
+ char *varname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ schema = get_namespace_name_or_temp(varform->varnamespace);
+ varname = NameStr(varform->varname);
+
+ appendStringInfo(&buffer, "%s",
+ quote_qualified_identifier(schema, varname));
+
+ if (objname)
+ *objname = list_make2(schema, varname);
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
diff --git a/src/backend/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c
new file mode 100644
index 0000000000..ff71f8bf6a
--- /dev/null
+++ b/src/backend/catalog/pg_variable.c
@@ -0,0 +1,305 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.c
+ * schema variables
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_variable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+
+#include "nodes/makefuncs.h"
+
+#include "storage/lmgr.h"
+
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * Returns name of schema variable. When variable is not on path,
+ * then the name is qualified.
+ */
+char *
+schema_variable_get_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+ char *nspname;
+ char *result;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ if (VariableIsVisible(varid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ result = quote_qualified_identifier(nspname, varname);
+
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/*
+ * Returns varname field of pg_variable
+ */
+char *
+get_schema_variable_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ ReleaseSysCache(tup);
+
+ return varname;
+}
+
+/*
+ * Returns type, typmod of schema variable
+ */
+void
+get_schema_variable_type_typmod(Oid varid, Oid *typid, int32 *typmod)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ *typid = varform->vartype;
+ *typmod = varform->vartypmod;
+
+ ReleaseSysCache(tup);
+
+ return;
+}
+
+/*
+ * Fetch all fields of schema variable from the syscache.
+ */
+Variable *
+GetVariable(Oid varid, bool missing_ok)
+{
+ HeapTuple tup;
+ Variable *var;
+ Form_pg_variable varform;
+ Datum aclDatum;
+ Datum defexprDatum;
+ bool isnull;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (missing_ok)
+ return NULL;
+
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+ }
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ var = (Variable *) palloc(sizeof(Variable));
+ var->oid = varid;
+ var->name = pstrdup(NameStr(varform->varname));
+ var->namespace = varform->varnamespace;
+ var->typid = varform->vartype;
+ var->typmod = varform->vartypmod;
+ var->owner = varform->varowner;
+
+ /* Get defexpr */
+ defexprDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_vardefexpr,
+ &isnull);
+
+ if (!isnull)
+ var->defexpr = stringToNode(TextDatumGetCString(defexprDatum));
+ else
+ var->defexpr = NULL;
+
+ /* Get varacl */
+ aclDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_varacl,
+ &isnull);
+ if (!isnull)
+ var->acl = DatumGetAclPCopy(aclDatum);
+ else
+ var->acl = NULL;
+
+ ReleaseSysCache(tup);
+
+ return var;
+}
+
+ObjectAddress
+VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Node *varDefexpr,
+ bool if_not_exists)
+{
+ Acl *varacl;
+ NameData varname;
+ bool nulls[Natts_pg_variable];
+ Datum values[Natts_pg_variable];
+ Relation rel;
+ HeapTuple tup,
+ oldtup;
+ TupleDesc tupdesc;
+ ObjectAddress myself,
+ referenced;
+ Oid retval;
+ int i;
+
+ for (i = 0; i < Natts_pg_variable; i++)
+ {
+ nulls[i] = false;
+ values[i] = (Datum) 0;
+ }
+
+ namestrcpy(&varname, varName);
+ values[Anum_pg_variable_varname - 1] = NameGetDatum(&varname);
+ values[Anum_pg_variable_varnamespace - 1] = ObjectIdGetDatum(varNamespace);
+ values[Anum_pg_variable_vartype - 1] = ObjectIdGetDatum(varType);
+ values[Anum_pg_variable_vartypmod - 1] = Int32GetDatum(varTypmod);
+ values[Anum_pg_variable_varowner - 1] = ObjectIdGetDatum(varOwner);
+ /* proacl will be determined later */
+
+ if (varDefexpr)
+ values[Anum_pg_variable_vardefexpr - 1] = CStringGetTextDatum(nodeToString(varDefexpr));
+ else
+ nulls[Anum_pg_variable_vardefexpr - 1] = true;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+ tupdesc = RelationGetDescr(rel);
+
+ oldtup = SearchSysCache2(VARIABLENAMENSP,
+ PointerGetDatum(varName),
+ ObjectIdGetDatum(varNamespace));
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ if (if_not_exists)
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists, skipping",
+ varName)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists",
+ varName)));
+
+ heap_freetuple(oldtup);
+ heap_close(rel, RowExclusiveLock);
+
+ return InvalidObjectAddress;
+ }
+
+ varacl = get_user_default_acl(OBJECT_VARIABLE, varOwner,
+ varNamespace);
+
+ if (varacl != NULL)
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(varacl);
+ else
+ nulls[Anum_pg_variable_varacl - 1] = true;
+
+ tup = heap_form_tuple(tupdesc, values, nulls);
+ CatalogTupleInsert(rel, tup);
+
+ retval = HeapTupleGetOid(tup);
+
+ myself.classId = VariableRelationId;
+ myself.objectId = retval;
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = varNamespace;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on used type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = varType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on any roles mentioned in ACL */
+ if (varacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(varacl, &newmembers);
+ updateAclDependencies(VariableRelationId, retval, 0,
+ varOwner,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_freetuple(tup);
+
+ /* Post creation hook for new function */
+ InvokeObjectPostCreateHook(VariableRelationId, retval, 0);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..2cb5b1172d 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,7 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
- schemacmds.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
+ schemacmds.o seclabel.o sequence.o schemavariable.o statscmds.o subscriptioncmds.o \
tablecmds.o tablespace.o trigger.o tsearchcmds.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index eff325cc7d..a9d5e5e0ad 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -387,6 +387,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_VARIABLE:
{
ObjectAddress address;
Relation catalog;
@@ -504,6 +505,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
@@ -594,6 +596,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_TSDICT:
case OCLASS_TSTEMPLATE:
case OCLASS_TSCONFIG:
+ case OCLASS_VARIABLE:
{
Relation catalog;
@@ -852,6 +855,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_TABLESPACE:
case OBJECT_TSDICTIONARY:
case OBJECT_TSCONFIGURATION:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 01a999c2ac..fec2495e93 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -19,6 +19,7 @@
#include "commands/discard.h"
#include "commands/prepare.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "utils/guc.h"
#include "utils/portal.h"
@@ -48,6 +49,10 @@ DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
ResetTempTableNamespace();
break;
+ case DISCARD_VARIABLES:
+ ResetSchemaVariableCache();
+ break;
+
default:
elog(ERROR, "unrecognized DISCARD target: %d", stmt->target);
}
@@ -75,4 +80,5 @@ DiscardAll(bool isTopLevel)
ResetPlanCache();
ResetTempTableNamespace();
ResetSequenceCaches();
+ ResetSchemaVariableCache();
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e..426df246b3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -126,6 +126,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"TEXT SEARCH TEMPLATE", true},
{"TYPE", true},
{"USER MAPPING", true},
+ {"VARIABLE", true},
{"VIEW", true},
{NULL, false}
};
@@ -297,7 +298,8 @@ check_ddl_tag(const char *tag)
pg_strcasecmp(tag, "REVOKE") == 0 ||
pg_strcasecmp(tag, "DROP OWNED") == 0 ||
pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
+ pg_strcasecmp(tag, "SECURITY LABEL") == 0 ||
+ pg_strcasecmp(tag, "CREATE VARIABLE") == 0)
return EVENT_TRIGGER_COMMAND_TAG_OK;
/*
@@ -1146,6 +1148,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -1209,6 +1212,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
return true;
/*
@@ -2244,6 +2248,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2326,6 +2332,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b1556a..eb8c08baf3 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_PLAN_UTILITY:
/* OK */
break;
default:
diff --git a/src/backend/commands/schemavariable.c b/src/backend/commands/schemavariable.c
new file mode 100644
index 0000000000..208d0d20c4
--- /dev/null
+++ b/src/backend/commands/schemavariable.c
@@ -0,0 +1,470 @@
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
+#include "executor/executor.h"
+#include "executor/svariableReceiver.h"
+#include "nodes/execnodes.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/inval.h"
+#include "utils/memutils.h"
+#include "utils/lsyscache.h"
+#include "utils/snapmgr.h"
+#include "utils/syscache.h"
+
+/*
+ * The content of variables is not transactional. Due this fact the
+ * implementation of DROP can be simple, because although DROP VARIABLE
+ * can be reverted, the content of variable can be lost. In this example,
+ * DROP VARIABLE is same like reset variable.
+ */
+
+typedef struct SchemaVariableData
+{
+ Oid varid; /* pg_variable OID of this sequence (hash key) */
+ Oid typid; /* OID of the data type */
+ int32 typmod;
+ int16 typlen;
+ bool typbyval;
+ bool isnull;
+ bool freeval;
+ Datum value;
+ bool is_rowtype; /* true when variable is composite */
+ bool is_valid; /* true when variable was successfuly initialized */
+} SchemaVariableData;
+
+typedef SchemaVariableData *SchemaVariable;
+
+static HTAB *schemavarhashtab = NULL; /* hash table for session variables */
+static MemoryContext SchemaVariableMemoryContext = NULL;
+
+static bool first_time = true;
+static void create_schemavar_hashtable(void);
+static bool clean_cache_req = false;
+
+static void clean_cache(void);
+static void force_clean_cache(XactEvent event, void *arg);
+
+
+/*
+ * Save info about ncessity to clean hash table, because some
+ * schema variable was dropped. Don't do here more, recheck
+ * needs to be in transaction state.
+ */
+static void
+InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ if (cacheid != VARIABLEOID)
+ return;
+
+ clean_cache_req = true;
+}
+
+static void
+force_clean_cache(XactEvent event, void *arg)
+{
+ /*
+ * should continue only in transaction time, when
+ * syscache is available.
+ */
+ if (clean_cache_req && IsTransactionState())
+ {
+ clean_cache();
+ clean_cache_req = false;
+ }
+}
+
+static void
+clean_cache(void)
+{
+ HASH_SEQ_STATUS status;
+ SchemaVariable var;
+
+ if (!schemavarhashtab)
+ return;
+
+ hash_seq_init(&status, schemavarhashtab);
+
+ /*
+ * Every valid variable have to have entry in system
+ * catalog. Removed if there is nothing.
+ */
+ while ((var = (SchemaVariable) hash_seq_search(&status)) != NULL)
+ {
+ HeapTuple tp = InvalidOid;
+
+ tp = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var->varid));
+ if (!HeapTupleIsValid(tp))
+ {
+ elog(DEBUG1, "variable %d is removed from cache", var->varid);
+
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (hash_search(schemavarhashtab,
+ (void *) &var->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(DEBUG1, "hash table corrupted");
+ }
+ else
+ ReleaseSysCache(tp);
+ }
+}
+
+char *
+VariableGetName(Variable *var)
+{
+ char *nspname;
+
+ if (VariableIsVisible(var->oid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(var->namespace);
+
+ return quote_qualified_identifier(nspname, var->name);
+}
+
+/*
+ * Create the hash table for storing schema variables
+ */
+static void
+create_schemavar_hashtable(void)
+{
+ HASHCTL ctl;
+
+ /* set callbacks */
+ if (first_time)
+ {
+ CacheRegisterSyscacheCallback(VARIABLEOID,
+ InvalidateSchemaVarCacheCallback,
+ (Datum) 0);
+
+ RegisterXactCallback(force_clean_cache, NULL);
+
+ first_time = false;
+ }
+
+ /* needs own long life memory context */
+ if (SchemaVariableMemoryContext == NULL)
+ {
+ SchemaVariableMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "schema variables",
+ ALLOCSET_START_SMALL_SIZES);
+ }
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(SchemaVariableData);
+ ctl.hcxt = SchemaVariableMemoryContext;
+
+ schemavarhashtab = hash_create("Schema variables", 64, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/*
+ * Fast drop complete content of schema variables
+ */
+void
+ResetSchemaVariableCache(void)
+{
+ if (schemavarhashtab)
+ {
+ hash_destroy(schemavarhashtab);
+ schemavarhashtab = NULL;
+ }
+
+ if (SchemaVariableMemoryContext != NULL)
+ {
+ MemoryContextReset(SchemaVariableMemoryContext);
+ }
+}
+
+/*
+ * Drop variable by OID
+ */
+void
+RemoveVariableById(Oid varid)
+{
+ Relation rel;
+ HeapTuple tup;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ CatalogTupleDelete(rel, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Creates new variable - entry in pg_catalog.pg_variable table
+ */
+ObjectAddress
+DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt)
+{
+ Oid namespaceid;
+ AclResult aclresult;
+ Oid typid;
+ int32 typmod;
+ Oid varowner = GetUserId();
+
+ Node *cooked_default = NULL;
+
+ namespaceid =
+ RangeVarGetAndCheckCreationNamespace(stmt->variable, NoLock, NULL);
+
+ typenameTypeIdAndMod(pstate, stmt->typeName, &typid, &typmod);
+
+ aclresult = pg_type_aclcheck(typid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error_type(aclresult, typid);
+
+ if (stmt->defexpr)
+ {
+ cooked_default = transformExpr(pstate, stmt->defexpr,
+ EXPR_KIND_VARIABLE_DEFAULT);
+
+ cooked_default = coerce_to_specific_type(pstate,
+ cooked_default, typid, "DEFAULT");
+ }
+
+ return VariableCreate(stmt->variable->relname,
+ namespaceid,
+ typid,
+ typmod,
+ varowner,
+ cooked_default,
+ stmt->if_not_exists);
+}
+
+/*
+ * Try to search value in hash table. If doesn't
+ * exists insert it (and calculate defexpr if exists.
+ */
+static SchemaVariable
+PrepareSchemaVariableForReading(Oid varid)
+{
+ SchemaVariable svar;
+ Variable *var;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+ if (!found)
+ {
+ var = GetVariable(varid, false);
+ get_typlenbyval(var->typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = var->typid;
+ svar->typmod = var->typmod;
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+ svar->is_rowtype = type_is_rowtype(var->typid);
+
+ /* when we don't need calculate defexpr, value is valid already */
+ svar->is_valid = var->defexpr ? false : true;
+ }
+ else if (!svar->is_valid)
+ {
+ /* we need var to recalculate defexpr */
+ var = GetVariable(varid, false);
+ }
+ else
+ /* we don't need to go to sys cache */
+ var = NULL;
+
+ /*
+ * Initialize variable when it is necessary. It is fresh
+ * or last initialization was not successfull.
+ */
+ if (var != NULL && var->defexpr && !svar->is_valid)
+ {
+ MemoryContext oldcontext = NULL;
+
+ Datum value = (Datum) 0;
+ bool null;
+ EState *estate = NULL;
+ Expr *defexpr;
+ ExprState *defexprs;
+
+ /* Prepare default expr */
+ estate = CreateExecutorState();
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ defexpr = expression_planner((Expr *) var->defexpr);
+ defexprs = ExecInitExpr(defexpr, NULL);
+ value = ExecEvalExprSwitchContext(defexprs, GetPerTupleExprContext(estate), &null);
+
+ MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!null)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ FreeExecutorState(estate);
+ }
+
+ if (!svar->is_valid)
+ elog(ERROR, "the content of variable is not valid");
+
+ return svar;
+}
+
+/*
+ * Returns content of variable. We expext secured access now.
+ * Secure check should be done before.
+ */
+Datum
+GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid)
+{
+ SchemaVariable svar;
+
+ svar = PrepareSchemaVariableForReading(varid);
+ *isNull = svar->isnull;
+
+ if (expected_typid != svar->typid)
+ elog(ERROR, "type of variable \"%s\" is different than expected",
+ schema_variable_get_name(varid));
+
+ return (Datum) svar->value;
+}
+
+/*
+ * Write value to variable. We expect secured access in this moment.
+ * In this time, we recheck syschache about used type.
+ */
+void
+SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod)
+{
+ MemoryContext oldcontext = NULL;
+
+ SchemaVariable svar;
+ Oid var_typid;
+ int32 var_typmod;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+
+ get_schema_variable_type_typmod(varid, &var_typid, &var_typmod);
+
+ /* check types first */
+ if (var_typid != typid)
+ elog(ERROR, "type of expression is different than schema variable type");
+
+ if (found)
+ {
+ /* release current content first */
+ if (svar->freeval)
+ {
+ pfree(DatumGetPointer(svar->value));
+ svar->value = (Datum) 0;
+ svar->isnull = true;
+ svar->freeval = false;
+ }
+ }
+
+ get_typlenbyval(typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = typid;
+ svar->typmod = typmod;
+
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+
+ svar->is_rowtype = type_is_rowtype(typid);
+ svar->is_valid = false;
+
+ oldcontext = MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!isNull)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+}
+
+void
+doLetStmt(PlannedStmt *pstmt,
+ ParamListInfo params,
+ QueryEnvironment *queryEnv,
+ const char *queryString)
+{
+ QueryDesc *queryDesc;
+ DestReceiver *dest;
+
+ PushCopiedSnapshot(GetActiveSnapshot());
+ UpdateActiveSnapshotCommandId();
+
+ /* Create dest receiver for LET */
+ dest = CreateDestReceiver(DestVariable);
+
+ SetVariableDestReceiverParams(dest, pstmt->resultVariable);
+
+ /* Create a QueryDesc requesting no output */
+ queryDesc = CreateQueryDesc(pstmt, queryString,
+ GetActiveSnapshot(),
+ InvalidSnapshot,
+ dest, params, queryEnv, 0);
+
+ ExecutorStart(queryDesc, 0);
+ ExecutorRun(queryDesc, ForwardScanDirection, 2L, true);
+ ExecutorFinish(queryDesc);
+ ExecutorEnd(queryDesc);
+
+ FreeQueryDesc(queryDesc);
+
+ PopActiveSnapshot();
+}
+
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index eb2d33dd86..22cd7871cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9630,6 +9630,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
/*
* We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index cc09895fa5..ee8ff7da9e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -29,6 +29,6 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
nodeCtescan.o nodeNamedtuplestorescan.o nodeWorktablescan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o \
- nodeTableFuncscan.o
+ nodeTableFuncscan.o svariableReceiver.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d7..58d4955dd8 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -33,6 +33,7 @@
#include "access/nbtree.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -727,6 +728,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
Param *param = (Param *) node;
ParamListInfo params;
+ AclResult aclresult;
switch (param->paramkind)
{
@@ -736,6 +738,19 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
break;
+ case PARAM_SCHEMA_VARIABLE:
+ /* Check permission to read schema variable */
+ aclresult = pg_variable_aclcheck(param->paramid, GetUserId(), ACL_READ);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE,
+ schema_variable_get_name(param->paramid));
+
+ scratch.opcode = EEOP_PARAM_VARIABLE;
+ scratch.d.param.paramid = param->paramid;
+ scratch.d.param.paramtype = param->paramtype;
+ ExprEvalPushStep(state, &scratch);
+ break;
+
case PARAM_EXTERN:
/*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25aae5..25966ceeeb 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -351,6 +352,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1007,6 +1009,20 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_VARIABLE)
+ {
+ Datum d;
+ bool isnull;
+
+ d = GetSchemaVariable(op->d.param.paramid, &isnull,
+ op->d.param.paramtype);
+
+ *op->resvalue = d;
+ *op->resnull = isnull;
+
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_CASE_TESTVAL)
{
/*
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 01e1a46180..3721aec7a1 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,9 +43,11 @@
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_publication.h"
+#include "catalog/pg_variable.h"
#include "commands/matview.h"
#include "commands/trigger.h"
#include "executor/execdebug.h"
+#include "executor/svariableReceiver.h"
#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -204,12 +206,18 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
*/
estate->es_queryEnv = queryDesc->queryEnv;
+ /*
+ * Result can be stored in schema variable.
+ */
+ estate->es_result_variable = queryDesc->plannedstmt->resultVariable;
+
/*
* If non-read-only query, set the command ID to mark output tuples with
*/
switch (queryDesc->operation)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
@@ -345,6 +353,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate->es_lastoid = InvalidOid;
sendTuples = (operation == CMD_SELECT ||
+ OidIsValid(estate->es_result_variable) ||
queryDesc->plannedstmt->hasReturning);
if (sendTuples)
@@ -924,6 +933,17 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_num_root_result_relations = 0;
}
+ if (OidIsValid(estate->es_result_variable))
+ {
+ AclResult aclresult;
+ Oid varid = estate->es_result_variable;
+
+ /* Ensure this variable is writeable */
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, schema_variable_get_name(varid));
+ }
+
/*
* Similarly, we have to lock relations selected FOR [KEY] UPDATE/SHARE
* before we initialize the plan tree, else we'd be risking lock upgrades.
diff --git a/src/backend/executor/svariableReceiver.c b/src/backend/executor/svariableReceiver.c
new file mode 100644
index 0000000000..0eac4b5d0c
--- /dev/null
+++ b/src/backend/executor/svariableReceiver.c
@@ -0,0 +1,145 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.c
+ * An implementation of DestReceiver that stores the result value in
+ * a schema variable.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/svariableReceiver.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/tuptoaster.h"
+#include "executor/svariableReceiver.h"
+#include "commands/schemavariable.h"
+
+typedef struct
+{
+ DestReceiver pub;
+ Oid varid;
+ Oid typid;
+ int32 typmod;
+ int typlen;
+ int slot_offset;
+ int rows;
+} svariableState;
+
+
+/*
+ * Prepare to receive tuples from executor.
+ */
+static void
+svariableStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+ svariableState *myState = (svariableState *) self;
+ int natts = typeinfo->natts;
+ int outcols = 0;
+ int i;
+
+ for (i = 0; i < natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+
+ if (attr->attisdropped)
+ continue;
+
+ if (++outcols > 1)
+ elog(ERROR, "svariable DestReceiver can take only one attribute");
+
+ myState->typid = attr->atttypid;
+ myState->typmod = attr->atttypmod;
+ myState->typlen = attr->attlen;
+ myState->slot_offset = i;
+ }
+
+ myState->rows = 0;
+}
+
+/*
+ * Receive a tuple from the executor and store it in schema variable.
+ */
+static bool
+svariableReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
+{
+ svariableState *myState = (svariableState *) self;
+ Datum value;
+ bool isnull;
+ bool freeval = false;
+
+ /* Make sure the tuple is fully deconstructed */
+ slot_getallattrs(slot);
+
+ value = slot->tts_values[myState->slot_offset];
+ isnull = slot->tts_isnull[myState->slot_offset];
+
+ if (myState->typlen == -1 && !isnull && VARATT_IS_EXTERNAL(DatumGetPointer(value)))
+ {
+ value = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+ DatumGetPointer(value)));
+ freeval = true;
+ }
+
+ SetSchemaVariable(myState->varid, value, isnull, myState->typid, myState->typmod);
+
+ if (freeval)
+ pfree(DatumGetPointer(value));
+
+ return true;
+}
+
+/*
+ * Clean up at end of an executor run
+ */
+static void
+svariableShutdownReceiver(DestReceiver *self)
+{
+ /* Do nothing */
+}
+
+/*
+ * Destroy receiver when done with it
+ */
+static void
+svariableDestroyReceiver(DestReceiver *self)
+{
+ pfree(self);
+}
+
+/*
+ * Initially create a DestReceiver object.
+ */
+DestReceiver *
+CreateVariableDestReceiver(void)
+{
+ svariableState *self = (svariableState *) palloc0(sizeof(svariableState));
+
+ self->pub.receiveSlot = svariableReceiveSlot;
+ self->pub.rStartup = svariableStartupReceiver;
+ self->pub.rShutdown = svariableShutdownReceiver;
+ self->pub.rDestroy = svariableDestroyReceiver;
+ self->pub.mydest = DestVariable;
+
+ /* private fields will be set by SetVariableDestReceiverParams */
+
+ return (DestReceiver *) self;
+}
+
+/*
+ * Set parameters for a VariableDestReceiver
+ */
+void
+SetVariableDestReceiverParams(DestReceiver *self, Oid varid)
+{
+ svariableState *myState = (svariableState *) self;
+
+ Assert(myState->pub.mydest == DestVariable);
+ Assert(OidIsValid(varid));
+
+ myState->varid = varid;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c8220cf65..fcaa2db51a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(nonleafResultRelations);
COPY_NODE_FIELD(rootResultRelations);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks);
@@ -3000,6 +3001,7 @@ _copyQuery(const Query *from)
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_SCALAR_FIELD(hasAggs);
COPY_SCALAR_FIELD(hasWindowFuncs);
COPY_SCALAR_FIELD(hasTargetSRFs);
@@ -3118,6 +3120,18 @@ _copySelectStmt(const SelectStmt *from)
return newnode;
}
+static LetStmt *
+_copyLetStmt(const LetStmt *from)
+{
+ LetStmt *newnode = makeNode(LetStmt);
+
+ COPY_NODE_FIELD(target);
+ COPY_NODE_FIELD(selectStmt);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
static SetOperationStmt *
_copySetOperationStmt(const SetOperationStmt *from)
{
@@ -5166,6 +5180,9 @@ copyObjectImpl(const void *from)
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
+ case T_LetStmt:
+ retval = _copyLetStmt(from);
+ break;
case T_SetOperationStmt:
retval = _copySetOperationStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 378f2facb8..3ec472e19b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -949,6 +949,7 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_SCALAR_FIELD(canSetTag);
COMPARE_NODE_FIELD(utilityStmt);
COMPARE_SCALAR_FIELD(resultRelation);
+ COMPARE_SCALAR_FIELD(resultVariable);
COMPARE_SCALAR_FIELD(hasAggs);
COMPARE_SCALAR_FIELD(hasWindowFuncs);
COMPARE_SCALAR_FIELD(hasTargetSRFs);
@@ -1057,6 +1058,16 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
return true;
}
+static bool
+_equalLetStmt(const LetStmt *a, const LetStmt *b)
+{
+ COMPARE_NODE_FIELD(target);
+ COMPARE_NODE_FIELD(selectStmt);
+
+ return true;
+}
+
+
static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
@@ -3225,6 +3236,9 @@ equal(const void *a, const void *b)
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
break;
+ case T_LetStmt:
+ retval = _equalLetStmt(a, b);
+ break;
case T_SetOperationStmt:
retval = _equalSetOperationStmt(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6269f474d2..46404ff9ac 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -278,6 +278,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(nonleafResultRelations);
WRITE_NODE_FIELD(rootResultRelations);
+ WRITE_OID_FIELD(resultVariable);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks);
@@ -2793,6 +2794,16 @@ _outSelectStmt(StringInfo str, const SelectStmt *node)
WRITE_NODE_FIELD(rarg);
}
+static void
+_outLetStmt(StringInfo str, const LetStmt *node)
+{
+ WRITE_NODE_TYPE("LET");
+
+ WRITE_NODE_FIELD(target);
+ WRITE_NODE_FIELD(selectStmt);
+ WRITE_LOCATION_FIELD(location);
+}
+
static void
_outFuncCall(StringInfo str, const FuncCall *node)
{
@@ -2971,6 +2982,7 @@ _outQuery(StringInfo str, const Query *node)
appendStringInfoString(str, " :utilityStmt <>");
WRITE_INT_FIELD(resultRelation);
+ WRITE_INT_FIELD(resultVariable);
WRITE_BOOL_FIELD(hasAggs);
WRITE_BOOL_FIELD(hasWindowFuncs);
WRITE_BOOL_FIELD(hasTargetSRFs);
@@ -4191,6 +4203,9 @@ outNode(StringInfo str, const void *obj)
case T_SelectStmt:
_outSelectStmt(str, obj);
break;
+ case T_LetStmt:
+ _outLetStmt(str, obj);
+ break;
case T_ColumnDef:
_outColumnDef(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3254524223..4454327549 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -242,6 +242,7 @@ _readQuery(void)
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
+ READ_INT_FIELD(resultVariable);
READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasWindowFuncs);
READ_BOOL_FIELD(hasTargetSRFs);
@@ -1485,6 +1486,7 @@ _readPlannedStmt(void)
READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(nonleafResultRelations);
READ_NODE_FIELD(rootResultRelations);
+ READ_OID_FIELD(resultVariable);
READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index fd06da98b9..01f97f2d86 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -335,7 +335,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
*/
if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
IsUnderPostmaster &&
- parse->commandType == CMD_SELECT &&
+ (parse->commandType == CMD_SELECT ||
+ parse->commandType == CMD_PLAN_UTILITY) &&
!parse->hasModifyingCTE &&
max_parallel_workers_per_gather > 0 &&
!IsParallelWorker() &&
@@ -352,6 +353,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->parallelModeOK = false;
}
+
+
/*
* glob->parallelModeNeeded is normally set to false here and changed to
* true during plan creation if a Gather or Gather Merge plan is actually
@@ -521,6 +524,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->resultRelations = glob->resultRelations;
result->nonleafResultRelations = glob->nonleafResultRelations;
result->rootResultRelations = glob->rootResultRelations;
+ result->resultVariable = parse->resultVariable;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks;
@@ -2167,7 +2171,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
* inheritance_planner, add the ModifyTable node.
*/
- if (parse->commandType != CMD_SELECT && !inheritance_update)
+ if (parse->commandType != CMD_SELECT && parse->commandType != CMD_PLAN_UTILITY && !inheritance_update)
{
List *withCheckOptionLists;
List *returningLists;
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 8603feef2b..2923e3fcc7 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -71,6 +71,7 @@ preprocess_targetlist(PlannerInfo *root)
{
Query *parse = root->parse;
int result_relation = parse->resultRelation;
+ int result_variable = parse->resultVariable;
List *range_table = parse->rtable;
CmdType command_type = parse->commandType;
RangeTblEntry *target_rte = NULL;
@@ -96,6 +97,10 @@ preprocess_targetlist(PlannerInfo *root)
target_relation = heap_open(target_rte->relid, NoLock);
}
+ else if (result_variable)
+ {
+ Assert(command_type == CMD_PLAN_UTILITY);
+ }
else
Assert(command_type == CMD_SELECT);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a04ad6e99e..da570bb23b 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1254,7 +1254,8 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
{
Param *param = (Param *) node;
- if (param->paramkind == PARAM_EXTERN)
+ if (param->paramkind == PARAM_EXTERN ||
+ param->paramkind == PARAM_SCHEMA_VARIABLE)
return false;
if (param->paramkind != PARAM_EXEC ||
@@ -4799,7 +4800,7 @@ substitute_actual_parameters_mutator(Node *node,
{
if (node == NULL)
return NULL;
- if (IsA(node, Param))
+ if (IsA(node, Param) && ((Param *) node)->paramkind != PARAM_SCHEMA_VARIABLE)
{
Param *param = (Param *) node;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8369e3ad62..fc0cf34c7d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1272,7 +1272,7 @@ get_relation_constraints(PlannerInfo *root,
* descriptor, instead of constraint exclusion which is driven by the
* individual partition's partition constraint.
*/
- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
+ if (enable_partition_pruning && root->parse->commandType != CMD_SELECT && root->parse->commandType != CMD_PLAN_UTILITY)
{
List *pcqual = RelationGetPartitionQual(relation);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c601b6d40d..441b298693 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,7 +25,10 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/namespace.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -44,6 +47,8 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -78,6 +83,8 @@ static Query *transformCreateTableAsStmt(ParseState *pstate,
CreateTableAsStmt *stmt);
static Query *transformCallStmt(ParseState *pstate,
CallStmt *stmt);
+static Query *transformLetStmt(ParseState *pstate,
+ LetStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown);
#ifdef RAW_EXPRESSION_COVERAGE_TEST
@@ -267,6 +274,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
+ case T_LetStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
@@ -327,6 +335,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
(CallStmt *) parseTree);
break;
+ case T_LetStmt:
+ result = transformLetStmt(pstate,
+ (LetStmt *) parseTree);
+ break;
+
default:
/*
@@ -367,6 +380,7 @@ analyze_requires_snapshot(RawStmt *parseTree)
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
+ case T_LetStmt:
result = true;
break;
@@ -1567,6 +1581,203 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
return qry;
}
+/*
+ * transformLetStmt -
+ * transform an Let Statement
+ */
+static Query *
+transformLetStmt(ParseState *pstate, LetStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ List *exprList = NIL;
+ List *exprListCoer = NIL;
+ List *indirection = NIL;
+ ListCell *lc;
+ Query *selectQuery;
+ int i = 0;
+
+ Oid varid;
+
+ ParseExprKind sv_expr_kind;
+ char *attrname = NULL;
+ bool not_unique;
+ bool is_rowtype;
+ Oid typid;
+ int32 typmod;
+
+ AclResult aclresult;
+ List *names = NULL;
+ int indirection_start;
+
+ sv_expr_kind = pstate->p_expr_kind;
+ pstate->p_expr_kind = EXPR_KIND_LET;
+
+ /* There can't be any outer WITH to worry about */
+ Assert(pstate->p_ctenamespace == NIL);
+
+ /* Exec this command as utility */
+ qry->commandType = CMD_PLAN_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ names = NamesFromList(stmt->target);
+
+ varid = identify_variable(names, &attrname, ¬_unique);
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("target \"%s\" of LET command is ambiguous",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ if (!OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("schema variable \"%s\" doesn't exists",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ qry->resultVariable = varid;
+
+ get_schema_variable_type_typmod(varid, &typid, &typmod);
+
+ is_rowtype = type_is_rowtype(typid);
+
+ if (attrname && !is_rowtype)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("target variable \"%s\" is not row type",
+ schema_variable_get_name(varid)),
+ parser_errposition(pstate, stmt->location)));
+
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, NameListToString(names));
+
+ selectQuery = transformStmt(pstate, stmt->selectStmt);
+
+ /* The grammar should have produced a SELECT */
+ if (!IsA(selectQuery, Query) ||
+ selectQuery->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in LET ... SELECT");
+
+ /*----------
+ * Generate an expression list for the LET that selects all the
+ * non-resjunk columns from the subquery.
+ *----------
+ */
+ exprList = NIL;
+ foreach(lc, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->resjunk)
+ continue;
+
+ exprList = lappend(exprList, tle->expr);
+ }
+
+ /*
+ * Because doesn't support pattern matching, don't allow multicolumn result
+ */
+ if (list_length(exprList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expression is not scalar value"),
+ parser_errposition(pstate,
+ exprLocation((Node *) exprList))));
+
+ indirection_start = list_length(names) - (attrname ? 1 : 0);
+ indirection = list_copy_tail(stmt->target, indirection_start);
+
+ exprListCoer = NIL;
+ foreach(lc, exprList)
+ {
+ Node *orig_expr = (Node*) lfirst(lc);
+ Oid exprtypid = exprType((Node *) orig_expr);
+ Param *param = makeNode(Param);
+ Expr *expr = NULL;
+
+ param->paramkind = PARAM_SCHEMA_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (indirection != NULL)
+ {
+ bool targetIsArray;
+ char *targetName;
+
+ targetName = attrname != NULL ? attrname : get_schema_variable_name(varid);
+ targetIsArray = OidIsValid(get_element_type(typid));
+
+ expr = (Expr *)
+ transformAssignmentIndirection(pstate,
+ (Node *) param,
+ targetName,
+ targetIsArray,
+ typid,
+ typmod,
+ InvalidOid,
+ list_head(indirection),
+ (Node *) orig_expr,
+ stmt->location);
+ }
+ else
+ expr = (Expr *)
+ coerce_to_target_type(pstate,
+ (Node *) orig_expr,
+ exprtypid,
+ typid, typmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ stmt->location);
+
+ if (expr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable \"%s\" is of type %s,"
+ " but expression is of type %s",
+ schema_variable_get_name(varid),
+ format_type_be(typid),
+ format_type_be(exprtypid)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *) orig_expr))));
+
+ exprListCoer = lappend(exprListCoer, expr);
+ }
+
+ /*
+ * Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
+ */
+ qry->targetList = NIL;
+ foreach(lc, exprListCoer)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ tle = makeTargetEntry(expr,
+ i + 1,
+ FigureColname((Node *)expr),
+ false);
+ qry->targetList = lappend(qry->targetList, tle);
+ }
+
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+ qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+
+ assign_query_collations(pstate, qry);
+
+ pstate->p_expr_kind = sv_expr_kind;
+
+ return qry;
+}
+
+
/*
* transformSetOperationStmt -
* transforms a set-operations tree
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 87f5e95827..25036669c1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -257,8 +257,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSchemaVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -268,7 +268,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DropTransformStmt
DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
- ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
+ LetStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -400,6 +400,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
TriggerTransitions TriggerReferencing
publication_name_list
vacuum_relation_list opt_vacuum_relation_list
+ let_target
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -584,6 +585,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> optSchemaVarDefExpr
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -649,7 +651,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+ LEADING LEAKPROOF LEAST LEFT LET LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -687,8 +689,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -878,6 +880,7 @@ stmt :
| CreatePolicyStmt
| CreatePLangStmt
| CreateSchemaStmt
+ | CreateSchemaVarStmt
| CreateSeqStmt
| CreateStmt
| CreateSubscriptionStmt
@@ -917,6 +920,7 @@ stmt :
| ImportForeignSchemaStmt
| IndexStmt
| InsertStmt
+ | LetStmt
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
@@ -1808,7 +1812,12 @@ DiscardStmt:
n->target = DISCARD_SEQUENCES;
$$ = (Node *) n;
}
-
+ | DISCARD VARIABLES
+ {
+ DiscardStmt *n = makeNode(DiscardStmt);
+ n->target = DISCARD_VARIABLES;
+ $$ = (Node *) n;
+ }
;
@@ -4479,6 +4488,42 @@ create_extension_opt_item:
}
;
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE VARIABLE varname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSchemaVarStmt:
+ CREATE OptTemp VARIABLE qualified_name opt_as Typename optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $4->relpersistence = $2;
+ n->variable = $4;
+ n->typeName = $6;
+ n->defexpr = $7;
+ n->if_not_exists = false;
+ $$ = (Node *) n;
+ }
+ | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $7->relpersistence = $2;
+ n->variable = $7;
+ n->typeName = $9;
+ n->defexpr = $10;
+ n->if_not_exists = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+optSchemaVarDefExpr: DEFAULT b_expr { $$ = $2; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+
+
/*****************************************************************************
*
* ALTER EXTENSION name UPDATE [ TO version ]
@@ -6335,6 +6380,7 @@ drop_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name_list */
@@ -6604,6 +6650,7 @@ comment_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -6742,6 +6789,7 @@ security_label_type_any_name:
| TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -7163,6 +7211,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| ALL TABLES IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7203,6 +7259,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
;
@@ -7363,6 +7427,7 @@ defacl_privilege_target:
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
;
@@ -8959,6 +9024,25 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newname = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
opt_column: COLUMN { $$ = COLUMN; }
@@ -9277,6 +9361,25 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newschema = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
/*****************************************************************************
@@ -9512,6 +9615,14 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
@@ -10693,6 +10804,7 @@ ExplainableStmt:
| CreateMatViewStmt
| RefreshMatViewStmt
| ExecuteStmt /* by default all are $$=$1 */
+ | LetStmt
;
explain_option_list:
@@ -10750,6 +10862,7 @@ PreparableStmt:
| InsertStmt
| UpdateStmt
| DeleteStmt /* by default all are $$=$1 */
+ | LetStmt
;
/*****************************************************************************
@@ -11148,6 +11261,44 @@ opt_hold: /* EMPTY */ { $$ = 0; }
| WITHOUT HOLD { $$ = 0; }
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * LET STATEMENTS
+ *
+ *****************************************************************************/
+LetStmt: LET let_target '=' a_expr
+ {
+ LetStmt *n = makeNode(LetStmt);
+ SelectStmt *select = makeNode(SelectStmt);
+ ResTarget *res = makeNode(ResTarget);
+
+ n->target = $2;
+
+ /* Create target list for implicit query */
+ res->name = NULL;
+ res->indirection = NIL;
+ res->val = (Node *) $4;
+ res->location = @4;
+
+ select->targetList = list_make1(res);
+ n->selectStmt = (Node *) select;
+
+ n->location = @2;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+let_target:
+ ColId opt_indirection
+ {
+ $$ = list_make1(makeString($1));
+ if ($2)
+ $$ = list_concat($$,
+ check_indirection($2, yyscanner));
+ }
+
/*****************************************************************************
*
* QUERY:
@@ -15127,6 +15278,7 @@ unreserved_keyword:
| LARGE_P
| LAST_P
| LEAKPROOF
+ | LET
| LEVEL
| LISTEN
| LOAD
@@ -15275,6 +15427,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 61727e1d71..6823612fba 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -349,6 +349,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
Assert(false); /* can't happen */
break;
case EXPR_KIND_OTHER:
+ case EXPR_KIND_LET:
/*
* Accept aggregate/grouping here; caller must throw error if
@@ -465,6 +466,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
if (isAgg)
err = _("aggregate functions are not allowed in DEFAULT expressions");
@@ -879,6 +881,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("window functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -902,6 +905,8 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_CALL_ARGUMENT:
err = _("window functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("window functions are not allowed in LET statement");
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a9b6..bcdda0fb4a 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -37,6 +38,7 @@
#include "utils/date.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
+#include "utils/typcache.h"
#include "utils/xml.h"
@@ -116,6 +118,9 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
+static Node *makeParamSchemaVariable(ParseState *pstate,
+ Oid varid, Oid typid, int32 typmod,
+ char *attrname, int location);
static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
int location);
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
@@ -512,6 +517,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
+ Oid varid = InvalidOid;
+ char *attrname = NULL;
+ bool not_unique;
+
RangeTblEntry *rte;
int levels_up;
enum
@@ -749,6 +758,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break;
}
+ varid = identify_variable(cref->fields, &attrname, ¬_unique);
+
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("schema variable reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+
/*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
@@ -773,6 +791,71 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
parser_errposition(pstate, cref->location)));
}
+ if (OidIsValid(varid))
+ {
+ Oid typid;
+ int32 typmod;
+
+ get_schema_variable_type_typmod(varid, &typid, &typmod);
+
+ if (node != NULL)
+ {
+ /*
+ * some collision can be solved simply here to reduce errors
+ * based on simply existence of some variables. Often error
+ * can be using alias same like variable name. In this case,
+ * when we found column reference, and we found reference to
+ * possible composite variable, but the variable is not composite,
+ * then we can ignore the variable as simply improper, and we
+ * use column reference only.
+ */
+ if (attrname)
+ {
+ if (type_is_rowtype(typid))
+ {
+ TupleDesc tupdesc;
+ bool found = false;
+ int i;
+
+ /* slow part, I hope it will not be to often */
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ if (namestrcmp(&(TupleDescAttr(tupdesc, i)->attname), attrname) == 0 &&
+ !TupleDescAttr(tupdesc, i)->attisdropped)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ FreeTupleDesc(tupdesc);
+
+ /* there are not composite variable with this field */
+ if (!found)
+ varid = InvalidOid;
+ }
+ else
+ /* there are not composite variable with this name */
+ varid = InvalidOid;
+ }
+
+ /* Raise error if varid is still valid. It should be really amigonuous */
+ if (OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ errdetail("The qualified identifier can be column reference or schema variable reference"),
+ parser_errposition(pstate, cref->location)));
+ }
+
+ if (OidIsValid(varid))
+ node = makeParamSchemaVariable(pstate,
+ varid, typid, typmod,
+ attrname, cref->location);
+ }
+
/*
* Throw error if no translation found.
*/
@@ -807,6 +890,59 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return node;
}
+/*
+ * Generate param variable for reference to schema variable
+ */
+static Node *
+makeParamSchemaVariable(ParseState *pstate, Oid varid, Oid typid, int32 typmod, char *attrname, int location)
+{
+ Param *param;
+
+ param = makeNode(Param);
+
+ param->paramkind = PARAM_SCHEMA_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (attrname != NULL)
+ {
+ TupleDesc tupdesc;
+ int i;
+
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+
+ if (strcmp(attrname, NameStr(att->attname)) == 0 &&
+ !att->attisdropped)
+ {
+ /* Success, so generate a FieldSelect expression */
+ FieldSelect *fselect = makeNode(FieldSelect);
+
+ fselect->arg = (Expr *) param;
+ fselect->fieldnum = i + 1;
+ fselect->resulttype = att->atttypid;
+ fselect->resulttypmod = att->atttypmod;
+ /* save attribute's collation for parse_collate.c */
+ fselect->resultcollid = att->attcollation;
+
+ ReleaseTupleDesc(tupdesc);
+ return (Node *) fselect;
+ }
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("could not identify column \"%s\" in variable", attrname),
+ parser_errposition(pstate, location)));
+ }
+
+ return (Node *) param;
+}
+
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
@@ -1818,6 +1954,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
case EXPR_KIND_VALUES_SINGLE:
+ case EXPR_KIND_LET:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
@@ -1826,6 +1963,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -3460,6 +3598,7 @@ ParseExprKindName(ParseExprKind exprKind)
return "CHECK";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
return "DEFAULT";
case EXPR_KIND_INDEX_EXPRESSION:
return "index expression";
@@ -3475,6 +3614,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "PARTITION BY";
case EXPR_KIND_CALL_ARGUMENT:
return "CALL";
+ case EXPR_KIND_LET:
+ return "LET";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 44257154b8..b2c9900e00 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2347,6 +2347,7 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("set-returning functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -2370,6 +2371,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_CALL_ARGUMENT:
err = _("set-returning functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("set-returning functions are not allowed in CALL arguments");
+ break;
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4932e58022..c60fe011f7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -35,16 +35,6 @@
static void markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
Var *var, int levelsup);
-static Node *transformAssignmentIndirection(ParseState *pstate,
- Node *basenode,
- const char *targetName,
- bool targetIsArray,
- Oid targetTypeId,
- int32 targetTypMod,
- Oid targetCollation,
- ListCell *indirection,
- Node *rhs,
- int location);
static Node *transformAssignmentSubscripts(ParseState *pstate,
Node *basenode,
const char *targetName,
@@ -672,7 +662,7 @@ updateTargetListEntry(ParseState *pstate,
* might want to decorate indirection cells with their own location info,
* in which case the location argument could probably be dropped.)
*/
-static Node *
+Node *
transformAssignmentIndirection(ParseState *pstate,
Node *basenode,
const char *targetName,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 3123ee274d..10737d422d 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3350,7 +3350,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
* get executed. Also, utilities aren't rewritten at all (do we still
* need that check?)
*/
- if (event != CMD_SELECT && event != CMD_UTILITY)
+ if (event != CMD_SELECT && event != CMD_UTILITY && event != CMD_PLAN_UTILITY)
{
int result_relation;
RangeTblEntry *rt_entry;
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 61ef396d8a..6a068af799 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -212,7 +212,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
}
/*
- * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
+ * For SELECT, LET, UPDATE and DELETE, add security quals to enforce the USING
* policies. These security quals control access to existing table rows.
* Restrictive policies are combined together using AND, and permissive
* policies are combined together using OR.
@@ -222,6 +222,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
&restrictive_policies);
if (commandType == CMD_SELECT ||
+ commandType == CMD_PLAN_UTILITY ||
commandType == CMD_UPDATE ||
commandType == CMD_DELETE)
add_security_quals(rt_index,
@@ -423,6 +424,7 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
switch (cmd)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
if (policy->polcmd == ACL_SELECT_CHR)
cmd_matches = true;
break;
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index c95a4d519d..47fb0f38b1 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -37,6 +37,7 @@
#include "executor/functions.h"
#include "executor/tqueue.h"
#include "executor/tstoreReceiver.h"
+#include "executor/svariableReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/portal.h"
@@ -143,6 +144,9 @@ CreateDestReceiver(CommandDest dest)
case DestTupleQueue:
return CreateTupleQueueDestReceiver(NULL);
+
+ case DestVariable:
+ return CreateVariableDestReceiver();
}
/* should never get here */
@@ -178,6 +182,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -222,6 +227,7 @@ NullCommand(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -268,6 +274,7 @@ ReadyForQuery(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b5804f64ad..35199fd0dc 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
@@ -344,7 +345,7 @@ ProcessUtility(PlannedStmt *pstmt,
char *completionTag)
{
Assert(IsA(pstmt, PlannedStmt));
- Assert(pstmt->commandType == CMD_UTILITY);
+ Assert(pstmt->commandType == CMD_UTILITY || pstmt->commandType == CMD_PLAN_UTILITY);
Assert(queryString != NULL); /* required as of 8.4 */
/*
@@ -915,6 +916,14 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
}
+ case T_LetStmt:
+ {
+ doLetStmt(pstmt, params, queryEnv, queryString);
+ if (completionTag)
+ strcpy(completionTag, "LET");
+ }
+ break;
+
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
@@ -1221,6 +1230,10 @@ ProcessUtilitySlow(ParseState *pstate,
}
break;
+ case T_CreateSchemaVarStmt:
+ address = DefineSchemaVariable(pstate, (CreateSchemaVarStmt *) parsetree);
+ break;
+
/*
* ************* object creation / destruction **************
*/
@@ -2055,6 +2068,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_STATISTIC_EXT:
tag = "ALTER STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "ALTER VARIABLE";
+ break;
default:
tag = "???";
break;
@@ -2104,6 +2120,10 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ case T_LetStmt:
+ tag = "LET";
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
@@ -2358,6 +2378,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "DROP VARIABLE";
+ break;
default:
tag = "???";
}
@@ -2639,6 +2662,9 @@ CreateCommandTag(Node *parsetree)
case DISCARD_SEQUENCES:
tag = "DISCARD SEQUENCES";
break;
+ case DISCARD_VARIABLES:
+ tag = "DISCARD VARIABLES";
+ break;
default:
tag = "???";
}
@@ -2844,6 +2870,7 @@ CreateCommandTag(Node *parsetree)
tag = "DELETE";
break;
case CMD_UTILITY:
+ case CMD_PLAN_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
default:
@@ -2915,6 +2942,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_CreateSchemaVarStmt:
+ tag = "CREATE VARIABLE";
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -2961,6 +2992,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ case T_LetStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a45e093de7..952c0d9628 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -315,6 +315,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_READ_CHR:
+ read = ACL_READ;
+ break;
+ case ACL_WRITE_CHR:
+ read = ACL_WRITE;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -808,6 +814,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -903,6 +913,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'T':
objtype = OBJECT_TYPE;
break;
+ case 'V':
+ objtype = OBJECT_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
}
@@ -1627,6 +1640,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CONNECT;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
+ if (pg_strcasecmp(priv_type, "READ") == 0)
+ return ACL_READ;
+ if (pg_strcasecmp(priv_type, "WRITE") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1721,6 +1738,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 03e9a28a63..488cb26d3f 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7362,6 +7362,14 @@ get_parameter(Param *param, deparse_context *context)
return;
}
+ /* translate paramid to original schema variable name */
+ if (param->paramkind == PARAM_SCHEMA_VARIABLE)
+ {
+ appendStringInfo(context->buf, "%s",
+ schema_variable_get_name(param->paramid));
+ return;
+ }
+
/*
* Not PARAM_EXEC, or couldn't find referent: just print $N.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bba595ad1d..858a6dd4be 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1691,6 +1691,18 @@ get_relname_relid(const char *relname, Oid relnamespace)
ObjectIdGetDatum(relnamespace));
}
+/*
+ * get_varname_varid
+ * Given name and namespace of variable, look up the OID.
+ */
+Oid
+get_varname_varid(const char *varname, Oid varnamespace)
+{
+ return GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(varnamespace));
+}
+
#ifdef NOT_USED
/*
* get_relnatts
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b381782a3..35dc32f649 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -73,6 +73,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "utils/rel.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
@@ -968,6 +969,28 @@ static const struct cachedesc cacheinfo[] = {
0
},
2
+ },
+ {VariableRelationId, /* VARIABLENAMENSP */
+ VariableNameNspIndexId,
+ 2,
+ {
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ 0,
+ 0
+ },
+ 8
+ },
+ {VariableRelationId, /* VARIABLEOID */
+ VariableObjectIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
}
};
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 0d147cb08d..6d97931d85 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -296,6 +296,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading variables\n");
+ getVariables(fout);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 83c976eaf7..c9bc91ca68 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3471,6 +3471,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(type, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(type, "STATISTICS") == 0 ||
+ strcmp(type, "VARIABLE") == 0 ||
/* non-schema-specified objects */
strcmp(type, "DATABASE") == 0 ||
strcmp(type, "PROCEDURAL LANGUAGE") == 0 ||
@@ -3670,7 +3671,8 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
strcmp(te->desc, "SERVER") == 0 ||
strcmp(te->desc, "STATISTICS") == 0 ||
strcmp(te->desc, "PUBLICATION") == 0 ||
- strcmp(te->desc, "SUBSCRIPTION") == 0)
+ strcmp(te->desc, "SUBSCRIPTION") == 0 ||
+ strcmp(te->desc, "VARIABLE") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 9baf7b2fde..f825a00c9d 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -260,6 +260,7 @@ static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
+static void dumpVariable(Archive *fout, VariableInfo *varinfo);
static void dumpDatabase(Archive *AH);
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
const char *dbname, Oid dboid);
@@ -4221,6 +4222,208 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+/*
+ * getVariables
+ * get information about variables
+ */
+void
+getVariables(Archive *fout)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer init_acl_subquery = createPQExpBuffer();
+ PQExpBuffer init_racl_subquery = createPQExpBuffer();
+ PGresult *res;
+ VariableInfo *varinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_varname;
+ int i_varnamespace;
+ int i_vartype;
+ int i_vartypname;
+ int i_vardefexpr;
+ int i_rolname;
+ int i_varacl;
+ int i_rvaracl;
+ int i_initvaracl;
+ int i_initrvaracl;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion <= 110000)
+ return;
+
+ acl_subquery = createPQExpBuffer();
+ racl_subquery = createPQExpBuffer();
+ init_acl_subquery = createPQExpBuffer();
+ init_racl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
+ init_racl_subquery, "v.varacl", "v.varowner", "'V'",
+ dopt->binary_upgrade);
+
+ query = createPQExpBuffer();
+
+ resetPQExpBuffer(query);
+
+ /* Get the variables in current database. */
+ appendPQExpBuffer(query,
+ "SELECT v.tableoid, v.oid, v.varname, "
+ "v.varnamespace,"
+ "(%s varowner) AS rolname, "
+ "%s as varacl, "
+ "%s as rvaracl, "
+ "%s as initvaracl, "
+ "%s as initrvaracl, "
+ "v.vartype, "
+ "pg_catalog.format_type(v.vartype, v.vartypmod) as vartypname, "
+ "pg_catalog.pg_get_expr(v.vardefexpr,0) as vardefexpr "
+ "FROM pg_variable v "
+ "LEFT JOIN pg_init_privs pip "
+ "ON (v.oid = pip.objoid "
+ "AND pip.classoid = 'pg_variable'::regclass "
+ "AND pip.objsubid = 0)",
+ username_subquery,
+ acl_subquery->data,
+ racl_subquery->data,
+ init_acl_subquery->data,
+ init_racl_subquery->data);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(init_acl_subquery);
+ destroyPQExpBuffer(init_racl_subquery);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_varname = PQfnumber(res, "varname");
+ i_varnamespace = PQfnumber(res, "varnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_vartype = PQfnumber(res, "vartype");
+ i_vartypname = PQfnumber(res, "vartypname");
+ i_vardefexpr = PQfnumber(res, "vardefexpr");
+ i_varacl = PQfnumber(res, "varacl");
+ i_rvaracl = PQfnumber(res, "rvaracl");
+ i_initvaracl = PQfnumber(res, "initvaracl");
+ i_initrvaracl = PQfnumber(res, "initrvaracl");
+
+ varinfo = pg_malloc(ntups * sizeof(VariableInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ TypeInfo *vtype;
+
+ varinfo[i].dobj.objType = DO_VARIABLE;
+ varinfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ varinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&varinfo[i].dobj);
+ varinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_varname));
+ varinfo[i].dobj.namespace =
+ findNamespace(fout,
+ atooid(PQgetvalue(res, i, i_varnamespace)));
+
+ varinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ varinfo[i].vartype = atooid(PQgetvalue(res, i, i_vartype));
+ varinfo[i].vartypname = pg_strdup(PQgetvalue(res, i, i_vartypname));
+
+ varinfo[i].varacl = pg_strdup(PQgetvalue(res, i, i_varacl));
+ varinfo[i].rvaracl = pg_strdup(PQgetvalue(res, i, i_rvaracl));
+ varinfo[i].initvaracl = pg_strdup(PQgetvalue(res, i, i_initvaracl));
+ varinfo[i].initrvaracl = pg_strdup(PQgetvalue(res, i, i_initrvaracl));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ /* Do not try to dump ACL if no ACL exists. */
+ if (PQgetisnull(res, i, i_varacl) && PQgetisnull(res, i, i_rvaracl) &&
+ PQgetisnull(res, i, i_initvaracl) &&
+ PQgetisnull(res, i, i_initrvaracl))
+ varinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+
+ if (PQgetisnull(res, i, i_vardefexpr))
+ varinfo[i].vardefexpr = NULL;
+ else
+ varinfo[i].vardefexpr = pg_strdup(PQgetvalue(res, i, i_vardefexpr));
+
+ if (strlen(varinfo[i].rolname) == 0)
+ write_msg(NULL, "WARNING: owner of variable \"%s\" appears to be invalid\n",
+ varinfo[i].dobj.name);
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ vtype = findTypeByOid(varinfo[i].vartype);
+ addObjectDependency(&varinfo[i].dobj, vtype->dobj.dumpId);
+ }
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpVariable
+ * dump the definition of the given variables
+ */
+static void
+dumpVariable(Archive *fout, VariableInfo *varinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ const char *varname;
+ const char *vartypname;
+ const char *vardefexpr;
+
+ /* Skip if not to be dumped */
+ if (!varinfo->dobj.dump || dopt->dataOnly)
+ return;
+
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ varname = fmtQualifiedDumpable(varinfo);
+ vartypname = varinfo->vartypname;
+ vardefexpr = varinfo->vardefexpr;
+
+ appendPQExpBuffer(delq, "DROP VARIABLE %s;\n",
+ varname);
+
+ appendPQExpBuffer(query, "CREATE VARIABLE %s AS %s",
+ varname, vartypname);
+
+ if (vardefexpr)
+ appendPQExpBuffer(query, " DEFAULT %s",
+ vardefexpr);
+
+ appendPQExpBuffer(query, ";\n");
+
+ ArchiveEntry(fout, varinfo->dobj.catId, varinfo->dobj.dumpId,
+ varinfo->dobj.name,
+ NULL,
+ NULL,
+ varinfo->rolname, false,
+ "VARIABLE", SECTION_PRE_DATA,
+ query->data, delq->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ if (varinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, "VARIABLE", varname,
+ NULL, varinfo->rolname,
+ varinfo->dobj.catId, 0, varinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -9849,6 +10052,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_VARIABLE:
+ dumpVariable(fout, (VariableInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -17935,6 +18141,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_OPFAMILY:
case DO_COLLATION:
case DO_CONVERSION:
+ case DO_VARIABLE:
case DO_TABLE:
case DO_ATTRDEF:
case DO_PROCLANG:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 1448005f30..0d49bb7ed7 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -84,7 +84,8 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_VARIABLE
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -625,6 +626,22 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/*
+ * The VariableInfo struct is used to represent schema variables
+ */
+typedef struct _VariableInfo
+{
+ DumpableObject dobj;
+ Oid vartype;
+ char *vartypname;
+ char *rolname; /* name of owner, or empty string */
+ char *vardefexpr;
+ char *varacl;
+ char *rvaracl;
+ char *initvaracl;
+ char *initrvaracl;
+} VariableInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -725,5 +742,6 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern void getVariables(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index ec751a7c23..2a67766ed4 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2601,6 +2601,38 @@ my %tests = (
},
},
+ 'CREATE VARIABLE test_variable' => {
+ all_runs => 1,
+ catch_all => 'CREATE ... commands',
+ create_order => 61,
+ create_sql => 'CREATE VARIABLE dump_test.variable AS integer DEFAULT 0;',
+ regexp => qr/^
+ \QCREATE VARIABLE dump_test.variable AS integer DEFAULT 0;\E/xm,
+ like => {
+ binary_upgrade => 1,
+ clean => 1,
+ clean_if_exists => 1,
+ createdb => 1,
+ defaults => 1,
+ exclude_test_table => 1,
+ exclude_test_table_data => 1,
+ no_blobs => 1,
+ no_privs => 1,
+ no_owner => 1,
+ only_dump_test_schema => 1,
+ pg_dumpall_dbprivs => 1,
+ schema_only => 1,
+ section_pre_data => 1,
+ test_schema_plus_blobs => 1,
+ with_oids => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_test_table => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1,
+ role => 1,
+ section_post_data => 1, }, },
+
'CREATE VIEW test_view' => {
create_order => 61,
create_sql => 'CREATE VIEW dump_test.test_view
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..73a752fd7e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -853,6 +853,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
break;
}
break;
+ case 'V': /* Variables */
+ success = listVariables(pattern, show_verbose);
+ break;
case 'x': /* Extensions */
if (show_verbose)
success = listExtensionContents(pattern);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 80d8338b96..d645bba7af 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4178,6 +4178,80 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
return true;
}
+/*
+ * \dV
+ *
+ * listVariables()
+ */
+bool
+listVariables(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false, false, false, false, false};
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname as \"%s\",\n"
+ " v.varname as \"%s\",\n"
+ " pg_catalog.format_type(v.vartype, v.vartypmod) as \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(v.varowner) as \"%s\",\n"
+ " pg_catalog.pg_get_expr(v.vardefexpr, 0) as \"%s\"",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Type"),
+ gettext_noop("Owner"),
+ gettext_noop("Default"));
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_variable v"
+ "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = v.varnamespace");
+
+ appendPQExpBufferStr(&buf, "\nWHERE true\n");
+ if (!pattern)
+ appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
+ " AND n.nspname <> 'information_schema'\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "v.varname", NULL,
+ "pg_catalog.pg_variable_is_visible(v.oid)");
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ /*
+ * Most functions in this file are content to print an empty table when
+ * there are no matching objects. We intentionally deviate from that
+ * here, but only in !quiet mode, for historical reasons.
+ */
+ if (PQntuples(res) == 0 && !pset.quiet)
+ {
+ if (pattern)
+ psql_error("Did not find any schema variable named \"%s\".\n",
+ pattern);
+ else
+ psql_error("Did not find any schema variables.\n");
+ }
+ else
+ {
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of variables");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+ }
+
+ PQclear(res);
+ return true;
+}
/*
* \dFp
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..ecc4e3a531 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
/* \dt, \di, \ds, \dS, etc. */
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
+/* \dV */
+extern bool listVariables(const char *pattern, bool varbose);
+
/* \dD */
extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 316030d358..adcc36cb6e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
@@ -257,6 +257,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[S+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dV [PATTERN] list variables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index bb696f8ee9..ebec00fe1f 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -805,6 +805,22 @@ static const SchemaQuery Query_for_list_of_statistics = {
NULL
};
+static const SchemaQuery Query_for_list_of_variables = {
+ /* min_server_version */
+ 0,
+ /* catname */
+ "pg_catalog.pg_variable v",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_variable_is_visible(v.oid)",
+ /* namespace */
+ "v.varnamespace",
+ /* result */
+ "pg_catalog.quote_ident(v.varname)",
+ /* qualresult */
+ NULL
+};
/*
* Queries to get lists of names of various kinds of things, possibly
@@ -1249,6 +1265,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
{"USER MAPPING FOR", NULL, NULL, NULL},
+ {"VARIABLE", NULL, NULL, &Query_for_list_of_variables},
{"VIEW", NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
@@ -1604,7 +1621,7 @@ psql_completion(const char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
- "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LET", "LISTEN", "LOAD", "LOCK",
"MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
"RESET", "REVOKE", "ROLLBACK",
@@ -1621,9 +1638,9 @@ psql_completion(const char *text, int start, int end)
"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
- "\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+ "\\dm", "\\dn", "\\do", "\\dO", "\\dp"
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+ "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", "\\dV",
"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
"\\endif", "\\errverbose", "\\ev",
"\\f",
@@ -2837,6 +2854,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
COMPLETE_WITH_LIST2("GROUP", "ROLE");
+/* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
+ /* Complete CREATE VARIABLE <name> with AS */
+ else if (TailMatches3("CREATE", "VARIABLE", MatchAny))
+ COMPLETE_WITH_CONST("AS");
+ /* Complete CREATE VARIABLE <name> with AS types*/
+ else if (TailMatches4("CREATE", "VARIABLE", MatchAny, "AS"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE VIEW <name> with AS */
else if (TailMatches3("CREATE", "VIEW", MatchAny))
@@ -2890,7 +2915,7 @@ psql_completion(const char *text, int start, int end)
/* DISCARD */
else if (Matches1("DISCARD"))
- COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP");
+ COMPLETE_WITH_LIST5("ALL", "PLANS", "SEQUENCES", "TEMP", "VARIABLES");
/* DO */
else if (Matches1("DO"))
@@ -2992,6 +3017,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP VARIABLE */
+ else if (Matches2("DROP", "VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ else if (Matches3("DROP", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3002,14 +3033,14 @@ psql_completion(const char *text, int start, int end)
* Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "ANALYZE", "VERBOSE");
+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "ANALYZE", "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "ANALYZE"))
- COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "VERBOSE");
+ COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "VERBOSE") ||
Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
- COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
+ COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "LET");
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
@@ -3118,6 +3149,7 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'ALL ROUTINES IN SCHEMA'"
" UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
" UNION SELECT 'ALL TABLES IN SCHEMA'"
+ " UNION SELECT 'ALL VARIABLES IN SCHEMA'"
" UNION SELECT 'DATABASE'"
" UNION SELECT 'DOMAIN'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
@@ -3131,14 +3163,16 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'SEQUENCE'"
" UNION SELECT 'TABLE'"
" UNION SELECT 'TABLESPACE'"
- " UNION SELECT 'TYPE'");
+ " UNION SELECT 'TYPE'"
+ " UNION SELECT 'VARIABLE'");
}
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
- COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA",
+ COMPLETE_WITH_LIST6("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
- "TABLES IN SCHEMA");
+ "TABLES IN SCHEMA",
+ "VARIABLES IN SCHEMA");
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
@@ -3172,6 +3206,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches1("TYPE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+ else if (TailMatches1("VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
COMPLETE_WITH_CONST("TO");
else
@@ -3324,7 +3360,7 @@ psql_completion(const char *text, int start, int end)
/* PREPARE xx AS */
else if (Matches3("PREPARE", MatchAny, "AS"))
- COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
+ COMPLETE_WITH_LIST5("SELECT", "UPDATE", "INSERT", "DELETE FROM", "LET");
/*
* PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -3547,6 +3583,14 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
COMPLETE_WITH_CONST("=");
+/* LET --- can be inside EXPLAIN, PREPARE etc */
+ /* If prev. word is LET suggest a list of variables */
+ else if (TailMatches1("LET"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ /* Complete LET <variable> with "=" */
+ else if (TailMatches2("LET", MatchAny))
+ COMPLETE_WITH_CONST("=");
+
/* USER MAPPING */
else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 46c271a46c..3e38a05e55 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -180,7 +180,8 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_VARIABLE /* pg_variable */
} ObjectClass;
#define LAST_OCLASS OCLASS_TRANSFORM
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 24915824ca..dae80c20a8 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -360,4 +360,10 @@ DECLARE_UNIQUE_INDEX(pg_subscription_subname_index, 6115, on pg_subscription usi
DECLARE_UNIQUE_INDEX(pg_subscription_rel_srrelid_srsubid_index, 6117, on pg_subscription_rel using btree(srrelid oid_ops, srsubid oid_ops));
#define SubscriptionRelSrrelidSrsubidIndexId 6117
+DECLARE_UNIQUE_INDEX(pg_variable_oid_index, 4288, on pg_variable using btree(oid oid_ops));
+#define VariableObjectIndexId 4288
+
+DECLARE_UNIQUE_INDEX(pg_variable_varname_nsp_index, 4289, on pg_variable using btree(varname name_ops, varnamespace oid_ops));
+#define VariableNameNspIndexId 4289
+
#endif /* INDEXING_H */
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 7991de5e21..75068d7e92 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -75,10 +75,13 @@ extern Oid RangeVarGetAndCheckCreationNamespace(RangeVar *newRelation,
extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
+extern bool VariableIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
+extern bool VariableIsVisible(Oid varid);
+
extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
bool expand_variadic,
@@ -145,6 +148,10 @@ extern void SetTempNamespaceState(Oid tempNamespaceId,
Oid tempToastNamespaceId);
extern void ResetTempTableNamespace(void);
+extern List *NamesFromList(List *names);
+extern Oid lookup_variable(const char *nspname, const char *varname, bool missing_ok);
+extern Oid identify_variable(List *names, char **attrname, bool *not_uniq);
+
extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
extern OverrideSearchPath *CopyOverrideSearchPath(OverrideSearchPath *path);
extern bool OverrideSearchPathMatchesCurrent(OverrideSearchPath *path);
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index d0410f5586..56deef1a45 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -57,6 +57,7 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a14651010f..61cbe65805 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5961,6 +5961,9 @@
proname => 'pg_collation_is_visible', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid',
prosrc => 'pg_collation_is_visible' },
+{ oid => '4187', descr => 'is schema variable visible in search path?',
+ proname => 'pg_variable_is_visible', procost => '10', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid', prosrc => 'pg_variable_is_visible' },
{ oid => '2854', descr => 'get OID of current session\'s temp schema, if any',
proname => 'pg_my_temp_schema', provolatile => 's', proparallel => 'r',
diff --git a/src/include/catalog/pg_variable.h b/src/include/catalog/pg_variable.h
new file mode 100644
index 0000000000..34f4c34202
--- /dev/null
+++ b/src/include/catalog/pg_variable.h
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.h
+ * definition of schema variables system catalog (pg_variables)
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_variable.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VARIABLE_H
+#define PG_VARIABLE_H
+
+#include "catalog/genbki.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable_d.h"
+#include "utils/acl.h"
+
+/* ----------------
+ * pg_variable definition. cpp turns this into
+ * typedef struct FormData_pg_variable
+ * ----------------
+ */
+CATALOG(pg_variable,4287,VariableRelationId)
+{
+ NameData varname; /* variable name */
+ Oid varnamespace; /* OID of namespace containing variable class */
+ Oid vartype; /* OID of entry in pg_type for variable's type */
+ int32 vartypmod; /* typmode for variable's type */
+ Oid varowner; /* class owner */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+
+ /* list of expression trees for variable default (NULL if none) */
+ pg_node_tree vardefexpr BKI_DEFAULT(_null_);
+
+ aclitem varacl[1] BKI_DEFAULT(_null_); /* access permissions */
+
+#endif
+} FormData_pg_variable;
+
+/* ----------------
+ * Form_pg_variable corresponds to a pointer to a tuple with
+ * the format of pg_variable relation.
+ * ----------------
+ */
+typedef FormData_pg_variable *Form_pg_variable;
+
+typedef struct Variable
+{
+ Oid oid;
+ char *name;
+ Oid namespace;
+ Oid typid;
+ int32 typmod;
+ Oid owner;
+ Node *defexpr;
+ Acl *acl;
+} Variable;
+
+/* returns fields from pg_variable table */
+extern char *get_schema_variable_name(Oid varid);
+extern void get_schema_variable_type_typmod(Oid varid, Oid *typid, int32 *typmod);
+
+/* returns name of variable based on current search path */
+extern char *schema_variable_get_name(Oid varid);
+
+extern Variable *GetVariable(Oid varid, bool missing_ok);
+extern ObjectAddress VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Node *varDefexpr,
+ bool if_not_exists);
+
+
+#endif /* PG_VARIABLE_H */
diff --git a/src/include/commands/schemavariable.h b/src/include/commands/schemavariable.h
new file mode 100644
index 0000000000..dd3239b236
--- /dev/null
+++ b/src/include/commands/schemavariable.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavariable.h
+ * prototypes for schemavariable.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/schemavariable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SCHEMAVARIABLE_H
+#define SCHEMAVARIABLE_H
+
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable.h"
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "utils/queryenvironment.h"
+
+extern char *VariableGetName(Variable *var);
+
+extern void ResetSchemaVariableCache(void);
+
+extern void RemoveVariableById(Oid varid);
+extern ObjectAddress DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt);
+
+extern Datum GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid);
+extern void SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod);
+
+extern void doLetStmt(PlannedStmt *pstmt, ParamListInfo params, QueryEnvironment *queryEnv, const char *queryString);
+
+#endif
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77616..cca30f275b 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -344,11 +345,11 @@ typedef struct ExprEvalStep
TupleDesc argdesc;
} nulltest_row;
- /* for EEOP_PARAM_EXEC/EXTERN */
+ /* for EEOP_PARAM_EXEC/EXTERN/VARIABLE */
struct
{
- int paramid; /* numeric ID for parameter */
- Oid paramtype; /* OID of parameter's datatype */
+ int paramid; /* numeric ID for parameter */
+ Oid paramtype; /* OID of parameter's datatype */
} param;
/* for EEOP_PARAM_CALLBACK */
diff --git a/src/include/executor/svariableReceiver.h b/src/include/executor/svariableReceiver.h
new file mode 100644
index 0000000000..8c8117701f
--- /dev/null
+++ b/src/include/executor/svariableReceiver.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.h
+ * prototypes for svariableReceiver.c
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/svariableReceiver.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SVARIABLE_RECEIVER_H
+#define SVARIABLE_RECEIVER_H
+
+#include "tcop/dest.h"
+
+
+extern DestReceiver *CreateVariableDestReceiver(void);
+
+extern void SetVariableDestReceiverParams(DestReceiver *self, Oid varid);
+
+#endif /* SVARIABLE_RECEIVER_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 018f50bbb7..08b4b2c2f2 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -564,6 +564,8 @@ typedef struct EState
/* The per-query shared memory area to use for parallel execution. */
struct dsa_area *es_query_dsa;
+ int es_result_variable; /* Oid of target variable */
+
/*
* JIT information. es_jit_flags indicates whether JIT should be performed
* and with which options. es_jit is created on-demand when JITing is
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 697d3d7a5f..dd7fd8ed42 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -348,6 +348,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSchemaVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
@@ -419,6 +420,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
+ T_LetStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -663,6 +665,7 @@ typedef enum CmdType
CMD_DELETE,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
+ CMD_PLAN_UTILITY, /* only let stmt now, requires planning */
CMD_NOTHING /* dummy command for instead nothing rules
* with qual */
} CmdType;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07ab1a3dde..2d4a3cb1b6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -84,7 +84,9 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
-#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_READ (1<<12) /* for variables */
+#define ACL_WRITE (1<<13) /* for variables */
+#define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -121,6 +123,7 @@ typedef struct Query
int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */
+ int resultVariable; /* Oid of target variable or 0 */
bool hasAggs; /* has aggregates in tlist or havingQual */
bool hasWindowFuncs; /* has window functions in tlist */
@@ -1505,6 +1508,18 @@ typedef struct UpdateStmt
WithClause *withClause; /* WITH clause */
} UpdateStmt;
+/* ----------------------
+ * Let Statement
+ * ----------------------
+ */
+typedef struct LetStmt
+{
+ NodeTag type;
+ List *target; /* target variable */
+ Node *selectStmt; /* source expression */
+ int location;
+} LetStmt;
+
/* ----------------------
* Select Statement
*
@@ -1682,6 +1697,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -2497,6 +2513,19 @@ typedef struct AlterSeqStmt
bool missing_ok; /* skip error if a role is missing? */
} AlterSeqStmt;
+/* ----------------------
+ * {Create|Alter} VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSchemaVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the type of variable */
+ Node *defexpr; /* default expression */
+ bool if_not_exists; /* do nothing if it already exists */
+} CreateSchemaVarStmt;
+
/* ----------------------
* Create {Aggregate|Operator|Type} Statement
* ----------------------
@@ -3238,7 +3267,8 @@ typedef enum DiscardMode
DISCARD_ALL,
DISCARD_PLANS,
DISCARD_SEQUENCES,
- DISCARD_TEMP
+ DISCARD_TEMP,
+ DISCARD_VARIABLES
} DiscardMode;
typedef struct DiscardStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd03a..2588f1455f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -43,7 +43,7 @@ typedef struct PlannedStmt
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete|utility */
+ CmdType commandType; /* select|let|insert|update|delete|utility */
uint64 queryId; /* query identifier (copied from Query) */
@@ -81,6 +81,9 @@ typedef struct PlannedStmt
*/
List *rootResultRelations;
+ /* Oid of target variable for LET command */
+ Oid resultVariable;
+
List *subplans; /* Plan trees for SubPlan expressions; note
* that some could be NULL */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..b366471940 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -229,13 +229,17 @@ typedef struct Const
* of the `paramid' field contain the SubLink's subLinkId, and
* the low-order 16 bits contain the column number. (This type
* of Param is also converted to PARAM_EXEC during planning.)
+ *
+ * PARAM_SCHEMA_VARIABLE: The parameter is a access to schema variable
+ * paramid holds varid.
*/
typedef enum ParamKind
{
PARAM_EXTERN,
PARAM_EXEC,
PARAM_SUBLINK,
- PARAM_MULTIEXPR
+ PARAM_MULTIEXPR,
+ PARAM_SCHEMA_VARIABLE
} ParamKind;
typedef struct Param
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db40147b..d3ed3f4d0f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -231,6 +231,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("let", LET, UNRESERVED_KEYWORD)
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -434,6 +435,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 0230543810..f7c2e67f33 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -69,7 +69,9 @@ typedef enum ParseExprKind
EXPR_KIND_TRIGGER_WHEN, /* WHEN condition in CREATE TRIGGER */
EXPR_KIND_POLICY, /* USING or WITH CHECK expr in policy */
EXPR_KIND_PARTITION_EXPRESSION, /* PARTITION BY expression */
- EXPR_KIND_CALL_ARGUMENT /* procedure argument in CALL */
+ EXPR_KIND_CALL_ARGUMENT, /* procedure argument in CALL */
+ EXPR_KIND_VARIABLE_DEFAULT, /* default value for schema variable */
+ EXPR_KIND_LET /* LET assignment (should be same like UPDATE) */
} ParseExprKind;
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index ec6e0c102f..1ee199ed8f 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -32,6 +32,16 @@ extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
int attrno,
List *indirection,
int location);
+extern Node *transformAssignmentIndirection(ParseState *pstate,
+ Node *basenode,
+ const char *targetName,
+ bool targetIsArray,
+ Oid targetTypeId,
+ int32 targetTypMod,
+ Oid targetCollation,
+ ListCell *indirection,
+ Node *rhs,
+ int location);
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
char *colname, int attrno,
List *indirection,
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 82f0f2e741..c49b653555 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -96,7 +96,8 @@ typedef enum
DestCopyOut, /* results sent to COPY TO code */
DestSQLFunction, /* results sent to SQL-language func mgr */
DestTransientRel, /* results sent to transient relation */
- DestTupleQueue /* results sent to tuple queue */
+ DestTupleQueue, /* results sent to tuple queue */
+ DestVariable /* results sents to schema variable */
} CommandDest;
/* ----------------
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f4d4be8d0d..c624d8dd0b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -147,9 +147,11 @@ typedef ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
+#define ACL_READ_CHR 'S' /* 'R' is occupated by old RULE priv */
+#define ACL_WRITE_CHR 'W'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcSW"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -166,6 +168,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_READ|ACL_WRITE)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -253,6 +256,8 @@ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_variable_aclmask(Oid var_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode);
@@ -269,6 +274,7 @@ extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
+extern AclResult pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, ObjectType objtype,
const char *objectname);
@@ -305,6 +311,7 @@ extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool pg_publication_ownercheck(Oid pub_oid, Oid roleid);
extern bool pg_subscription_ownercheck(Oid sub_oid, Oid roleid);
extern bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid);
+extern bool pg_variable_ownercheck(Oid stat_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index e55ea4035b..cb3f4aaca9 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -122,6 +122,7 @@ extern bool get_func_leakproof(Oid funcid);
extern float4 get_func_cost(Oid funcid);
extern float4 get_func_rows(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
+extern Oid get_varname_varid(const char *varname, Oid varnamespace);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4f333586ee..453699be3c 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -107,9 +107,11 @@ enum SysCacheIdentifier
TYPENAMENSP,
TYPEOID,
USERMAPPINGOID,
- USERMAPPINGUSERSERVER
+ USERMAPPINGUSERSERVER,
+ VARIABLENAMENSP,
+ VARIABLEOID
-#define SysCacheSize (USERMAPPINGUSERSERVER + 1)
+#define SysCacheSize (VARIABLEOID + 1)
};
extern void InitCatalogCache(void);
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b500..48286f8e1a 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -105,5 +105,7 @@ ORDER BY 1, 2;
pg_index | indpred | pg_node_tree
pg_largeobject | data | bytea
pg_largeobject_metadata | lomacl | aclitem[]
-(11 rows)
+ pg_variable | varacl | aclitem[]
+ pg_variable | vardefexpr | pg_node_tree
+(13 rows)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 0aa5357917..848b041a4b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -163,6 +163,7 @@ pg_ts_parser|t
pg_ts_template|t
pg_type|t
pg_user_mapping|t
+pg_variable|t
point_tbl|t
polygon_tbl|t
quad_box_tbl|t
diff --git a/src/test/regress/expected/schema_variables.out b/src/test/regress/expected/schema_variables.out
new file mode 100644
index 0000000000..f2017b8da9
--- /dev/null
+++ b/src/test/regress/expected/schema_variables.out
@@ -0,0 +1,306 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+DROP VARIABLE var1, var2;
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT var1;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT READ ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT var1;
+ var1
+------
+
+(1 row)
+
+SET ROLE TO DEFAULT;
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to work
+LET var1 = 333;
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var1;
+ERROR: permission denied for schema variable var1
+-- should to work;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: ((g.v)::numeric = var1)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var1;
+SELECT * FROM schema_var_view;
+ var1
+------
+ 333
+(1 row)
+
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ var1
+------
+
+(1 row)
+
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+----------------------------
+ Result
+ Output: 3.14159265358979
+(2 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+SELECT inc(1);
+ inc
+-----
+ 1
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 2
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 3
+(1 row)
+
+SELECT inc(1) FROM generate_series(1,10);
+ inc
+-----
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+(10 rows)
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+-- composite variables
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+\d v1
+\d v2
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+SELECT v1;
+ v1
+------------
+ (1,2,3.14)
+(1 row)
+
+SELECT v2;
+ v2
+---------------
+ (10,20,31.40)
+(1 row)
+
+SELECT (v1).*;
+ x | y | z
+---+---+------
+ 1 | 2 | 3.14
+(1 row)
+
+SELECT (v2).*;
+ x | y | z
+----+----+-------
+ 10 | 20 | 31.40
+(1 row)
+
+SELECT v1.x + v1.z;
+ ?column?
+----------
+ 4.14
+(1 row)
+
+SELECT v2.x + v2.z;
+ ?column?
+----------
+ 41.40
+(1 row)
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+SELECT v2.x;
+ERROR: permission denied for schema variable v2
+SET ROLE TO DEFAULT;
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+DROP ROLE var_test_role;
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+ relname
+----------
+ pg_class
+(1 row)
+
+-- should to fail
+SELECT varx.xxx;
+ERROR: type text is not composite
+-- variables can be updated under RO transaction
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+SELECT varx;
+ varx
+-------
+ hello
+(1 row)
+
+DROP VARIABLE varx;
+CREATE TYPE t1 AS (a int, b numeric, c text);
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+ v1
+----------------------------
+ (1,3.14159265358979,hello)
+(1 row)
+
+LET v1.b = 10.2222;
+SELECT v1;
+ v1
+-------------------
+ (1,10.2222,hello)
+(1 row)
+
+-- should to fail
+LET v1.x = 10;
+ERROR: cannot assign to field "x" of column "x" because there is no such column in data type t1
+LINE 1: LET v1.x = 10;
+ ^
+DROP VARIABLE v1;
+DROP TYPE t1;
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+ va1
+------------
+ {10.1,2.1}
+(1 row)
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+ va2
+--------------------
+ (10.2,"{0.0,0.0}")
+(1 row)
+
+LET va2.b[1] = 10.3;
+SELECT va2;
+ va2
+---------------------
+ (10.2,"{10.3,0.0}")
+(1 row)
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+ v1
+------------------
+ 6.28318530717958
+(1 row)
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+ v2
+--------------------------
+ (3.14159265358979,Hello)
+(1 row)
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+ERROR: cannot drop type t2 because other objects depend on it
+DETAIL: schema variable v2 depends on type t2
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 16f979c8d9..9bf379b87b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml schema_variables
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/schema_variables.out b/src/test/regress/schema_variables.out
new file mode 100644
index 0000000000..b485cc0995
--- /dev/null
+++ b/src/test/regress/schema_variables.out
@@ -0,0 +1,313 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+DROP VARIABLE var1, var2;
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT var1;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT SELECT ON VARIABLE var1 TO var_test_role;
+ERROR: invalid privilege type SELECT for schema variable
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT var1;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT UPDATE ON VARIABLE var1 TO var_test_role;
+ERROR: invalid privilege type UPDATE for schema variable
+SET ROLE TO var_test_role;
+-- should to work
+LET var1 = 333;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1;
+$$ LANGUAGE sql SECURITY DEFINER;
+ERROR: return type mismatch in function declared to return integer
+DETAIL: Actual return type is numeric.
+CONTEXT: SQL function "secure_var"
+SELECT secure_var();
+ERROR: function secure_var() does not exist
+LINE 1: SELECT secure_var();
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var1;
+ERROR: permission denied for schema variable var1
+-- should to work;
+SELECT secure_var();
+ERROR: function secure_var() does not exist
+LINE 1: SELECT secure_var();
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: ((g.v)::numeric = $46404)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var1;
+SELECT * FROM schema_var_view;
+ERROR: the content of variable is not valid
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ERROR: the content of variable is not valid
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+----------------------------
+ Result
+ Output: 3.14159265358979
+(2 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+NOTICE: InvalidateSchemaVarCacheCallback
+SELECT inc(1);
+ERROR: the content of variable is not valid
+CONTEXT: SQL statement "LET public.var3 = COALESCE(public.var3 + $1, $1)"
+PL/pgSQL function inc(integer) line 3 at SQL statement
+SELECT inc(1);
+ERROR: the content of variable is not valid
+CONTEXT: SQL statement "LET public.var3 = COALESCE(public.var3 + $1, $1)"
+PL/pgSQL function inc(integer) line 3 at SQL statement
+SELECT inc(1);
+ERROR: the content of variable is not valid
+CONTEXT: SQL statement "LET public.var3 = COALESCE(public.var3 + $1, $1)"
+PL/pgSQL function inc(integer) line 3 at SQL statement
+SELECT inc(1) FROM generate_series(1,10);
+ERROR: the content of variable is not valid
+CONTEXT: SQL statement "LET public.var3 = COALESCE(public.var3 + $1, $1)"
+PL/pgSQL function inc(integer) line 3 at SQL statement
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP VARIABLE var1 CASCADE;
+NOTICE: InvalidateSchemaVarCacheCallback
+DROP VARIABLE var3 CASCADE;
+NOTICE: InvalidateSchemaVarCacheCallback
+NOTICE: InvalidateSchemaVarCacheCallback
+-- composite variables
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+NOTICE: InvalidateSchemaVarCacheCallback
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS (x int, y int, z numeric(10,2));
+NOTICE: InvalidateSchemaVarCacheCallback
+ERROR: syntax error at or near "("
+LINE 1: CREATE VARIABLE v2 AS (x int, y int, z numeric(10,2));
+ ^
+\d v1
+\d v2
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+ERROR: schema variable "v2" doesn't exists
+LINE 1: LET v2 = (10,20,3.14*10);
+ ^
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+SELECT v1;
+ v1
+------------
+ (1,2,3.14)
+(1 row)
+
+SELECT v2;
+ERROR: column "v2" does not exist
+LINE 1: SELECT v2;
+ ^
+SELECT (v1).*;
+ x | y | z
+---+---+------
+ 1 | 2 | 3.14
+(1 row)
+
+SELECT (v2).*;
+ERROR: column "v2" does not exist
+LINE 1: SELECT (v2).*;
+ ^
+SELECT v1.x + v1.z;
+ ?column?
+----------
+ 4.14
+(1 row)
+
+SELECT v2.x + v2.z;
+ERROR: missing FROM-clause entry for table "v2"
+LINE 1: SELECT v2.x + v2.z;
+ ^
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+SELECT v2.x;
+ERROR: missing FROM-clause entry for table "v2"
+LINE 1: SELECT v2.x;
+ ^
+SET ROLE TO DEFAULT;
+DROP VARIABLE v1;
+NOTICE: InvalidateSchemaVarCacheCallback
+DROP VARIABLE v2;
+NOTICE: InvalidateSchemaVarCacheCallback
+ERROR: variable "v2" does not exist
+DROP VARIABLE v3;
+ERROR: variable "v3" does not exist
+DROP ROLE var_test_role;
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+NOTICE: InvalidateSchemaVarCacheCallback
+ relname
+----------
+ pg_class
+(1 row)
+
+-- should to fail
+SELECT varx.xxx;
+ERROR: type text is not composite
+-- variables can be updated under RO transaction
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+WARNING: unrecognized commandType: 6
+COMMIT;
+SELECT varx;
+ varx
+-------
+ hello
+(1 row)
+
+DROP VARIABLE varx;
+NOTICE: InvalidateSchemaVarCacheCallback
+CREATE VARIABLE v1 AS (a int, b numeric, c text);
+NOTICE: InvalidateSchemaVarCacheCallback
+ERROR: syntax error at or near "("
+LINE 1: CREATE VARIABLE v1 AS (a int, b numeric, c text);
+ ^
+LET v1 = (1, pi(), 'hello');
+ERROR: schema variable "v1" doesn't exists
+LINE 1: LET v1 = (1, pi(), 'hello');
+ ^
+SELECT v1;
+ERROR: column "v1" does not exist
+LINE 1: SELECT v1;
+ ^
+LET v1.b = 10.2222;
+ERROR: schema variable "v1.b" doesn't exists
+LINE 1: LET v1.b = 10.2222;
+ ^
+SELECT v1;
+ERROR: column "v1" does not exist
+LINE 1: SELECT v1;
+ ^
+-- should to fail
+LET v1.x = 10;
+ERROR: schema variable "v1.x" doesn't exists
+LINE 1: LET v1.x = 10;
+ ^
+DROP VARIABLE v1;
+ERROR: variable "v1" does not exist
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+NOTICE: InvalidateSchemaVarCacheCallback
+LET va1[1] = 10.1;
+SELECT va1;
+ va1
+------------
+ {10.1,2.1}
+(1 row)
+
+CREATE VARIABLE va2 AS (a numeric, b numeric[]);
+ERROR: syntax error at or near "("
+LINE 1: CREATE VARIABLE va2 AS (a numeric, b numeric[]);
+ ^
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+ERROR: schema variable "va2" doesn't exists
+LINE 1: LET va2 = (10.1, ARRAY[0.0, 0.0]);
+ ^
+LET va2.a = 10.2;
+ERROR: schema variable "va2.a" doesn't exists
+LINE 1: LET va2.a = 10.2;
+ ^
+SELECT va2;
+ERROR: column "va2" does not exist
+LINE 1: SELECT va2;
+ ^
+LET va2.b[1] = 10.3;
+ERROR: schema variable "va2.b" doesn't exists
+LINE 1: LET va2.b[1] = 10.3;
+ ^
+SELECT va2;
+ERROR: column "va2" does not exist
+LINE 1: SELECT va2;
+ ^
+DROP VARIABLE va1;
+NOTICE: InvalidateSchemaVarCacheCallback
+DROP VARIABLE va2;
+NOTICE: InvalidateSchemaVarCacheCallback
+ERROR: variable "va2" does not exist
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+NOTICE: InvalidateSchemaVarCacheCallback
+SELECT v1;
+ v1
+------------------
+ 6.28318530717958
+(1 row)
+
+CREATE VARIABLE v2 AS (a numeric, b text DEFAULT 'hello');
+ERROR: syntax error at or near "("
+LINE 1: CREATE VARIABLE v2 AS (a numeric, b text DEFAULT 'hello');
+ ^
+LET public.v2.a = pi();
+ERROR: schema variable "public.v2.a" doesn't exists
+LINE 1: LET public.v2.a = pi();
+ ^
+SELECT v2;
+ERROR: column "v2" does not exist
+LINE 1: SELECT v2;
+ ^
+DROP VARIABLE v1;
+NOTICE: InvalidateSchemaVarCacheCallback
+DROP VARIABLE v2;
+NOTICE: InvalidateSchemaVarCacheCallback
+ERROR: variable "v2" does not exist
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 42632be675..42bf4ecb3f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -191,3 +191,4 @@ test: partition_aggregate
test: event_trigger
test: fast_default
test: stats
+test: schema_variables
diff --git a/src/test/regress/sql/schema_variables.sql b/src/test/regress/sql/schema_variables.sql
new file mode 100644
index 0000000000..619b6ee4c0
--- /dev/null
+++ b/src/test/regress/sql/schema_variables.sql
@@ -0,0 +1,213 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+
+DROP VARIABLE var1, var2;
+
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+
+CREATE ROLE var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT READ ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+-- should to work
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to work
+LET var1 = 333;
+
+SET ROLE TO DEFAULT;
+
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+
+SELECT secure_var();
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT public.var1;
+
+-- should to work;
+SELECT secure_var();
+
+SET ROLE TO DEFAULT;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+
+CREATE VIEW schema_var_view AS SELECT var1;
+
+SELECT * FROM schema_var_view;
+
+\c -
+
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+
+LET var1 = pi();
+
+SELECT var1;
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+
+EXECUTE var_pp(100, 1.23456);
+
+SELECT var1;
+
+CREATE VARIABLE var3 AS int;
+
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT inc(1);
+SELECT inc(1);
+SELECT inc(1);
+
+SELECT inc(1) FROM generate_series(1,10);
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+LET var3 = 0;
+
+SET ROLE TO DEFAULT;
+
+DROP VIEW schema_var_view;
+
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+
+-- composite variables
+
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+
+\d v1
+\d v2
+
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+
+SELECT v1;
+SELECT v2;
+SELECT (v1).*;
+SELECT (v2).*;
+
+SELECT v1.x + v1.z;
+SELECT v2.x + v2.z;
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+
+SELECT v2.x;
+
+SET ROLE TO DEFAULT;
+
+
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+DROP ROLE var_test_role;
+
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+
+-- should to fail
+SELECT varx.xxx;
+
+
+-- variables can be updated under RO transaction
+
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+
+SELECT varx;
+
+DROP VARIABLE varx;
+
+CREATE TYPE t1 AS (a int, b numeric, c text);
+
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+LET v1.b = 10.2222;
+SELECT v1;
+
+-- should to fail
+LET v1.x = 10;
+
+DROP VARIABLE v1;
+DROP TYPE t1;
+
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+LET va2.b[1] = 10.3;
+SELECT va2;
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-08-08 20:35 ` Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-08-08 20:35 UTC (permalink / raw)
To: Gilles Darold <gilles.darold@dalibo.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
removed forgotten file
Regards
Pavel
Attachments:
[text/x-patch] schema-variables-180808-02.patch (181.4K, ../../CAFj8pRCN84iKuvP9Mh41=jOMSOCP89z8VvK0sWBnKTptBYPA4w@mail.gmail.com/3-schema-variables-180808-02.patch)
download | inline diff:
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3bb48d4ccf..0a7a932ef5 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -359,6 +359,11 @@
<entry><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link></entry>
<entry>mappings of users to foreign servers</entry>
</row>
+
+ <row>
+ <entry><link linkend="catalog-pg-variable"><structname>pg_variable</structname></link></entry>
+ <entry>schema variables</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -11255,7 +11260,6 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
-
<sect1 id="view-pg-views">
<title><structname>pg_views</structname></title>
@@ -11311,4 +11315,104 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
+ <sect1 id="catalog-pg-variable">
+ <title><structname>pg_variable</structname></title>
+
+ <indexterm zone="catalog-pg-variable">
+ <primary>pg_variable</primary>
+ </indexterm>
+
+ <para>
+ The table <structname>pg_variable</structname> holds metadata
+ of schema variables.
+ </para>
+
+ <table>
+ <title><structname>pg_views</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><structfield>oid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry></entry>
+ <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Name of the schema variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the namespace that contains this variable
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartype</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the data type of this variable.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartypmod</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ <structfield>vartypmod</structfield> records type-specific data
+ supplied at table creation time (for example, the maximum
+ length of a <type>varchar</type> column). It is passed to
+ type-specific input functions and length coercion functions.
+ The value will generally be -1 for types that do not need <structfield>vartypmod</structfield>.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>varowner</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>Owner of the variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>vardefexpr</structfield></entry>
+ <entry><type>pg_node_tree</type></entry>
+ <entry></entry>
+ <entry>The internal representation of the variable default value</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>
+ Access privileges; see
+ <xref linkend="sql-grant"/> and
+ <xref linkend="sql-revoke"/>
+ for details
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..f5aaf60233 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -99,6 +99,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createType SYSTEM "create_type.sgml">
<!ENTITY createUser SYSTEM "create_user.sgml">
<!ENTITY createUserMapping SYSTEM "create_user_mapping.sgml">
+<!ENTITY createVariable SYSTEM "create_variable.sgml">
<!ENTITY createView SYSTEM "create_view.sgml">
<!ENTITY deallocate SYSTEM "deallocate.sgml">
<!ENTITY declare SYSTEM "declare.sgml">
@@ -148,6 +149,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropUser SYSTEM "drop_user.sgml">
<!ENTITY dropUserMapping SYSTEM "drop_user_mapping.sgml">
<!ENTITY dropView SYSTEM "drop_view.sgml">
+<!ENTITY dropVariable SYSTEM "drop_variable.sgml">
<!ENTITY end SYSTEM "end.sgml">
<!ENTITY execute SYSTEM "execute.sgml">
<!ENTITY explain SYSTEM "explain.sgml">
@@ -155,6 +157,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY grant SYSTEM "grant.sgml">
<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
<!ENTITY insert SYSTEM "insert.sgml">
+<!ENTITY let SYSTEM "let.sgml">
<!ENTITY listen SYSTEM "listen.sgml">
<!ENTITY load SYSTEM "load.sgml">
<!ENTITY lock SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
new file mode 100644
index 0000000000..c8070051f5
--- /dev/null
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -0,0 +1,133 @@
+<!--
+doc/src/sgml/ref/create_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-createvariable">
+ <indexterm zone="sql-createvariable">
+ <primary>CREATE VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE VARIABLE</refname>
+ <refpurpose>define a new permissioned typed schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ]
+</synopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> creates a new schema variable.
+ These variables are scalar typed, non-transactional, and, like relations,
+ exist within a schema with access controlled via
+ <command>GRANT</command> and <command>REVOKE</command>.
+ </para>
+
+ <para>
+ The value of a schema variable is session-local. Retrieving
+ a variable's value will return NULL unless its value has been set
+ to something else in the current session.
+ </para>
+
+ <para>
+ Retrieval is done via the <function>get_schema_variable</function>dunxrion or the SQL
+ command <command>SELECT</command>. Setting of values is done via the
+ <function>set_schema_variable</function> function or the SQL command
+ <command>LET</command>.
+ Notably, while schema variables are in many ways a kind of table you cannot use
+ <command>UPDATE</command> on them.
+ </para>
+
+ <para>
+ For purposes of name uniqueness relation-like objects (e.g., tables, indexes)
+ within the same schema are considered. i.e., you cannot give a table and a
+ schema variable the same name. This is a consequence of them being treated
+ like relations for purposes of <command>SELECT</command>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF NOT EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the name already exists. A notice is issued in this case.
+ Note that type of the variable is not considered, nor could it be since the namespace
+ searched contains non-variable objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the data type of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Use <command>DROP VARIABLE</command> to remove a variable.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Create an integer variable <literal>var1</literal>:
+<programlisting>
+CREATE VARIABLE var1 AS integer;
+SELECT var1;
+</programlisting>
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> is a PostgreSQL feature.
+ <!-- The choice of wording here seems to be left to personal preference... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index 6b909b7232..d83ad811fd 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
+DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES }
</synopsis>
</refsynopsisdiv>
@@ -75,6 +75,17 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>VARIABLES</literal></term>
+ <listitem>
+ <para>
+ Resets the value of all schema variables. When variables
+ will be used later, then will be initialized again to
+ NULL or default value.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
new file mode 100644
index 0000000000..06130fd510
--- /dev/null
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -0,0 +1,92 @@
+<!--
+doc/src/sgml/ref/drop_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropvariable">
+ <indexterm zone="sql-dropvariable">
+ <primary>DROP VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP VARIABLE</refname>
+ <refpurpose>remove a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP VARIABLE</command> removes a schema variable.
+ A variable can only be dropped by its owner or a superuser.
+ <!-- this would suggest that we need an alter variable owner to command -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the variable does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To remove the schema variable <literal>var1</literal>:
+
+<programlisting>
+DROP VARIABLE var1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ <!-- create variable is a "PostgreSQL feature",
+ this is a "proprietary PostgreSQL command" ... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index ff64c7a3ba..a83920a7a1 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -79,6 +79,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE <replaceable>type_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
[ GROUP ] <replaceable class="parameter">role_name</replaceable>
@@ -167,6 +171,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
foreign servers,
large objects,
schemas,
+ schema variable
or tablespaces.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
@@ -385,6 +390,24 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>READ</literal></term>
+ <listitem>
+ <para>
+ Allows to read a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WRITE</literal></term>
+ <listitem>
+ <para>
+ Allows to set a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL PRIVILEGES</literal></term>
<listitem>
@@ -550,6 +573,8 @@ rolename=xxxx -- privileges granted to a role
C -- CREATE
c -- CONNECT
T -- TEMPORARY
+ S -- READ
+ w -- WRITE
arwdDxt -- ALL PRIVILEGES (for tables, varies for other objects)
* -- grant option for preceding privilege
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
new file mode 100644
index 0000000000..e8bf3f6dd4
--- /dev/null
+++ b/doc/src/sgml/ref/let.sgml
@@ -0,0 +1,90 @@
+<!--
+doc/src/sgml/ref/let.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-let">
+ <indexterm zone="sql-let">
+ <primary>LET</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>LET</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>LET</refname>
+ <refpurpose>change a schema variable's value</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable class="parameter">sql_expression</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ The <command>LET</command> command updates the specified schema variable' value.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>schema_variable</literal></term>
+ <listitem>
+ <para>
+ The name of schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sql expression</literal></term>
+ <listitem>
+ <para>
+ An SQL expression, the result is cast to the schema variable's type.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Example:
+<programlisting>
+CREATE VARIABLE myvar AS integer;
+LET myvar = 10;
+LET myvar = (SELECT sum(val) FROM tab);
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <!-- this feels like it needs to be more specific,
+ but I don't know enough to make it so -->
+ <literal>LET</literal> extends syntax defined in the SQL
+ standard. The standard knows <literal>SET</literal> command,
+ that is used for different purpouse in PostgreSQL.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 5317f8ccba..8435e05957 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -108,6 +108,12 @@ REVOKE [ GRANT OPTION FOR ]
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_name</replaceable> [, ...]
[ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { READ | WRITE } [, ...] | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..afcc69432d 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -127,6 +127,7 @@
&createType;
&createUser;
&createUserMapping;
+ &createVariable;
&createView;
&deallocate;
&declare;
@@ -175,6 +176,7 @@
&dropType;
&dropUser;
&dropUserMapping;
+ &dropVariable;
&dropView;
&end;
&execute;
@@ -183,6 +185,7 @@
&grant;
&importForeignSchema;
&insert;
+ &let;
&listen;
&load;
&lock;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 0865240f11..1f7c4d1223 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -19,7 +19,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_publication.o pg_range.o \
pg_db_role_setting.o pg_shdepend.o pg_subscription.o pg_type.o \
- storage.o toasting.o
+ pg_variable.o storage.o toasting.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
@@ -46,7 +46,7 @@ CATALOG_HEADERS := \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
- pg_subscription_rel.h
+ pg_subscription_rel.h pg_variable.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 578e4c6592..86917e15a8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -57,6 +57,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_transform.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
@@ -112,6 +113,7 @@ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
+static void ExecGrant_Variable(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
@@ -284,6 +286,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -507,6 +512,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -609,6 +618,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_VARIABLE:
+ ExecGrant_Variable(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -768,6 +780,16 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_VARIABLE:
+ foreach(cell, objnames)
+ {
+ RangeVar *varvar = (RangeVar *) lfirst(cell);
+ Oid relOid;
+
+ relOid = lookup_variable(varvar->schemaname, varvar->relname, false);
+ objects = lappend_oid(objects, relOid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -855,6 +877,31 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
heap_close(rel, AccessShareLock);
}
break;
+ case OBJECT_VARIABLE:
+ {
+ ScanKeyData key;
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&key,
+ Anum_pg_variable_varnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(namespaceId));
+
+ rel = heap_open(VariableRelationId, AccessShareLock);
+ scan = heap_beginscan_catalog(rel, 1, &key);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ }
+ break;
+
default:
/* should not happen */
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
@@ -1018,6 +1065,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1215,6 +1266,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1441,6 +1498,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -3266,6 +3326,129 @@ ExecGrant_Type(InternalGrant *istmt)
heap_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Variable(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_VARIABLE;
+
+ relation = heap_open(VariableRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid varId = lfirst_oid(cell);
+ Form_pg_variable pg_variable_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_variable];
+ bool nulls[Natts_pg_variable];
+ bool replaces[Natts_pg_variable];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for schema variables %u", varId);
+
+ pg_variable_tuple = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_variable_tuple->varowner;
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple, Anum_pg_variable_varacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_VARIABLE, ownerId);
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ oldmembers = NULL;
+ }
+ else
+ {
+ old_acl = DatumGetAclPCopy(aclDatum);
+ /* Get the roles mentioned in the existing ACL */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+ }
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ varId, grantorId, OBJECT_VARIABLE,
+ NameStr(pg_variable_tuple->varname),
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ */
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_variable_varacl - 1] = true;
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(varId, VariableRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(VariableRelationId, varId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3298,6 +3481,10 @@ string_to_privilege(const char *privname)
return ACL_CONNECT;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
+ if (strcmp(privname, "read") == 0)
+ return ACL_READ;
+ if (strcmp(privname, "write") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized privilege type \"%s\"", privname)));
@@ -3333,6 +3520,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3456,6 +3647,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3566,6 +3760,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3710,6 +3907,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
+ case OBJECT_VARIABLE:
+ return pg_variable_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objtype: %d",
(int) objtype);
@@ -4499,6 +4698,67 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a variable.
+ */
+AclMode
+pg_variable_aclmask(Oid var_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_variable varForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the type's tuple from pg_type
+ */
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable with OID %u does not exist",
+ var_oid)));
+ varForm = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Now get the type's owner and ACL from the tuple
+ */
+ ownerId = varForm->varowner;
+
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple,
+ Anum_pg_variable_varacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_VARIABLE, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+
/*
* Exported routine for checking a user's access privileges to a column
*
@@ -4744,6 +5004,18 @@ pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a variable
+ */
+AclResult
+pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
+{
+ if (pg_variable_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Ownership check for a relation (specified by OID).
*/
@@ -5361,6 +5633,33 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a schema variables (specified by OID).
+ */
+bool
+pg_variable_ownercheck(Oid db_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(db_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("variable with OID %u does not exist", db_oid)));
+
+ ownerId = ((Form_pg_variable) GETSTRUCT(tuple))->varowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+
/*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
@@ -5486,6 +5785,10 @@ get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
+ case OBJECT_VARIABLE:
+ defaclobjtype = DEFACLOBJ_VARIABLE;
+ break;
+
default:
return NULL;
}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 4f1d365357..782ddb1655 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -59,6 +59,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -67,6 +68,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
@@ -1280,6 +1282,10 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_VARIABLE:
+ RemoveVariableById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2537,6 +2543,9 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case VariableRelationId:
+ return OCLASS_VARIABLE;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 0f67a122ed..81aaf454a8 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -39,6 +39,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
@@ -755,6 +756,71 @@ RelationIsVisible(Oid relid)
return visible;
}
+/*
+ * VariableIsVisible
+ * Determine whether a variable (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified variable name".
+ */
+bool
+VariableIsVisible(Oid varid)
+{
+ HeapTuple vartup;
+ Form_pg_variable varform;
+ Oid varnamespace;
+ bool visible;
+
+ vartup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(vartup))
+ elog(ERROR, "cache lookup failed for schema variable %u", varid);
+ varform = (Form_pg_variable) GETSTRUCT(vartup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ varnamespace = varform->varnamespace;
+ if (varnamespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, varnamespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another relation of the same name earlier in the path. So
+ * we must do a slow check for conflicting relations.
+ */
+ char *varname = NameStr(varform->varname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == varnamespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (OidIsValid(get_varname_varid(varname, namespaceId)))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(vartup);
+
+ return visible;
+}
+
+
/*
* TypenameGetTypid
@@ -2776,6 +2842,202 @@ TSConfigIsVisible(Oid cfgid)
return visible;
}
+/*
+ * When we know a variable name, then we can find variable simply
+ */
+Oid
+lookup_variable(const char *nspname, const char *varname, bool missing_ok)
+{
+ Oid namespaceId;
+ Oid varoid = InvalidOid;
+ ListCell *l;
+
+ if (nspname)
+ {
+ namespaceId = LookupExplicitNamespace(nspname, missing_ok);
+ if (!OidIsValid(namespaceId))
+ return InvalidOid;
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+
+ if (OidIsValid(varoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(varoid) && !missing_ok)
+ {
+ if (nspname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\".\"%s\" does not exist",
+ nspname, varname)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\" does not exist",
+ varname)));
+ }
+
+ return varoid;
+}
+
+List *
+NamesFromList(List *names)
+{
+ ListCell *l;
+ List *result = NIL;
+
+ foreach(l, names)
+ {
+ Node *n = lfirst(l);
+
+ if (IsA(n, String))
+ {
+ result = lappend(result, n);
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * identify_variable
+ *
+ * Returns oid of not ambigonuous variable specified by qualified path
+ * or InvalidOid. When the path is ambigonuous, then not_uniq flag is
+ * is true.
+ */
+Oid
+identify_variable(List *names, char **attrname, bool *not_uniq)
+{
+ char *a = NULL;
+ char *b = NULL;
+ char *c = NULL;
+ char *d = NULL;
+ Oid varoid_without_attr;
+ Oid varoid_with_attr;
+
+ *not_uniq = false;
+
+ switch (list_length(names))
+ {
+ case 1:
+ a = strVal(linitial(names));
+ return lookup_variable(NULL, a, true);
+
+ case 2:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+
+ /*
+ * a.b can mean "schema"."variable" or "variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(a, b, true);
+ varoid_with_attr = lookup_variable(NULL, a, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = b;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 3:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+
+ /*
+ * a.b.c can mean "catalog"."schema"."variable" or "schema"."variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(b, c, true);
+ varoid_with_attr = lookup_variable(a, b, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = c;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 4:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+ d = strVal(lfourth(names));
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ *attrname = d;
+ return lookup_variable(b, c, true);
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(names))));
+ break;
+ }
+}
/*
* DeconstructQualifiedName
@@ -4416,3 +4678,14 @@ pg_is_other_temp_schema(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(isOtherTempNamespace(oid));
}
+
+Datum
+pg_variable_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ if (!SearchSysCacheExists1(VARIABLEOID, ObjectIdGetDatum(oid)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_BOOL(VariableIsVisible(oid));
+}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7db942dcba..cc3d415e61 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -58,6 +58,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -489,6 +490,18 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
OBJECT_STATISTIC_EXT,
true
+ },
+ {
+ VariableRelationId,
+ VariableObjectIndexId,
+ VARIABLEOID,
+ VARIABLENAMENSP,
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ Anum_pg_variable_varowner,
+ Anum_pg_variable_varacl,
+ OBJECT_VARIABLE,
+ true
}
};
@@ -714,6 +727,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_VARIABLE */
+ {
+ "schema variable", OBJECT_VARIABLE
}
};
@@ -739,6 +756,7 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
bool missing_ok);
static ObjectAddress get_object_address_type(ObjectType objtype,
TypeName *typename, bool missing_ok);
+static ObjectAddress get_object_address_variable(List *object, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object,
bool missing_ok);
static ObjectAddress get_object_address_opf_member(ObjectType objtype,
@@ -996,6 +1014,10 @@ get_object_address(ObjectType objtype, Node *object,
missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_VARIABLE:
+ address = get_object_address_variable(castNode(List, object), missing_ok);
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
@@ -1848,16 +1870,20 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type \"%c\"", objtype),
- errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
+ errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
DEFACLOBJ_RELATION,
DEFACLOBJ_SEQUENCE,
DEFACLOBJ_FUNCTION,
DEFACLOBJ_TYPE,
- DEFACLOBJ_NAMESPACE)));
+ DEFACLOBJ_NAMESPACE,
+ DEFACLOBJ_VARIABLE)));
}
/*
@@ -1942,6 +1968,24 @@ textarray_to_strvaluelist(ArrayType *arr)
return list;
}
+/*
+ * Find the ObjectAddress for a type or domain
+ */
+static ObjectAddress
+get_object_address_variable(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ char *nspname = NULL;
+ char *varname = NULL;
+
+ ObjectAddressSet(address, VariableRelationId, InvalidOid);
+
+ DeconstructQualifiedName(object, &nspname, &varname);
+ address.objectId = lookup_variable(nspname, varname, missing_ok);
+
+ return address;
+}
+
/*
* SQL-callable version of get_object_address
*/
@@ -2131,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_TABCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
+ case OBJECT_VARIABLE:
objnode = (Node *) name;
break;
case OBJECT_ACCESS_METHOD:
@@ -2415,6 +2460,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
break;
+ case OBJECT_VARIABLE:
+ if (!pg_variable_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
default:
elog(ERROR, "unrecognized object type: %d",
(int) objtype);
@@ -3157,6 +3207,32 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_VARIABLE:
+ {
+ char *nspname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ if (VariableIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ appendStringInfo(&buffer, _("schema variable %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(varform->varname)));
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_TSPARSER:
{
HeapTuple tup;
@@ -3422,6 +3498,16 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new schemas belonging to role %s"),
rolename);
break;
+ case DEFACLOBJ_VARIABLE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s"),
+ rolename);
+ break;
default:
/* shouldn't get here */
if (nspname)
@@ -4070,6 +4156,10 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_VARIABLE:
+ appendStringInfoString(&buffer, "schema variable");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4962,6 +5052,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on variables");
+ break;
}
if (objname)
@@ -5121,6 +5215,33 @@ getObjectIdentityParts(const ObjectAddress *object,
}
break;
+ case OCLASS_VARIABLE:
+ {
+ char *schema;
+ char *varname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ schema = get_namespace_name_or_temp(varform->varnamespace);
+ varname = NameStr(varform->varname);
+
+ appendStringInfo(&buffer, "%s",
+ quote_qualified_identifier(schema, varname));
+
+ if (objname)
+ *objname = list_make2(schema, varname);
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
diff --git a/src/backend/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c
new file mode 100644
index 0000000000..ff71f8bf6a
--- /dev/null
+++ b/src/backend/catalog/pg_variable.c
@@ -0,0 +1,305 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.c
+ * schema variables
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_variable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+
+#include "nodes/makefuncs.h"
+
+#include "storage/lmgr.h"
+
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * Returns name of schema variable. When variable is not on path,
+ * then the name is qualified.
+ */
+char *
+schema_variable_get_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+ char *nspname;
+ char *result;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ if (VariableIsVisible(varid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ result = quote_qualified_identifier(nspname, varname);
+
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/*
+ * Returns varname field of pg_variable
+ */
+char *
+get_schema_variable_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ ReleaseSysCache(tup);
+
+ return varname;
+}
+
+/*
+ * Returns type, typmod of schema variable
+ */
+void
+get_schema_variable_type_typmod(Oid varid, Oid *typid, int32 *typmod)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ *typid = varform->vartype;
+ *typmod = varform->vartypmod;
+
+ ReleaseSysCache(tup);
+
+ return;
+}
+
+/*
+ * Fetch all fields of schema variable from the syscache.
+ */
+Variable *
+GetVariable(Oid varid, bool missing_ok)
+{
+ HeapTuple tup;
+ Variable *var;
+ Form_pg_variable varform;
+ Datum aclDatum;
+ Datum defexprDatum;
+ bool isnull;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (missing_ok)
+ return NULL;
+
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+ }
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ var = (Variable *) palloc(sizeof(Variable));
+ var->oid = varid;
+ var->name = pstrdup(NameStr(varform->varname));
+ var->namespace = varform->varnamespace;
+ var->typid = varform->vartype;
+ var->typmod = varform->vartypmod;
+ var->owner = varform->varowner;
+
+ /* Get defexpr */
+ defexprDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_vardefexpr,
+ &isnull);
+
+ if (!isnull)
+ var->defexpr = stringToNode(TextDatumGetCString(defexprDatum));
+ else
+ var->defexpr = NULL;
+
+ /* Get varacl */
+ aclDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_varacl,
+ &isnull);
+ if (!isnull)
+ var->acl = DatumGetAclPCopy(aclDatum);
+ else
+ var->acl = NULL;
+
+ ReleaseSysCache(tup);
+
+ return var;
+}
+
+ObjectAddress
+VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Node *varDefexpr,
+ bool if_not_exists)
+{
+ Acl *varacl;
+ NameData varname;
+ bool nulls[Natts_pg_variable];
+ Datum values[Natts_pg_variable];
+ Relation rel;
+ HeapTuple tup,
+ oldtup;
+ TupleDesc tupdesc;
+ ObjectAddress myself,
+ referenced;
+ Oid retval;
+ int i;
+
+ for (i = 0; i < Natts_pg_variable; i++)
+ {
+ nulls[i] = false;
+ values[i] = (Datum) 0;
+ }
+
+ namestrcpy(&varname, varName);
+ values[Anum_pg_variable_varname - 1] = NameGetDatum(&varname);
+ values[Anum_pg_variable_varnamespace - 1] = ObjectIdGetDatum(varNamespace);
+ values[Anum_pg_variable_vartype - 1] = ObjectIdGetDatum(varType);
+ values[Anum_pg_variable_vartypmod - 1] = Int32GetDatum(varTypmod);
+ values[Anum_pg_variable_varowner - 1] = ObjectIdGetDatum(varOwner);
+ /* proacl will be determined later */
+
+ if (varDefexpr)
+ values[Anum_pg_variable_vardefexpr - 1] = CStringGetTextDatum(nodeToString(varDefexpr));
+ else
+ nulls[Anum_pg_variable_vardefexpr - 1] = true;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+ tupdesc = RelationGetDescr(rel);
+
+ oldtup = SearchSysCache2(VARIABLENAMENSP,
+ PointerGetDatum(varName),
+ ObjectIdGetDatum(varNamespace));
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ if (if_not_exists)
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists, skipping",
+ varName)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists",
+ varName)));
+
+ heap_freetuple(oldtup);
+ heap_close(rel, RowExclusiveLock);
+
+ return InvalidObjectAddress;
+ }
+
+ varacl = get_user_default_acl(OBJECT_VARIABLE, varOwner,
+ varNamespace);
+
+ if (varacl != NULL)
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(varacl);
+ else
+ nulls[Anum_pg_variable_varacl - 1] = true;
+
+ tup = heap_form_tuple(tupdesc, values, nulls);
+ CatalogTupleInsert(rel, tup);
+
+ retval = HeapTupleGetOid(tup);
+
+ myself.classId = VariableRelationId;
+ myself.objectId = retval;
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = varNamespace;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on used type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = varType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on any roles mentioned in ACL */
+ if (varacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(varacl, &newmembers);
+ updateAclDependencies(VariableRelationId, retval, 0,
+ varOwner,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_freetuple(tup);
+
+ /* Post creation hook for new function */
+ InvokeObjectPostCreateHook(VariableRelationId, retval, 0);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..2cb5b1172d 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,7 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
- schemacmds.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
+ schemacmds.o seclabel.o sequence.o schemavariable.o statscmds.o subscriptioncmds.o \
tablecmds.o tablespace.o trigger.o tsearchcmds.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index eff325cc7d..a9d5e5e0ad 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -387,6 +387,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_VARIABLE:
{
ObjectAddress address;
Relation catalog;
@@ -504,6 +505,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
@@ -594,6 +596,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_TSDICT:
case OCLASS_TSTEMPLATE:
case OCLASS_TSCONFIG:
+ case OCLASS_VARIABLE:
{
Relation catalog;
@@ -852,6 +855,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_TABLESPACE:
case OBJECT_TSDICTIONARY:
case OBJECT_TSCONFIGURATION:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 01a999c2ac..fec2495e93 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -19,6 +19,7 @@
#include "commands/discard.h"
#include "commands/prepare.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "utils/guc.h"
#include "utils/portal.h"
@@ -48,6 +49,10 @@ DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
ResetTempTableNamespace();
break;
+ case DISCARD_VARIABLES:
+ ResetSchemaVariableCache();
+ break;
+
default:
elog(ERROR, "unrecognized DISCARD target: %d", stmt->target);
}
@@ -75,4 +80,5 @@ DiscardAll(bool isTopLevel)
ResetPlanCache();
ResetTempTableNamespace();
ResetSequenceCaches();
+ ResetSchemaVariableCache();
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e..426df246b3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -126,6 +126,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"TEXT SEARCH TEMPLATE", true},
{"TYPE", true},
{"USER MAPPING", true},
+ {"VARIABLE", true},
{"VIEW", true},
{NULL, false}
};
@@ -297,7 +298,8 @@ check_ddl_tag(const char *tag)
pg_strcasecmp(tag, "REVOKE") == 0 ||
pg_strcasecmp(tag, "DROP OWNED") == 0 ||
pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
+ pg_strcasecmp(tag, "SECURITY LABEL") == 0 ||
+ pg_strcasecmp(tag, "CREATE VARIABLE") == 0)
return EVENT_TRIGGER_COMMAND_TAG_OK;
/*
@@ -1146,6 +1148,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -1209,6 +1212,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
return true;
/*
@@ -2244,6 +2248,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2326,6 +2332,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b1556a..eb8c08baf3 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_PLAN_UTILITY:
/* OK */
break;
default:
diff --git a/src/backend/commands/schemavariable.c b/src/backend/commands/schemavariable.c
new file mode 100644
index 0000000000..208d0d20c4
--- /dev/null
+++ b/src/backend/commands/schemavariable.c
@@ -0,0 +1,470 @@
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
+#include "executor/executor.h"
+#include "executor/svariableReceiver.h"
+#include "nodes/execnodes.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/inval.h"
+#include "utils/memutils.h"
+#include "utils/lsyscache.h"
+#include "utils/snapmgr.h"
+#include "utils/syscache.h"
+
+/*
+ * The content of variables is not transactional. Due this fact the
+ * implementation of DROP can be simple, because although DROP VARIABLE
+ * can be reverted, the content of variable can be lost. In this example,
+ * DROP VARIABLE is same like reset variable.
+ */
+
+typedef struct SchemaVariableData
+{
+ Oid varid; /* pg_variable OID of this sequence (hash key) */
+ Oid typid; /* OID of the data type */
+ int32 typmod;
+ int16 typlen;
+ bool typbyval;
+ bool isnull;
+ bool freeval;
+ Datum value;
+ bool is_rowtype; /* true when variable is composite */
+ bool is_valid; /* true when variable was successfuly initialized */
+} SchemaVariableData;
+
+typedef SchemaVariableData *SchemaVariable;
+
+static HTAB *schemavarhashtab = NULL; /* hash table for session variables */
+static MemoryContext SchemaVariableMemoryContext = NULL;
+
+static bool first_time = true;
+static void create_schemavar_hashtable(void);
+static bool clean_cache_req = false;
+
+static void clean_cache(void);
+static void force_clean_cache(XactEvent event, void *arg);
+
+
+/*
+ * Save info about ncessity to clean hash table, because some
+ * schema variable was dropped. Don't do here more, recheck
+ * needs to be in transaction state.
+ */
+static void
+InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ if (cacheid != VARIABLEOID)
+ return;
+
+ clean_cache_req = true;
+}
+
+static void
+force_clean_cache(XactEvent event, void *arg)
+{
+ /*
+ * should continue only in transaction time, when
+ * syscache is available.
+ */
+ if (clean_cache_req && IsTransactionState())
+ {
+ clean_cache();
+ clean_cache_req = false;
+ }
+}
+
+static void
+clean_cache(void)
+{
+ HASH_SEQ_STATUS status;
+ SchemaVariable var;
+
+ if (!schemavarhashtab)
+ return;
+
+ hash_seq_init(&status, schemavarhashtab);
+
+ /*
+ * Every valid variable have to have entry in system
+ * catalog. Removed if there is nothing.
+ */
+ while ((var = (SchemaVariable) hash_seq_search(&status)) != NULL)
+ {
+ HeapTuple tp = InvalidOid;
+
+ tp = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var->varid));
+ if (!HeapTupleIsValid(tp))
+ {
+ elog(DEBUG1, "variable %d is removed from cache", var->varid);
+
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (hash_search(schemavarhashtab,
+ (void *) &var->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(DEBUG1, "hash table corrupted");
+ }
+ else
+ ReleaseSysCache(tp);
+ }
+}
+
+char *
+VariableGetName(Variable *var)
+{
+ char *nspname;
+
+ if (VariableIsVisible(var->oid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(var->namespace);
+
+ return quote_qualified_identifier(nspname, var->name);
+}
+
+/*
+ * Create the hash table for storing schema variables
+ */
+static void
+create_schemavar_hashtable(void)
+{
+ HASHCTL ctl;
+
+ /* set callbacks */
+ if (first_time)
+ {
+ CacheRegisterSyscacheCallback(VARIABLEOID,
+ InvalidateSchemaVarCacheCallback,
+ (Datum) 0);
+
+ RegisterXactCallback(force_clean_cache, NULL);
+
+ first_time = false;
+ }
+
+ /* needs own long life memory context */
+ if (SchemaVariableMemoryContext == NULL)
+ {
+ SchemaVariableMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "schema variables",
+ ALLOCSET_START_SMALL_SIZES);
+ }
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(SchemaVariableData);
+ ctl.hcxt = SchemaVariableMemoryContext;
+
+ schemavarhashtab = hash_create("Schema variables", 64, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/*
+ * Fast drop complete content of schema variables
+ */
+void
+ResetSchemaVariableCache(void)
+{
+ if (schemavarhashtab)
+ {
+ hash_destroy(schemavarhashtab);
+ schemavarhashtab = NULL;
+ }
+
+ if (SchemaVariableMemoryContext != NULL)
+ {
+ MemoryContextReset(SchemaVariableMemoryContext);
+ }
+}
+
+/*
+ * Drop variable by OID
+ */
+void
+RemoveVariableById(Oid varid)
+{
+ Relation rel;
+ HeapTuple tup;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ CatalogTupleDelete(rel, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Creates new variable - entry in pg_catalog.pg_variable table
+ */
+ObjectAddress
+DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt)
+{
+ Oid namespaceid;
+ AclResult aclresult;
+ Oid typid;
+ int32 typmod;
+ Oid varowner = GetUserId();
+
+ Node *cooked_default = NULL;
+
+ namespaceid =
+ RangeVarGetAndCheckCreationNamespace(stmt->variable, NoLock, NULL);
+
+ typenameTypeIdAndMod(pstate, stmt->typeName, &typid, &typmod);
+
+ aclresult = pg_type_aclcheck(typid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error_type(aclresult, typid);
+
+ if (stmt->defexpr)
+ {
+ cooked_default = transformExpr(pstate, stmt->defexpr,
+ EXPR_KIND_VARIABLE_DEFAULT);
+
+ cooked_default = coerce_to_specific_type(pstate,
+ cooked_default, typid, "DEFAULT");
+ }
+
+ return VariableCreate(stmt->variable->relname,
+ namespaceid,
+ typid,
+ typmod,
+ varowner,
+ cooked_default,
+ stmt->if_not_exists);
+}
+
+/*
+ * Try to search value in hash table. If doesn't
+ * exists insert it (and calculate defexpr if exists.
+ */
+static SchemaVariable
+PrepareSchemaVariableForReading(Oid varid)
+{
+ SchemaVariable svar;
+ Variable *var;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+ if (!found)
+ {
+ var = GetVariable(varid, false);
+ get_typlenbyval(var->typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = var->typid;
+ svar->typmod = var->typmod;
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+ svar->is_rowtype = type_is_rowtype(var->typid);
+
+ /* when we don't need calculate defexpr, value is valid already */
+ svar->is_valid = var->defexpr ? false : true;
+ }
+ else if (!svar->is_valid)
+ {
+ /* we need var to recalculate defexpr */
+ var = GetVariable(varid, false);
+ }
+ else
+ /* we don't need to go to sys cache */
+ var = NULL;
+
+ /*
+ * Initialize variable when it is necessary. It is fresh
+ * or last initialization was not successfull.
+ */
+ if (var != NULL && var->defexpr && !svar->is_valid)
+ {
+ MemoryContext oldcontext = NULL;
+
+ Datum value = (Datum) 0;
+ bool null;
+ EState *estate = NULL;
+ Expr *defexpr;
+ ExprState *defexprs;
+
+ /* Prepare default expr */
+ estate = CreateExecutorState();
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ defexpr = expression_planner((Expr *) var->defexpr);
+ defexprs = ExecInitExpr(defexpr, NULL);
+ value = ExecEvalExprSwitchContext(defexprs, GetPerTupleExprContext(estate), &null);
+
+ MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!null)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ FreeExecutorState(estate);
+ }
+
+ if (!svar->is_valid)
+ elog(ERROR, "the content of variable is not valid");
+
+ return svar;
+}
+
+/*
+ * Returns content of variable. We expext secured access now.
+ * Secure check should be done before.
+ */
+Datum
+GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid)
+{
+ SchemaVariable svar;
+
+ svar = PrepareSchemaVariableForReading(varid);
+ *isNull = svar->isnull;
+
+ if (expected_typid != svar->typid)
+ elog(ERROR, "type of variable \"%s\" is different than expected",
+ schema_variable_get_name(varid));
+
+ return (Datum) svar->value;
+}
+
+/*
+ * Write value to variable. We expect secured access in this moment.
+ * In this time, we recheck syschache about used type.
+ */
+void
+SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod)
+{
+ MemoryContext oldcontext = NULL;
+
+ SchemaVariable svar;
+ Oid var_typid;
+ int32 var_typmod;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+
+ get_schema_variable_type_typmod(varid, &var_typid, &var_typmod);
+
+ /* check types first */
+ if (var_typid != typid)
+ elog(ERROR, "type of expression is different than schema variable type");
+
+ if (found)
+ {
+ /* release current content first */
+ if (svar->freeval)
+ {
+ pfree(DatumGetPointer(svar->value));
+ svar->value = (Datum) 0;
+ svar->isnull = true;
+ svar->freeval = false;
+ }
+ }
+
+ get_typlenbyval(typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = typid;
+ svar->typmod = typmod;
+
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+
+ svar->is_rowtype = type_is_rowtype(typid);
+ svar->is_valid = false;
+
+ oldcontext = MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!isNull)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+}
+
+void
+doLetStmt(PlannedStmt *pstmt,
+ ParamListInfo params,
+ QueryEnvironment *queryEnv,
+ const char *queryString)
+{
+ QueryDesc *queryDesc;
+ DestReceiver *dest;
+
+ PushCopiedSnapshot(GetActiveSnapshot());
+ UpdateActiveSnapshotCommandId();
+
+ /* Create dest receiver for LET */
+ dest = CreateDestReceiver(DestVariable);
+
+ SetVariableDestReceiverParams(dest, pstmt->resultVariable);
+
+ /* Create a QueryDesc requesting no output */
+ queryDesc = CreateQueryDesc(pstmt, queryString,
+ GetActiveSnapshot(),
+ InvalidSnapshot,
+ dest, params, queryEnv, 0);
+
+ ExecutorStart(queryDesc, 0);
+ ExecutorRun(queryDesc, ForwardScanDirection, 2L, true);
+ ExecutorFinish(queryDesc);
+ ExecutorEnd(queryDesc);
+
+ FreeQueryDesc(queryDesc);
+
+ PopActiveSnapshot();
+}
+
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index eb2d33dd86..22cd7871cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9630,6 +9630,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
/*
* We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index cc09895fa5..ee8ff7da9e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -29,6 +29,6 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
nodeCtescan.o nodeNamedtuplestorescan.o nodeWorktablescan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o \
- nodeTableFuncscan.o
+ nodeTableFuncscan.o svariableReceiver.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d7..58d4955dd8 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -33,6 +33,7 @@
#include "access/nbtree.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -727,6 +728,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
Param *param = (Param *) node;
ParamListInfo params;
+ AclResult aclresult;
switch (param->paramkind)
{
@@ -736,6 +738,19 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
break;
+ case PARAM_SCHEMA_VARIABLE:
+ /* Check permission to read schema variable */
+ aclresult = pg_variable_aclcheck(param->paramid, GetUserId(), ACL_READ);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE,
+ schema_variable_get_name(param->paramid));
+
+ scratch.opcode = EEOP_PARAM_VARIABLE;
+ scratch.d.param.paramid = param->paramid;
+ scratch.d.param.paramtype = param->paramtype;
+ ExprEvalPushStep(state, &scratch);
+ break;
+
case PARAM_EXTERN:
/*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25aae5..25966ceeeb 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -351,6 +352,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1007,6 +1009,20 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_VARIABLE)
+ {
+ Datum d;
+ bool isnull;
+
+ d = GetSchemaVariable(op->d.param.paramid, &isnull,
+ op->d.param.paramtype);
+
+ *op->resvalue = d;
+ *op->resnull = isnull;
+
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_CASE_TESTVAL)
{
/*
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 01e1a46180..3721aec7a1 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,9 +43,11 @@
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_publication.h"
+#include "catalog/pg_variable.h"
#include "commands/matview.h"
#include "commands/trigger.h"
#include "executor/execdebug.h"
+#include "executor/svariableReceiver.h"
#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -204,12 +206,18 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
*/
estate->es_queryEnv = queryDesc->queryEnv;
+ /*
+ * Result can be stored in schema variable.
+ */
+ estate->es_result_variable = queryDesc->plannedstmt->resultVariable;
+
/*
* If non-read-only query, set the command ID to mark output tuples with
*/
switch (queryDesc->operation)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
@@ -345,6 +353,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate->es_lastoid = InvalidOid;
sendTuples = (operation == CMD_SELECT ||
+ OidIsValid(estate->es_result_variable) ||
queryDesc->plannedstmt->hasReturning);
if (sendTuples)
@@ -924,6 +933,17 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_num_root_result_relations = 0;
}
+ if (OidIsValid(estate->es_result_variable))
+ {
+ AclResult aclresult;
+ Oid varid = estate->es_result_variable;
+
+ /* Ensure this variable is writeable */
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, schema_variable_get_name(varid));
+ }
+
/*
* Similarly, we have to lock relations selected FOR [KEY] UPDATE/SHARE
* before we initialize the plan tree, else we'd be risking lock upgrades.
diff --git a/src/backend/executor/svariableReceiver.c b/src/backend/executor/svariableReceiver.c
new file mode 100644
index 0000000000..0eac4b5d0c
--- /dev/null
+++ b/src/backend/executor/svariableReceiver.c
@@ -0,0 +1,145 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.c
+ * An implementation of DestReceiver that stores the result value in
+ * a schema variable.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/svariableReceiver.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/tuptoaster.h"
+#include "executor/svariableReceiver.h"
+#include "commands/schemavariable.h"
+
+typedef struct
+{
+ DestReceiver pub;
+ Oid varid;
+ Oid typid;
+ int32 typmod;
+ int typlen;
+ int slot_offset;
+ int rows;
+} svariableState;
+
+
+/*
+ * Prepare to receive tuples from executor.
+ */
+static void
+svariableStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+ svariableState *myState = (svariableState *) self;
+ int natts = typeinfo->natts;
+ int outcols = 0;
+ int i;
+
+ for (i = 0; i < natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+
+ if (attr->attisdropped)
+ continue;
+
+ if (++outcols > 1)
+ elog(ERROR, "svariable DestReceiver can take only one attribute");
+
+ myState->typid = attr->atttypid;
+ myState->typmod = attr->atttypmod;
+ myState->typlen = attr->attlen;
+ myState->slot_offset = i;
+ }
+
+ myState->rows = 0;
+}
+
+/*
+ * Receive a tuple from the executor and store it in schema variable.
+ */
+static bool
+svariableReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
+{
+ svariableState *myState = (svariableState *) self;
+ Datum value;
+ bool isnull;
+ bool freeval = false;
+
+ /* Make sure the tuple is fully deconstructed */
+ slot_getallattrs(slot);
+
+ value = slot->tts_values[myState->slot_offset];
+ isnull = slot->tts_isnull[myState->slot_offset];
+
+ if (myState->typlen == -1 && !isnull && VARATT_IS_EXTERNAL(DatumGetPointer(value)))
+ {
+ value = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+ DatumGetPointer(value)));
+ freeval = true;
+ }
+
+ SetSchemaVariable(myState->varid, value, isnull, myState->typid, myState->typmod);
+
+ if (freeval)
+ pfree(DatumGetPointer(value));
+
+ return true;
+}
+
+/*
+ * Clean up at end of an executor run
+ */
+static void
+svariableShutdownReceiver(DestReceiver *self)
+{
+ /* Do nothing */
+}
+
+/*
+ * Destroy receiver when done with it
+ */
+static void
+svariableDestroyReceiver(DestReceiver *self)
+{
+ pfree(self);
+}
+
+/*
+ * Initially create a DestReceiver object.
+ */
+DestReceiver *
+CreateVariableDestReceiver(void)
+{
+ svariableState *self = (svariableState *) palloc0(sizeof(svariableState));
+
+ self->pub.receiveSlot = svariableReceiveSlot;
+ self->pub.rStartup = svariableStartupReceiver;
+ self->pub.rShutdown = svariableShutdownReceiver;
+ self->pub.rDestroy = svariableDestroyReceiver;
+ self->pub.mydest = DestVariable;
+
+ /* private fields will be set by SetVariableDestReceiverParams */
+
+ return (DestReceiver *) self;
+}
+
+/*
+ * Set parameters for a VariableDestReceiver
+ */
+void
+SetVariableDestReceiverParams(DestReceiver *self, Oid varid)
+{
+ svariableState *myState = (svariableState *) self;
+
+ Assert(myState->pub.mydest == DestVariable);
+ Assert(OidIsValid(varid));
+
+ myState->varid = varid;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c8220cf65..fcaa2db51a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(nonleafResultRelations);
COPY_NODE_FIELD(rootResultRelations);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks);
@@ -3000,6 +3001,7 @@ _copyQuery(const Query *from)
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_SCALAR_FIELD(hasAggs);
COPY_SCALAR_FIELD(hasWindowFuncs);
COPY_SCALAR_FIELD(hasTargetSRFs);
@@ -3118,6 +3120,18 @@ _copySelectStmt(const SelectStmt *from)
return newnode;
}
+static LetStmt *
+_copyLetStmt(const LetStmt *from)
+{
+ LetStmt *newnode = makeNode(LetStmt);
+
+ COPY_NODE_FIELD(target);
+ COPY_NODE_FIELD(selectStmt);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
static SetOperationStmt *
_copySetOperationStmt(const SetOperationStmt *from)
{
@@ -5166,6 +5180,9 @@ copyObjectImpl(const void *from)
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
+ case T_LetStmt:
+ retval = _copyLetStmt(from);
+ break;
case T_SetOperationStmt:
retval = _copySetOperationStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 378f2facb8..3ec472e19b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -949,6 +949,7 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_SCALAR_FIELD(canSetTag);
COMPARE_NODE_FIELD(utilityStmt);
COMPARE_SCALAR_FIELD(resultRelation);
+ COMPARE_SCALAR_FIELD(resultVariable);
COMPARE_SCALAR_FIELD(hasAggs);
COMPARE_SCALAR_FIELD(hasWindowFuncs);
COMPARE_SCALAR_FIELD(hasTargetSRFs);
@@ -1057,6 +1058,16 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
return true;
}
+static bool
+_equalLetStmt(const LetStmt *a, const LetStmt *b)
+{
+ COMPARE_NODE_FIELD(target);
+ COMPARE_NODE_FIELD(selectStmt);
+
+ return true;
+}
+
+
static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
@@ -3225,6 +3236,9 @@ equal(const void *a, const void *b)
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
break;
+ case T_LetStmt:
+ retval = _equalLetStmt(a, b);
+ break;
case T_SetOperationStmt:
retval = _equalSetOperationStmt(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6269f474d2..46404ff9ac 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -278,6 +278,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(nonleafResultRelations);
WRITE_NODE_FIELD(rootResultRelations);
+ WRITE_OID_FIELD(resultVariable);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks);
@@ -2793,6 +2794,16 @@ _outSelectStmt(StringInfo str, const SelectStmt *node)
WRITE_NODE_FIELD(rarg);
}
+static void
+_outLetStmt(StringInfo str, const LetStmt *node)
+{
+ WRITE_NODE_TYPE("LET");
+
+ WRITE_NODE_FIELD(target);
+ WRITE_NODE_FIELD(selectStmt);
+ WRITE_LOCATION_FIELD(location);
+}
+
static void
_outFuncCall(StringInfo str, const FuncCall *node)
{
@@ -2971,6 +2982,7 @@ _outQuery(StringInfo str, const Query *node)
appendStringInfoString(str, " :utilityStmt <>");
WRITE_INT_FIELD(resultRelation);
+ WRITE_INT_FIELD(resultVariable);
WRITE_BOOL_FIELD(hasAggs);
WRITE_BOOL_FIELD(hasWindowFuncs);
WRITE_BOOL_FIELD(hasTargetSRFs);
@@ -4191,6 +4203,9 @@ outNode(StringInfo str, const void *obj)
case T_SelectStmt:
_outSelectStmt(str, obj);
break;
+ case T_LetStmt:
+ _outLetStmt(str, obj);
+ break;
case T_ColumnDef:
_outColumnDef(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3254524223..4454327549 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -242,6 +242,7 @@ _readQuery(void)
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
+ READ_INT_FIELD(resultVariable);
READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasWindowFuncs);
READ_BOOL_FIELD(hasTargetSRFs);
@@ -1485,6 +1486,7 @@ _readPlannedStmt(void)
READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(nonleafResultRelations);
READ_NODE_FIELD(rootResultRelations);
+ READ_OID_FIELD(resultVariable);
READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index fd06da98b9..01f97f2d86 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -335,7 +335,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
*/
if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
IsUnderPostmaster &&
- parse->commandType == CMD_SELECT &&
+ (parse->commandType == CMD_SELECT ||
+ parse->commandType == CMD_PLAN_UTILITY) &&
!parse->hasModifyingCTE &&
max_parallel_workers_per_gather > 0 &&
!IsParallelWorker() &&
@@ -352,6 +353,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->parallelModeOK = false;
}
+
+
/*
* glob->parallelModeNeeded is normally set to false here and changed to
* true during plan creation if a Gather or Gather Merge plan is actually
@@ -521,6 +524,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->resultRelations = glob->resultRelations;
result->nonleafResultRelations = glob->nonleafResultRelations;
result->rootResultRelations = glob->rootResultRelations;
+ result->resultVariable = parse->resultVariable;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks;
@@ -2167,7 +2171,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
* inheritance_planner, add the ModifyTable node.
*/
- if (parse->commandType != CMD_SELECT && !inheritance_update)
+ if (parse->commandType != CMD_SELECT && parse->commandType != CMD_PLAN_UTILITY && !inheritance_update)
{
List *withCheckOptionLists;
List *returningLists;
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 8603feef2b..2923e3fcc7 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -71,6 +71,7 @@ preprocess_targetlist(PlannerInfo *root)
{
Query *parse = root->parse;
int result_relation = parse->resultRelation;
+ int result_variable = parse->resultVariable;
List *range_table = parse->rtable;
CmdType command_type = parse->commandType;
RangeTblEntry *target_rte = NULL;
@@ -96,6 +97,10 @@ preprocess_targetlist(PlannerInfo *root)
target_relation = heap_open(target_rte->relid, NoLock);
}
+ else if (result_variable)
+ {
+ Assert(command_type == CMD_PLAN_UTILITY);
+ }
else
Assert(command_type == CMD_SELECT);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a04ad6e99e..da570bb23b 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1254,7 +1254,8 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
{
Param *param = (Param *) node;
- if (param->paramkind == PARAM_EXTERN)
+ if (param->paramkind == PARAM_EXTERN ||
+ param->paramkind == PARAM_SCHEMA_VARIABLE)
return false;
if (param->paramkind != PARAM_EXEC ||
@@ -4799,7 +4800,7 @@ substitute_actual_parameters_mutator(Node *node,
{
if (node == NULL)
return NULL;
- if (IsA(node, Param))
+ if (IsA(node, Param) && ((Param *) node)->paramkind != PARAM_SCHEMA_VARIABLE)
{
Param *param = (Param *) node;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8369e3ad62..fc0cf34c7d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1272,7 +1272,7 @@ get_relation_constraints(PlannerInfo *root,
* descriptor, instead of constraint exclusion which is driven by the
* individual partition's partition constraint.
*/
- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
+ if (enable_partition_pruning && root->parse->commandType != CMD_SELECT && root->parse->commandType != CMD_PLAN_UTILITY)
{
List *pcqual = RelationGetPartitionQual(relation);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c601b6d40d..441b298693 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,7 +25,10 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/namespace.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -44,6 +47,8 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -78,6 +83,8 @@ static Query *transformCreateTableAsStmt(ParseState *pstate,
CreateTableAsStmt *stmt);
static Query *transformCallStmt(ParseState *pstate,
CallStmt *stmt);
+static Query *transformLetStmt(ParseState *pstate,
+ LetStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown);
#ifdef RAW_EXPRESSION_COVERAGE_TEST
@@ -267,6 +274,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
+ case T_LetStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
@@ -327,6 +335,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
(CallStmt *) parseTree);
break;
+ case T_LetStmt:
+ result = transformLetStmt(pstate,
+ (LetStmt *) parseTree);
+ break;
+
default:
/*
@@ -367,6 +380,7 @@ analyze_requires_snapshot(RawStmt *parseTree)
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
+ case T_LetStmt:
result = true;
break;
@@ -1567,6 +1581,203 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
return qry;
}
+/*
+ * transformLetStmt -
+ * transform an Let Statement
+ */
+static Query *
+transformLetStmt(ParseState *pstate, LetStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ List *exprList = NIL;
+ List *exprListCoer = NIL;
+ List *indirection = NIL;
+ ListCell *lc;
+ Query *selectQuery;
+ int i = 0;
+
+ Oid varid;
+
+ ParseExprKind sv_expr_kind;
+ char *attrname = NULL;
+ bool not_unique;
+ bool is_rowtype;
+ Oid typid;
+ int32 typmod;
+
+ AclResult aclresult;
+ List *names = NULL;
+ int indirection_start;
+
+ sv_expr_kind = pstate->p_expr_kind;
+ pstate->p_expr_kind = EXPR_KIND_LET;
+
+ /* There can't be any outer WITH to worry about */
+ Assert(pstate->p_ctenamespace == NIL);
+
+ /* Exec this command as utility */
+ qry->commandType = CMD_PLAN_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ names = NamesFromList(stmt->target);
+
+ varid = identify_variable(names, &attrname, ¬_unique);
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("target \"%s\" of LET command is ambiguous",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ if (!OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("schema variable \"%s\" doesn't exists",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ qry->resultVariable = varid;
+
+ get_schema_variable_type_typmod(varid, &typid, &typmod);
+
+ is_rowtype = type_is_rowtype(typid);
+
+ if (attrname && !is_rowtype)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("target variable \"%s\" is not row type",
+ schema_variable_get_name(varid)),
+ parser_errposition(pstate, stmt->location)));
+
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, NameListToString(names));
+
+ selectQuery = transformStmt(pstate, stmt->selectStmt);
+
+ /* The grammar should have produced a SELECT */
+ if (!IsA(selectQuery, Query) ||
+ selectQuery->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in LET ... SELECT");
+
+ /*----------
+ * Generate an expression list for the LET that selects all the
+ * non-resjunk columns from the subquery.
+ *----------
+ */
+ exprList = NIL;
+ foreach(lc, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->resjunk)
+ continue;
+
+ exprList = lappend(exprList, tle->expr);
+ }
+
+ /*
+ * Because doesn't support pattern matching, don't allow multicolumn result
+ */
+ if (list_length(exprList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expression is not scalar value"),
+ parser_errposition(pstate,
+ exprLocation((Node *) exprList))));
+
+ indirection_start = list_length(names) - (attrname ? 1 : 0);
+ indirection = list_copy_tail(stmt->target, indirection_start);
+
+ exprListCoer = NIL;
+ foreach(lc, exprList)
+ {
+ Node *orig_expr = (Node*) lfirst(lc);
+ Oid exprtypid = exprType((Node *) orig_expr);
+ Param *param = makeNode(Param);
+ Expr *expr = NULL;
+
+ param->paramkind = PARAM_SCHEMA_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (indirection != NULL)
+ {
+ bool targetIsArray;
+ char *targetName;
+
+ targetName = attrname != NULL ? attrname : get_schema_variable_name(varid);
+ targetIsArray = OidIsValid(get_element_type(typid));
+
+ expr = (Expr *)
+ transformAssignmentIndirection(pstate,
+ (Node *) param,
+ targetName,
+ targetIsArray,
+ typid,
+ typmod,
+ InvalidOid,
+ list_head(indirection),
+ (Node *) orig_expr,
+ stmt->location);
+ }
+ else
+ expr = (Expr *)
+ coerce_to_target_type(pstate,
+ (Node *) orig_expr,
+ exprtypid,
+ typid, typmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ stmt->location);
+
+ if (expr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable \"%s\" is of type %s,"
+ " but expression is of type %s",
+ schema_variable_get_name(varid),
+ format_type_be(typid),
+ format_type_be(exprtypid)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *) orig_expr))));
+
+ exprListCoer = lappend(exprListCoer, expr);
+ }
+
+ /*
+ * Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
+ */
+ qry->targetList = NIL;
+ foreach(lc, exprListCoer)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ tle = makeTargetEntry(expr,
+ i + 1,
+ FigureColname((Node *)expr),
+ false);
+ qry->targetList = lappend(qry->targetList, tle);
+ }
+
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+ qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+
+ assign_query_collations(pstate, qry);
+
+ pstate->p_expr_kind = sv_expr_kind;
+
+ return qry;
+}
+
+
/*
* transformSetOperationStmt -
* transforms a set-operations tree
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 87f5e95827..25036669c1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -257,8 +257,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSchemaVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -268,7 +268,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DropTransformStmt
DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
- ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
+ LetStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -400,6 +400,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
TriggerTransitions TriggerReferencing
publication_name_list
vacuum_relation_list opt_vacuum_relation_list
+ let_target
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -584,6 +585,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> optSchemaVarDefExpr
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -649,7 +651,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+ LEADING LEAKPROOF LEAST LEFT LET LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -687,8 +689,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -878,6 +880,7 @@ stmt :
| CreatePolicyStmt
| CreatePLangStmt
| CreateSchemaStmt
+ | CreateSchemaVarStmt
| CreateSeqStmt
| CreateStmt
| CreateSubscriptionStmt
@@ -917,6 +920,7 @@ stmt :
| ImportForeignSchemaStmt
| IndexStmt
| InsertStmt
+ | LetStmt
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
@@ -1808,7 +1812,12 @@ DiscardStmt:
n->target = DISCARD_SEQUENCES;
$$ = (Node *) n;
}
-
+ | DISCARD VARIABLES
+ {
+ DiscardStmt *n = makeNode(DiscardStmt);
+ n->target = DISCARD_VARIABLES;
+ $$ = (Node *) n;
+ }
;
@@ -4479,6 +4488,42 @@ create_extension_opt_item:
}
;
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE VARIABLE varname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSchemaVarStmt:
+ CREATE OptTemp VARIABLE qualified_name opt_as Typename optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $4->relpersistence = $2;
+ n->variable = $4;
+ n->typeName = $6;
+ n->defexpr = $7;
+ n->if_not_exists = false;
+ $$ = (Node *) n;
+ }
+ | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $7->relpersistence = $2;
+ n->variable = $7;
+ n->typeName = $9;
+ n->defexpr = $10;
+ n->if_not_exists = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+optSchemaVarDefExpr: DEFAULT b_expr { $$ = $2; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+
+
/*****************************************************************************
*
* ALTER EXTENSION name UPDATE [ TO version ]
@@ -6335,6 +6380,7 @@ drop_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name_list */
@@ -6604,6 +6650,7 @@ comment_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -6742,6 +6789,7 @@ security_label_type_any_name:
| TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -7163,6 +7211,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| ALL TABLES IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7203,6 +7259,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
;
@@ -7363,6 +7427,7 @@ defacl_privilege_target:
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
;
@@ -8959,6 +9024,25 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newname = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
opt_column: COLUMN { $$ = COLUMN; }
@@ -9277,6 +9361,25 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newschema = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
/*****************************************************************************
@@ -9512,6 +9615,14 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
@@ -10693,6 +10804,7 @@ ExplainableStmt:
| CreateMatViewStmt
| RefreshMatViewStmt
| ExecuteStmt /* by default all are $$=$1 */
+ | LetStmt
;
explain_option_list:
@@ -10750,6 +10862,7 @@ PreparableStmt:
| InsertStmt
| UpdateStmt
| DeleteStmt /* by default all are $$=$1 */
+ | LetStmt
;
/*****************************************************************************
@@ -11148,6 +11261,44 @@ opt_hold: /* EMPTY */ { $$ = 0; }
| WITHOUT HOLD { $$ = 0; }
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * LET STATEMENTS
+ *
+ *****************************************************************************/
+LetStmt: LET let_target '=' a_expr
+ {
+ LetStmt *n = makeNode(LetStmt);
+ SelectStmt *select = makeNode(SelectStmt);
+ ResTarget *res = makeNode(ResTarget);
+
+ n->target = $2;
+
+ /* Create target list for implicit query */
+ res->name = NULL;
+ res->indirection = NIL;
+ res->val = (Node *) $4;
+ res->location = @4;
+
+ select->targetList = list_make1(res);
+ n->selectStmt = (Node *) select;
+
+ n->location = @2;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+let_target:
+ ColId opt_indirection
+ {
+ $$ = list_make1(makeString($1));
+ if ($2)
+ $$ = list_concat($$,
+ check_indirection($2, yyscanner));
+ }
+
/*****************************************************************************
*
* QUERY:
@@ -15127,6 +15278,7 @@ unreserved_keyword:
| LARGE_P
| LAST_P
| LEAKPROOF
+ | LET
| LEVEL
| LISTEN
| LOAD
@@ -15275,6 +15427,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 61727e1d71..6823612fba 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -349,6 +349,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
Assert(false); /* can't happen */
break;
case EXPR_KIND_OTHER:
+ case EXPR_KIND_LET:
/*
* Accept aggregate/grouping here; caller must throw error if
@@ -465,6 +466,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
if (isAgg)
err = _("aggregate functions are not allowed in DEFAULT expressions");
@@ -879,6 +881,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("window functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -902,6 +905,8 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_CALL_ARGUMENT:
err = _("window functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("window functions are not allowed in LET statement");
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a9b6..bcdda0fb4a 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -37,6 +38,7 @@
#include "utils/date.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
+#include "utils/typcache.h"
#include "utils/xml.h"
@@ -116,6 +118,9 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
+static Node *makeParamSchemaVariable(ParseState *pstate,
+ Oid varid, Oid typid, int32 typmod,
+ char *attrname, int location);
static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
int location);
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
@@ -512,6 +517,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
+ Oid varid = InvalidOid;
+ char *attrname = NULL;
+ bool not_unique;
+
RangeTblEntry *rte;
int levels_up;
enum
@@ -749,6 +758,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break;
}
+ varid = identify_variable(cref->fields, &attrname, ¬_unique);
+
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("schema variable reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+
/*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
@@ -773,6 +791,71 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
parser_errposition(pstate, cref->location)));
}
+ if (OidIsValid(varid))
+ {
+ Oid typid;
+ int32 typmod;
+
+ get_schema_variable_type_typmod(varid, &typid, &typmod);
+
+ if (node != NULL)
+ {
+ /*
+ * some collision can be solved simply here to reduce errors
+ * based on simply existence of some variables. Often error
+ * can be using alias same like variable name. In this case,
+ * when we found column reference, and we found reference to
+ * possible composite variable, but the variable is not composite,
+ * then we can ignore the variable as simply improper, and we
+ * use column reference only.
+ */
+ if (attrname)
+ {
+ if (type_is_rowtype(typid))
+ {
+ TupleDesc tupdesc;
+ bool found = false;
+ int i;
+
+ /* slow part, I hope it will not be to often */
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ if (namestrcmp(&(TupleDescAttr(tupdesc, i)->attname), attrname) == 0 &&
+ !TupleDescAttr(tupdesc, i)->attisdropped)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ FreeTupleDesc(tupdesc);
+
+ /* there are not composite variable with this field */
+ if (!found)
+ varid = InvalidOid;
+ }
+ else
+ /* there are not composite variable with this name */
+ varid = InvalidOid;
+ }
+
+ /* Raise error if varid is still valid. It should be really amigonuous */
+ if (OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ errdetail("The qualified identifier can be column reference or schema variable reference"),
+ parser_errposition(pstate, cref->location)));
+ }
+
+ if (OidIsValid(varid))
+ node = makeParamSchemaVariable(pstate,
+ varid, typid, typmod,
+ attrname, cref->location);
+ }
+
/*
* Throw error if no translation found.
*/
@@ -807,6 +890,59 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return node;
}
+/*
+ * Generate param variable for reference to schema variable
+ */
+static Node *
+makeParamSchemaVariable(ParseState *pstate, Oid varid, Oid typid, int32 typmod, char *attrname, int location)
+{
+ Param *param;
+
+ param = makeNode(Param);
+
+ param->paramkind = PARAM_SCHEMA_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (attrname != NULL)
+ {
+ TupleDesc tupdesc;
+ int i;
+
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+
+ if (strcmp(attrname, NameStr(att->attname)) == 0 &&
+ !att->attisdropped)
+ {
+ /* Success, so generate a FieldSelect expression */
+ FieldSelect *fselect = makeNode(FieldSelect);
+
+ fselect->arg = (Expr *) param;
+ fselect->fieldnum = i + 1;
+ fselect->resulttype = att->atttypid;
+ fselect->resulttypmod = att->atttypmod;
+ /* save attribute's collation for parse_collate.c */
+ fselect->resultcollid = att->attcollation;
+
+ ReleaseTupleDesc(tupdesc);
+ return (Node *) fselect;
+ }
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("could not identify column \"%s\" in variable", attrname),
+ parser_errposition(pstate, location)));
+ }
+
+ return (Node *) param;
+}
+
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
@@ -1818,6 +1954,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
case EXPR_KIND_VALUES_SINGLE:
+ case EXPR_KIND_LET:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
@@ -1826,6 +1963,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -3460,6 +3598,7 @@ ParseExprKindName(ParseExprKind exprKind)
return "CHECK";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
return "DEFAULT";
case EXPR_KIND_INDEX_EXPRESSION:
return "index expression";
@@ -3475,6 +3614,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "PARTITION BY";
case EXPR_KIND_CALL_ARGUMENT:
return "CALL";
+ case EXPR_KIND_LET:
+ return "LET";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 44257154b8..b2c9900e00 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2347,6 +2347,7 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("set-returning functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -2370,6 +2371,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_CALL_ARGUMENT:
err = _("set-returning functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("set-returning functions are not allowed in CALL arguments");
+ break;
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4932e58022..c60fe011f7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -35,16 +35,6 @@
static void markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
Var *var, int levelsup);
-static Node *transformAssignmentIndirection(ParseState *pstate,
- Node *basenode,
- const char *targetName,
- bool targetIsArray,
- Oid targetTypeId,
- int32 targetTypMod,
- Oid targetCollation,
- ListCell *indirection,
- Node *rhs,
- int location);
static Node *transformAssignmentSubscripts(ParseState *pstate,
Node *basenode,
const char *targetName,
@@ -672,7 +662,7 @@ updateTargetListEntry(ParseState *pstate,
* might want to decorate indirection cells with their own location info,
* in which case the location argument could probably be dropped.)
*/
-static Node *
+Node *
transformAssignmentIndirection(ParseState *pstate,
Node *basenode,
const char *targetName,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 3123ee274d..10737d422d 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3350,7 +3350,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
* get executed. Also, utilities aren't rewritten at all (do we still
* need that check?)
*/
- if (event != CMD_SELECT && event != CMD_UTILITY)
+ if (event != CMD_SELECT && event != CMD_UTILITY && event != CMD_PLAN_UTILITY)
{
int result_relation;
RangeTblEntry *rt_entry;
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 61ef396d8a..6a068af799 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -212,7 +212,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
}
/*
- * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
+ * For SELECT, LET, UPDATE and DELETE, add security quals to enforce the USING
* policies. These security quals control access to existing table rows.
* Restrictive policies are combined together using AND, and permissive
* policies are combined together using OR.
@@ -222,6 +222,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
&restrictive_policies);
if (commandType == CMD_SELECT ||
+ commandType == CMD_PLAN_UTILITY ||
commandType == CMD_UPDATE ||
commandType == CMD_DELETE)
add_security_quals(rt_index,
@@ -423,6 +424,7 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
switch (cmd)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
if (policy->polcmd == ACL_SELECT_CHR)
cmd_matches = true;
break;
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index c95a4d519d..47fb0f38b1 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -37,6 +37,7 @@
#include "executor/functions.h"
#include "executor/tqueue.h"
#include "executor/tstoreReceiver.h"
+#include "executor/svariableReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/portal.h"
@@ -143,6 +144,9 @@ CreateDestReceiver(CommandDest dest)
case DestTupleQueue:
return CreateTupleQueueDestReceiver(NULL);
+
+ case DestVariable:
+ return CreateVariableDestReceiver();
}
/* should never get here */
@@ -178,6 +182,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -222,6 +227,7 @@ NullCommand(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -268,6 +274,7 @@ ReadyForQuery(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b5804f64ad..35199fd0dc 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
@@ -344,7 +345,7 @@ ProcessUtility(PlannedStmt *pstmt,
char *completionTag)
{
Assert(IsA(pstmt, PlannedStmt));
- Assert(pstmt->commandType == CMD_UTILITY);
+ Assert(pstmt->commandType == CMD_UTILITY || pstmt->commandType == CMD_PLAN_UTILITY);
Assert(queryString != NULL); /* required as of 8.4 */
/*
@@ -915,6 +916,14 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
}
+ case T_LetStmt:
+ {
+ doLetStmt(pstmt, params, queryEnv, queryString);
+ if (completionTag)
+ strcpy(completionTag, "LET");
+ }
+ break;
+
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
@@ -1221,6 +1230,10 @@ ProcessUtilitySlow(ParseState *pstate,
}
break;
+ case T_CreateSchemaVarStmt:
+ address = DefineSchemaVariable(pstate, (CreateSchemaVarStmt *) parsetree);
+ break;
+
/*
* ************* object creation / destruction **************
*/
@@ -2055,6 +2068,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_STATISTIC_EXT:
tag = "ALTER STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "ALTER VARIABLE";
+ break;
default:
tag = "???";
break;
@@ -2104,6 +2120,10 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ case T_LetStmt:
+ tag = "LET";
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
@@ -2358,6 +2378,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "DROP VARIABLE";
+ break;
default:
tag = "???";
}
@@ -2639,6 +2662,9 @@ CreateCommandTag(Node *parsetree)
case DISCARD_SEQUENCES:
tag = "DISCARD SEQUENCES";
break;
+ case DISCARD_VARIABLES:
+ tag = "DISCARD VARIABLES";
+ break;
default:
tag = "???";
}
@@ -2844,6 +2870,7 @@ CreateCommandTag(Node *parsetree)
tag = "DELETE";
break;
case CMD_UTILITY:
+ case CMD_PLAN_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
default:
@@ -2915,6 +2942,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_CreateSchemaVarStmt:
+ tag = "CREATE VARIABLE";
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -2961,6 +2992,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ case T_LetStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a45e093de7..952c0d9628 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -315,6 +315,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_READ_CHR:
+ read = ACL_READ;
+ break;
+ case ACL_WRITE_CHR:
+ read = ACL_WRITE;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -808,6 +814,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -903,6 +913,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'T':
objtype = OBJECT_TYPE;
break;
+ case 'V':
+ objtype = OBJECT_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
}
@@ -1627,6 +1640,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CONNECT;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
+ if (pg_strcasecmp(priv_type, "READ") == 0)
+ return ACL_READ;
+ if (pg_strcasecmp(priv_type, "WRITE") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1721,6 +1738,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 03e9a28a63..488cb26d3f 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7362,6 +7362,14 @@ get_parameter(Param *param, deparse_context *context)
return;
}
+ /* translate paramid to original schema variable name */
+ if (param->paramkind == PARAM_SCHEMA_VARIABLE)
+ {
+ appendStringInfo(context->buf, "%s",
+ schema_variable_get_name(param->paramid));
+ return;
+ }
+
/*
* Not PARAM_EXEC, or couldn't find referent: just print $N.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bba595ad1d..858a6dd4be 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1691,6 +1691,18 @@ get_relname_relid(const char *relname, Oid relnamespace)
ObjectIdGetDatum(relnamespace));
}
+/*
+ * get_varname_varid
+ * Given name and namespace of variable, look up the OID.
+ */
+Oid
+get_varname_varid(const char *varname, Oid varnamespace)
+{
+ return GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(varnamespace));
+}
+
#ifdef NOT_USED
/*
* get_relnatts
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b381782a3..35dc32f649 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -73,6 +73,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "utils/rel.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
@@ -968,6 +969,28 @@ static const struct cachedesc cacheinfo[] = {
0
},
2
+ },
+ {VariableRelationId, /* VARIABLENAMENSP */
+ VariableNameNspIndexId,
+ 2,
+ {
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ 0,
+ 0
+ },
+ 8
+ },
+ {VariableRelationId, /* VARIABLEOID */
+ VariableObjectIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
}
};
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 0d147cb08d..6d97931d85 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -296,6 +296,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading variables\n");
+ getVariables(fout);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 83c976eaf7..c9bc91ca68 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3471,6 +3471,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(type, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(type, "STATISTICS") == 0 ||
+ strcmp(type, "VARIABLE") == 0 ||
/* non-schema-specified objects */
strcmp(type, "DATABASE") == 0 ||
strcmp(type, "PROCEDURAL LANGUAGE") == 0 ||
@@ -3670,7 +3671,8 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
strcmp(te->desc, "SERVER") == 0 ||
strcmp(te->desc, "STATISTICS") == 0 ||
strcmp(te->desc, "PUBLICATION") == 0 ||
- strcmp(te->desc, "SUBSCRIPTION") == 0)
+ strcmp(te->desc, "SUBSCRIPTION") == 0 ||
+ strcmp(te->desc, "VARIABLE") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 9baf7b2fde..f825a00c9d 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -260,6 +260,7 @@ static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
+static void dumpVariable(Archive *fout, VariableInfo *varinfo);
static void dumpDatabase(Archive *AH);
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
const char *dbname, Oid dboid);
@@ -4221,6 +4222,208 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+/*
+ * getVariables
+ * get information about variables
+ */
+void
+getVariables(Archive *fout)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer init_acl_subquery = createPQExpBuffer();
+ PQExpBuffer init_racl_subquery = createPQExpBuffer();
+ PGresult *res;
+ VariableInfo *varinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_varname;
+ int i_varnamespace;
+ int i_vartype;
+ int i_vartypname;
+ int i_vardefexpr;
+ int i_rolname;
+ int i_varacl;
+ int i_rvaracl;
+ int i_initvaracl;
+ int i_initrvaracl;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion <= 110000)
+ return;
+
+ acl_subquery = createPQExpBuffer();
+ racl_subquery = createPQExpBuffer();
+ init_acl_subquery = createPQExpBuffer();
+ init_racl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
+ init_racl_subquery, "v.varacl", "v.varowner", "'V'",
+ dopt->binary_upgrade);
+
+ query = createPQExpBuffer();
+
+ resetPQExpBuffer(query);
+
+ /* Get the variables in current database. */
+ appendPQExpBuffer(query,
+ "SELECT v.tableoid, v.oid, v.varname, "
+ "v.varnamespace,"
+ "(%s varowner) AS rolname, "
+ "%s as varacl, "
+ "%s as rvaracl, "
+ "%s as initvaracl, "
+ "%s as initrvaracl, "
+ "v.vartype, "
+ "pg_catalog.format_type(v.vartype, v.vartypmod) as vartypname, "
+ "pg_catalog.pg_get_expr(v.vardefexpr,0) as vardefexpr "
+ "FROM pg_variable v "
+ "LEFT JOIN pg_init_privs pip "
+ "ON (v.oid = pip.objoid "
+ "AND pip.classoid = 'pg_variable'::regclass "
+ "AND pip.objsubid = 0)",
+ username_subquery,
+ acl_subquery->data,
+ racl_subquery->data,
+ init_acl_subquery->data,
+ init_racl_subquery->data);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(init_acl_subquery);
+ destroyPQExpBuffer(init_racl_subquery);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_varname = PQfnumber(res, "varname");
+ i_varnamespace = PQfnumber(res, "varnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_vartype = PQfnumber(res, "vartype");
+ i_vartypname = PQfnumber(res, "vartypname");
+ i_vardefexpr = PQfnumber(res, "vardefexpr");
+ i_varacl = PQfnumber(res, "varacl");
+ i_rvaracl = PQfnumber(res, "rvaracl");
+ i_initvaracl = PQfnumber(res, "initvaracl");
+ i_initrvaracl = PQfnumber(res, "initrvaracl");
+
+ varinfo = pg_malloc(ntups * sizeof(VariableInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ TypeInfo *vtype;
+
+ varinfo[i].dobj.objType = DO_VARIABLE;
+ varinfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ varinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&varinfo[i].dobj);
+ varinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_varname));
+ varinfo[i].dobj.namespace =
+ findNamespace(fout,
+ atooid(PQgetvalue(res, i, i_varnamespace)));
+
+ varinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ varinfo[i].vartype = atooid(PQgetvalue(res, i, i_vartype));
+ varinfo[i].vartypname = pg_strdup(PQgetvalue(res, i, i_vartypname));
+
+ varinfo[i].varacl = pg_strdup(PQgetvalue(res, i, i_varacl));
+ varinfo[i].rvaracl = pg_strdup(PQgetvalue(res, i, i_rvaracl));
+ varinfo[i].initvaracl = pg_strdup(PQgetvalue(res, i, i_initvaracl));
+ varinfo[i].initrvaracl = pg_strdup(PQgetvalue(res, i, i_initrvaracl));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ /* Do not try to dump ACL if no ACL exists. */
+ if (PQgetisnull(res, i, i_varacl) && PQgetisnull(res, i, i_rvaracl) &&
+ PQgetisnull(res, i, i_initvaracl) &&
+ PQgetisnull(res, i, i_initrvaracl))
+ varinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+
+ if (PQgetisnull(res, i, i_vardefexpr))
+ varinfo[i].vardefexpr = NULL;
+ else
+ varinfo[i].vardefexpr = pg_strdup(PQgetvalue(res, i, i_vardefexpr));
+
+ if (strlen(varinfo[i].rolname) == 0)
+ write_msg(NULL, "WARNING: owner of variable \"%s\" appears to be invalid\n",
+ varinfo[i].dobj.name);
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ vtype = findTypeByOid(varinfo[i].vartype);
+ addObjectDependency(&varinfo[i].dobj, vtype->dobj.dumpId);
+ }
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpVariable
+ * dump the definition of the given variables
+ */
+static void
+dumpVariable(Archive *fout, VariableInfo *varinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ const char *varname;
+ const char *vartypname;
+ const char *vardefexpr;
+
+ /* Skip if not to be dumped */
+ if (!varinfo->dobj.dump || dopt->dataOnly)
+ return;
+
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ varname = fmtQualifiedDumpable(varinfo);
+ vartypname = varinfo->vartypname;
+ vardefexpr = varinfo->vardefexpr;
+
+ appendPQExpBuffer(delq, "DROP VARIABLE %s;\n",
+ varname);
+
+ appendPQExpBuffer(query, "CREATE VARIABLE %s AS %s",
+ varname, vartypname);
+
+ if (vardefexpr)
+ appendPQExpBuffer(query, " DEFAULT %s",
+ vardefexpr);
+
+ appendPQExpBuffer(query, ";\n");
+
+ ArchiveEntry(fout, varinfo->dobj.catId, varinfo->dobj.dumpId,
+ varinfo->dobj.name,
+ NULL,
+ NULL,
+ varinfo->rolname, false,
+ "VARIABLE", SECTION_PRE_DATA,
+ query->data, delq->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ if (varinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, "VARIABLE", varname,
+ NULL, varinfo->rolname,
+ varinfo->dobj.catId, 0, varinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -9849,6 +10052,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_VARIABLE:
+ dumpVariable(fout, (VariableInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -17935,6 +18141,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_OPFAMILY:
case DO_COLLATION:
case DO_CONVERSION:
+ case DO_VARIABLE:
case DO_TABLE:
case DO_ATTRDEF:
case DO_PROCLANG:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 1448005f30..0d49bb7ed7 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -84,7 +84,8 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_VARIABLE
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -625,6 +626,22 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/*
+ * The VariableInfo struct is used to represent schema variables
+ */
+typedef struct _VariableInfo
+{
+ DumpableObject dobj;
+ Oid vartype;
+ char *vartypname;
+ char *rolname; /* name of owner, or empty string */
+ char *vardefexpr;
+ char *varacl;
+ char *rvaracl;
+ char *initvaracl;
+ char *initrvaracl;
+} VariableInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -725,5 +742,6 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern void getVariables(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index ec751a7c23..2a67766ed4 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2601,6 +2601,38 @@ my %tests = (
},
},
+ 'CREATE VARIABLE test_variable' => {
+ all_runs => 1,
+ catch_all => 'CREATE ... commands',
+ create_order => 61,
+ create_sql => 'CREATE VARIABLE dump_test.variable AS integer DEFAULT 0;',
+ regexp => qr/^
+ \QCREATE VARIABLE dump_test.variable AS integer DEFAULT 0;\E/xm,
+ like => {
+ binary_upgrade => 1,
+ clean => 1,
+ clean_if_exists => 1,
+ createdb => 1,
+ defaults => 1,
+ exclude_test_table => 1,
+ exclude_test_table_data => 1,
+ no_blobs => 1,
+ no_privs => 1,
+ no_owner => 1,
+ only_dump_test_schema => 1,
+ pg_dumpall_dbprivs => 1,
+ schema_only => 1,
+ section_pre_data => 1,
+ test_schema_plus_blobs => 1,
+ with_oids => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_test_table => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1,
+ role => 1,
+ section_post_data => 1, }, },
+
'CREATE VIEW test_view' => {
create_order => 61,
create_sql => 'CREATE VIEW dump_test.test_view
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..73a752fd7e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -853,6 +853,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
break;
}
break;
+ case 'V': /* Variables */
+ success = listVariables(pattern, show_verbose);
+ break;
case 'x': /* Extensions */
if (show_verbose)
success = listExtensionContents(pattern);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 80d8338b96..d645bba7af 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4178,6 +4178,80 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
return true;
}
+/*
+ * \dV
+ *
+ * listVariables()
+ */
+bool
+listVariables(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false, false, false, false, false};
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname as \"%s\",\n"
+ " v.varname as \"%s\",\n"
+ " pg_catalog.format_type(v.vartype, v.vartypmod) as \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(v.varowner) as \"%s\",\n"
+ " pg_catalog.pg_get_expr(v.vardefexpr, 0) as \"%s\"",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Type"),
+ gettext_noop("Owner"),
+ gettext_noop("Default"));
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_variable v"
+ "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = v.varnamespace");
+
+ appendPQExpBufferStr(&buf, "\nWHERE true\n");
+ if (!pattern)
+ appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
+ " AND n.nspname <> 'information_schema'\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "v.varname", NULL,
+ "pg_catalog.pg_variable_is_visible(v.oid)");
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ /*
+ * Most functions in this file are content to print an empty table when
+ * there are no matching objects. We intentionally deviate from that
+ * here, but only in !quiet mode, for historical reasons.
+ */
+ if (PQntuples(res) == 0 && !pset.quiet)
+ {
+ if (pattern)
+ psql_error("Did not find any schema variable named \"%s\".\n",
+ pattern);
+ else
+ psql_error("Did not find any schema variables.\n");
+ }
+ else
+ {
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of variables");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+ }
+
+ PQclear(res);
+ return true;
+}
/*
* \dFp
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..ecc4e3a531 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
/* \dt, \di, \ds, \dS, etc. */
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
+/* \dV */
+extern bool listVariables(const char *pattern, bool varbose);
+
/* \dD */
extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 316030d358..adcc36cb6e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
@@ -257,6 +257,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[S+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dV [PATTERN] list variables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index bb696f8ee9..ebec00fe1f 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -805,6 +805,22 @@ static const SchemaQuery Query_for_list_of_statistics = {
NULL
};
+static const SchemaQuery Query_for_list_of_variables = {
+ /* min_server_version */
+ 0,
+ /* catname */
+ "pg_catalog.pg_variable v",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_variable_is_visible(v.oid)",
+ /* namespace */
+ "v.varnamespace",
+ /* result */
+ "pg_catalog.quote_ident(v.varname)",
+ /* qualresult */
+ NULL
+};
/*
* Queries to get lists of names of various kinds of things, possibly
@@ -1249,6 +1265,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
{"USER MAPPING FOR", NULL, NULL, NULL},
+ {"VARIABLE", NULL, NULL, &Query_for_list_of_variables},
{"VIEW", NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
@@ -1604,7 +1621,7 @@ psql_completion(const char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
- "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LET", "LISTEN", "LOAD", "LOCK",
"MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
"RESET", "REVOKE", "ROLLBACK",
@@ -1621,9 +1638,9 @@ psql_completion(const char *text, int start, int end)
"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
- "\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+ "\\dm", "\\dn", "\\do", "\\dO", "\\dp"
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+ "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", "\\dV",
"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
"\\endif", "\\errverbose", "\\ev",
"\\f",
@@ -2837,6 +2854,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
COMPLETE_WITH_LIST2("GROUP", "ROLE");
+/* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
+ /* Complete CREATE VARIABLE <name> with AS */
+ else if (TailMatches3("CREATE", "VARIABLE", MatchAny))
+ COMPLETE_WITH_CONST("AS");
+ /* Complete CREATE VARIABLE <name> with AS types*/
+ else if (TailMatches4("CREATE", "VARIABLE", MatchAny, "AS"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE VIEW <name> with AS */
else if (TailMatches3("CREATE", "VIEW", MatchAny))
@@ -2890,7 +2915,7 @@ psql_completion(const char *text, int start, int end)
/* DISCARD */
else if (Matches1("DISCARD"))
- COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP");
+ COMPLETE_WITH_LIST5("ALL", "PLANS", "SEQUENCES", "TEMP", "VARIABLES");
/* DO */
else if (Matches1("DO"))
@@ -2992,6 +3017,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP VARIABLE */
+ else if (Matches2("DROP", "VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ else if (Matches3("DROP", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3002,14 +3033,14 @@ psql_completion(const char *text, int start, int end)
* Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "ANALYZE", "VERBOSE");
+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "ANALYZE", "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "ANALYZE"))
- COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "VERBOSE");
+ COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "VERBOSE") ||
Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
- COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
+ COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "LET");
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
@@ -3118,6 +3149,7 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'ALL ROUTINES IN SCHEMA'"
" UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
" UNION SELECT 'ALL TABLES IN SCHEMA'"
+ " UNION SELECT 'ALL VARIABLES IN SCHEMA'"
" UNION SELECT 'DATABASE'"
" UNION SELECT 'DOMAIN'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
@@ -3131,14 +3163,16 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'SEQUENCE'"
" UNION SELECT 'TABLE'"
" UNION SELECT 'TABLESPACE'"
- " UNION SELECT 'TYPE'");
+ " UNION SELECT 'TYPE'"
+ " UNION SELECT 'VARIABLE'");
}
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
- COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA",
+ COMPLETE_WITH_LIST6("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
- "TABLES IN SCHEMA");
+ "TABLES IN SCHEMA",
+ "VARIABLES IN SCHEMA");
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
@@ -3172,6 +3206,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches1("TYPE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+ else if (TailMatches1("VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
COMPLETE_WITH_CONST("TO");
else
@@ -3324,7 +3360,7 @@ psql_completion(const char *text, int start, int end)
/* PREPARE xx AS */
else if (Matches3("PREPARE", MatchAny, "AS"))
- COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
+ COMPLETE_WITH_LIST5("SELECT", "UPDATE", "INSERT", "DELETE FROM", "LET");
/*
* PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -3547,6 +3583,14 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
COMPLETE_WITH_CONST("=");
+/* LET --- can be inside EXPLAIN, PREPARE etc */
+ /* If prev. word is LET suggest a list of variables */
+ else if (TailMatches1("LET"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ /* Complete LET <variable> with "=" */
+ else if (TailMatches2("LET", MatchAny))
+ COMPLETE_WITH_CONST("=");
+
/* USER MAPPING */
else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 46c271a46c..3e38a05e55 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -180,7 +180,8 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_VARIABLE /* pg_variable */
} ObjectClass;
#define LAST_OCLASS OCLASS_TRANSFORM
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 24915824ca..dae80c20a8 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -360,4 +360,10 @@ DECLARE_UNIQUE_INDEX(pg_subscription_subname_index, 6115, on pg_subscription usi
DECLARE_UNIQUE_INDEX(pg_subscription_rel_srrelid_srsubid_index, 6117, on pg_subscription_rel using btree(srrelid oid_ops, srsubid oid_ops));
#define SubscriptionRelSrrelidSrsubidIndexId 6117
+DECLARE_UNIQUE_INDEX(pg_variable_oid_index, 4288, on pg_variable using btree(oid oid_ops));
+#define VariableObjectIndexId 4288
+
+DECLARE_UNIQUE_INDEX(pg_variable_varname_nsp_index, 4289, on pg_variable using btree(varname name_ops, varnamespace oid_ops));
+#define VariableNameNspIndexId 4289
+
#endif /* INDEXING_H */
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 7991de5e21..75068d7e92 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -75,10 +75,13 @@ extern Oid RangeVarGetAndCheckCreationNamespace(RangeVar *newRelation,
extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
+extern bool VariableIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
+extern bool VariableIsVisible(Oid varid);
+
extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
bool expand_variadic,
@@ -145,6 +148,10 @@ extern void SetTempNamespaceState(Oid tempNamespaceId,
Oid tempToastNamespaceId);
extern void ResetTempTableNamespace(void);
+extern List *NamesFromList(List *names);
+extern Oid lookup_variable(const char *nspname, const char *varname, bool missing_ok);
+extern Oid identify_variable(List *names, char **attrname, bool *not_uniq);
+
extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
extern OverrideSearchPath *CopyOverrideSearchPath(OverrideSearchPath *path);
extern bool OverrideSearchPathMatchesCurrent(OverrideSearchPath *path);
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index d0410f5586..56deef1a45 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -57,6 +57,7 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a14651010f..61cbe65805 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5961,6 +5961,9 @@
proname => 'pg_collation_is_visible', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid',
prosrc => 'pg_collation_is_visible' },
+{ oid => '4187', descr => 'is schema variable visible in search path?',
+ proname => 'pg_variable_is_visible', procost => '10', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid', prosrc => 'pg_variable_is_visible' },
{ oid => '2854', descr => 'get OID of current session\'s temp schema, if any',
proname => 'pg_my_temp_schema', provolatile => 's', proparallel => 'r',
diff --git a/src/include/catalog/pg_variable.h b/src/include/catalog/pg_variable.h
new file mode 100644
index 0000000000..34f4c34202
--- /dev/null
+++ b/src/include/catalog/pg_variable.h
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.h
+ * definition of schema variables system catalog (pg_variables)
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_variable.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VARIABLE_H
+#define PG_VARIABLE_H
+
+#include "catalog/genbki.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable_d.h"
+#include "utils/acl.h"
+
+/* ----------------
+ * pg_variable definition. cpp turns this into
+ * typedef struct FormData_pg_variable
+ * ----------------
+ */
+CATALOG(pg_variable,4287,VariableRelationId)
+{
+ NameData varname; /* variable name */
+ Oid varnamespace; /* OID of namespace containing variable class */
+ Oid vartype; /* OID of entry in pg_type for variable's type */
+ int32 vartypmod; /* typmode for variable's type */
+ Oid varowner; /* class owner */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+
+ /* list of expression trees for variable default (NULL if none) */
+ pg_node_tree vardefexpr BKI_DEFAULT(_null_);
+
+ aclitem varacl[1] BKI_DEFAULT(_null_); /* access permissions */
+
+#endif
+} FormData_pg_variable;
+
+/* ----------------
+ * Form_pg_variable corresponds to a pointer to a tuple with
+ * the format of pg_variable relation.
+ * ----------------
+ */
+typedef FormData_pg_variable *Form_pg_variable;
+
+typedef struct Variable
+{
+ Oid oid;
+ char *name;
+ Oid namespace;
+ Oid typid;
+ int32 typmod;
+ Oid owner;
+ Node *defexpr;
+ Acl *acl;
+} Variable;
+
+/* returns fields from pg_variable table */
+extern char *get_schema_variable_name(Oid varid);
+extern void get_schema_variable_type_typmod(Oid varid, Oid *typid, int32 *typmod);
+
+/* returns name of variable based on current search path */
+extern char *schema_variable_get_name(Oid varid);
+
+extern Variable *GetVariable(Oid varid, bool missing_ok);
+extern ObjectAddress VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Node *varDefexpr,
+ bool if_not_exists);
+
+
+#endif /* PG_VARIABLE_H */
diff --git a/src/include/commands/schemavariable.h b/src/include/commands/schemavariable.h
new file mode 100644
index 0000000000..dd3239b236
--- /dev/null
+++ b/src/include/commands/schemavariable.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavariable.h
+ * prototypes for schemavariable.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/schemavariable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SCHEMAVARIABLE_H
+#define SCHEMAVARIABLE_H
+
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable.h"
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "utils/queryenvironment.h"
+
+extern char *VariableGetName(Variable *var);
+
+extern void ResetSchemaVariableCache(void);
+
+extern void RemoveVariableById(Oid varid);
+extern ObjectAddress DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt);
+
+extern Datum GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid);
+extern void SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod);
+
+extern void doLetStmt(PlannedStmt *pstmt, ParamListInfo params, QueryEnvironment *queryEnv, const char *queryString);
+
+#endif
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77616..cca30f275b 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -344,11 +345,11 @@ typedef struct ExprEvalStep
TupleDesc argdesc;
} nulltest_row;
- /* for EEOP_PARAM_EXEC/EXTERN */
+ /* for EEOP_PARAM_EXEC/EXTERN/VARIABLE */
struct
{
- int paramid; /* numeric ID for parameter */
- Oid paramtype; /* OID of parameter's datatype */
+ int paramid; /* numeric ID for parameter */
+ Oid paramtype; /* OID of parameter's datatype */
} param;
/* for EEOP_PARAM_CALLBACK */
diff --git a/src/include/executor/svariableReceiver.h b/src/include/executor/svariableReceiver.h
new file mode 100644
index 0000000000..8c8117701f
--- /dev/null
+++ b/src/include/executor/svariableReceiver.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.h
+ * prototypes for svariableReceiver.c
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/svariableReceiver.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SVARIABLE_RECEIVER_H
+#define SVARIABLE_RECEIVER_H
+
+#include "tcop/dest.h"
+
+
+extern DestReceiver *CreateVariableDestReceiver(void);
+
+extern void SetVariableDestReceiverParams(DestReceiver *self, Oid varid);
+
+#endif /* SVARIABLE_RECEIVER_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 018f50bbb7..08b4b2c2f2 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -564,6 +564,8 @@ typedef struct EState
/* The per-query shared memory area to use for parallel execution. */
struct dsa_area *es_query_dsa;
+ int es_result_variable; /* Oid of target variable */
+
/*
* JIT information. es_jit_flags indicates whether JIT should be performed
* and with which options. es_jit is created on-demand when JITing is
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 697d3d7a5f..dd7fd8ed42 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -348,6 +348,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSchemaVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
@@ -419,6 +420,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
+ T_LetStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -663,6 +665,7 @@ typedef enum CmdType
CMD_DELETE,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
+ CMD_PLAN_UTILITY, /* only let stmt now, requires planning */
CMD_NOTHING /* dummy command for instead nothing rules
* with qual */
} CmdType;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07ab1a3dde..2d4a3cb1b6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -84,7 +84,9 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
-#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_READ (1<<12) /* for variables */
+#define ACL_WRITE (1<<13) /* for variables */
+#define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -121,6 +123,7 @@ typedef struct Query
int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */
+ int resultVariable; /* Oid of target variable or 0 */
bool hasAggs; /* has aggregates in tlist or havingQual */
bool hasWindowFuncs; /* has window functions in tlist */
@@ -1505,6 +1508,18 @@ typedef struct UpdateStmt
WithClause *withClause; /* WITH clause */
} UpdateStmt;
+/* ----------------------
+ * Let Statement
+ * ----------------------
+ */
+typedef struct LetStmt
+{
+ NodeTag type;
+ List *target; /* target variable */
+ Node *selectStmt; /* source expression */
+ int location;
+} LetStmt;
+
/* ----------------------
* Select Statement
*
@@ -1682,6 +1697,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -2497,6 +2513,19 @@ typedef struct AlterSeqStmt
bool missing_ok; /* skip error if a role is missing? */
} AlterSeqStmt;
+/* ----------------------
+ * {Create|Alter} VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSchemaVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the type of variable */
+ Node *defexpr; /* default expression */
+ bool if_not_exists; /* do nothing if it already exists */
+} CreateSchemaVarStmt;
+
/* ----------------------
* Create {Aggregate|Operator|Type} Statement
* ----------------------
@@ -3238,7 +3267,8 @@ typedef enum DiscardMode
DISCARD_ALL,
DISCARD_PLANS,
DISCARD_SEQUENCES,
- DISCARD_TEMP
+ DISCARD_TEMP,
+ DISCARD_VARIABLES
} DiscardMode;
typedef struct DiscardStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd03a..2588f1455f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -43,7 +43,7 @@ typedef struct PlannedStmt
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete|utility */
+ CmdType commandType; /* select|let|insert|update|delete|utility */
uint64 queryId; /* query identifier (copied from Query) */
@@ -81,6 +81,9 @@ typedef struct PlannedStmt
*/
List *rootResultRelations;
+ /* Oid of target variable for LET command */
+ Oid resultVariable;
+
List *subplans; /* Plan trees for SubPlan expressions; note
* that some could be NULL */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..b366471940 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -229,13 +229,17 @@ typedef struct Const
* of the `paramid' field contain the SubLink's subLinkId, and
* the low-order 16 bits contain the column number. (This type
* of Param is also converted to PARAM_EXEC during planning.)
+ *
+ * PARAM_SCHEMA_VARIABLE: The parameter is a access to schema variable
+ * paramid holds varid.
*/
typedef enum ParamKind
{
PARAM_EXTERN,
PARAM_EXEC,
PARAM_SUBLINK,
- PARAM_MULTIEXPR
+ PARAM_MULTIEXPR,
+ PARAM_SCHEMA_VARIABLE
} ParamKind;
typedef struct Param
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db40147b..d3ed3f4d0f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -231,6 +231,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("let", LET, UNRESERVED_KEYWORD)
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -434,6 +435,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 0230543810..f7c2e67f33 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -69,7 +69,9 @@ typedef enum ParseExprKind
EXPR_KIND_TRIGGER_WHEN, /* WHEN condition in CREATE TRIGGER */
EXPR_KIND_POLICY, /* USING or WITH CHECK expr in policy */
EXPR_KIND_PARTITION_EXPRESSION, /* PARTITION BY expression */
- EXPR_KIND_CALL_ARGUMENT /* procedure argument in CALL */
+ EXPR_KIND_CALL_ARGUMENT, /* procedure argument in CALL */
+ EXPR_KIND_VARIABLE_DEFAULT, /* default value for schema variable */
+ EXPR_KIND_LET /* LET assignment (should be same like UPDATE) */
} ParseExprKind;
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index ec6e0c102f..1ee199ed8f 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -32,6 +32,16 @@ extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
int attrno,
List *indirection,
int location);
+extern Node *transformAssignmentIndirection(ParseState *pstate,
+ Node *basenode,
+ const char *targetName,
+ bool targetIsArray,
+ Oid targetTypeId,
+ int32 targetTypMod,
+ Oid targetCollation,
+ ListCell *indirection,
+ Node *rhs,
+ int location);
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
char *colname, int attrno,
List *indirection,
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 82f0f2e741..c49b653555 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -96,7 +96,8 @@ typedef enum
DestCopyOut, /* results sent to COPY TO code */
DestSQLFunction, /* results sent to SQL-language func mgr */
DestTransientRel, /* results sent to transient relation */
- DestTupleQueue /* results sent to tuple queue */
+ DestTupleQueue, /* results sent to tuple queue */
+ DestVariable /* results sents to schema variable */
} CommandDest;
/* ----------------
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f4d4be8d0d..c624d8dd0b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -147,9 +147,11 @@ typedef ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
+#define ACL_READ_CHR 'S' /* 'R' is occupated by old RULE priv */
+#define ACL_WRITE_CHR 'W'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcSW"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -166,6 +168,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_READ|ACL_WRITE)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -253,6 +256,8 @@ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_variable_aclmask(Oid var_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode);
@@ -269,6 +274,7 @@ extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
+extern AclResult pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, ObjectType objtype,
const char *objectname);
@@ -305,6 +311,7 @@ extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool pg_publication_ownercheck(Oid pub_oid, Oid roleid);
extern bool pg_subscription_ownercheck(Oid sub_oid, Oid roleid);
extern bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid);
+extern bool pg_variable_ownercheck(Oid stat_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index e55ea4035b..cb3f4aaca9 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -122,6 +122,7 @@ extern bool get_func_leakproof(Oid funcid);
extern float4 get_func_cost(Oid funcid);
extern float4 get_func_rows(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
+extern Oid get_varname_varid(const char *varname, Oid varnamespace);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4f333586ee..453699be3c 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -107,9 +107,11 @@ enum SysCacheIdentifier
TYPENAMENSP,
TYPEOID,
USERMAPPINGOID,
- USERMAPPINGUSERSERVER
+ USERMAPPINGUSERSERVER,
+ VARIABLENAMENSP,
+ VARIABLEOID
-#define SysCacheSize (USERMAPPINGUSERSERVER + 1)
+#define SysCacheSize (VARIABLEOID + 1)
};
extern void InitCatalogCache(void);
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b500..48286f8e1a 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -105,5 +105,7 @@ ORDER BY 1, 2;
pg_index | indpred | pg_node_tree
pg_largeobject | data | bytea
pg_largeobject_metadata | lomacl | aclitem[]
-(11 rows)
+ pg_variable | varacl | aclitem[]
+ pg_variable | vardefexpr | pg_node_tree
+(13 rows)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 0aa5357917..848b041a4b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -163,6 +163,7 @@ pg_ts_parser|t
pg_ts_template|t
pg_type|t
pg_user_mapping|t
+pg_variable|t
point_tbl|t
polygon_tbl|t
quad_box_tbl|t
diff --git a/src/test/regress/expected/schema_variables.out b/src/test/regress/expected/schema_variables.out
new file mode 100644
index 0000000000..f2017b8da9
--- /dev/null
+++ b/src/test/regress/expected/schema_variables.out
@@ -0,0 +1,306 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+DROP VARIABLE var1, var2;
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT var1;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT READ ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT var1;
+ var1
+------
+
+(1 row)
+
+SET ROLE TO DEFAULT;
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to work
+LET var1 = 333;
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var1;
+ERROR: permission denied for schema variable var1
+-- should to work;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: ((g.v)::numeric = var1)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var1;
+SELECT * FROM schema_var_view;
+ var1
+------
+ 333
+(1 row)
+
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ var1
+------
+
+(1 row)
+
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+----------------------------
+ Result
+ Output: 3.14159265358979
+(2 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+SELECT inc(1);
+ inc
+-----
+ 1
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 2
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 3
+(1 row)
+
+SELECT inc(1) FROM generate_series(1,10);
+ inc
+-----
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+(10 rows)
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+-- composite variables
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+\d v1
+\d v2
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+SELECT v1;
+ v1
+------------
+ (1,2,3.14)
+(1 row)
+
+SELECT v2;
+ v2
+---------------
+ (10,20,31.40)
+(1 row)
+
+SELECT (v1).*;
+ x | y | z
+---+---+------
+ 1 | 2 | 3.14
+(1 row)
+
+SELECT (v2).*;
+ x | y | z
+----+----+-------
+ 10 | 20 | 31.40
+(1 row)
+
+SELECT v1.x + v1.z;
+ ?column?
+----------
+ 4.14
+(1 row)
+
+SELECT v2.x + v2.z;
+ ?column?
+----------
+ 41.40
+(1 row)
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+SELECT v2.x;
+ERROR: permission denied for schema variable v2
+SET ROLE TO DEFAULT;
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+DROP ROLE var_test_role;
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+ relname
+----------
+ pg_class
+(1 row)
+
+-- should to fail
+SELECT varx.xxx;
+ERROR: type text is not composite
+-- variables can be updated under RO transaction
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+SELECT varx;
+ varx
+-------
+ hello
+(1 row)
+
+DROP VARIABLE varx;
+CREATE TYPE t1 AS (a int, b numeric, c text);
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+ v1
+----------------------------
+ (1,3.14159265358979,hello)
+(1 row)
+
+LET v1.b = 10.2222;
+SELECT v1;
+ v1
+-------------------
+ (1,10.2222,hello)
+(1 row)
+
+-- should to fail
+LET v1.x = 10;
+ERROR: cannot assign to field "x" of column "x" because there is no such column in data type t1
+LINE 1: LET v1.x = 10;
+ ^
+DROP VARIABLE v1;
+DROP TYPE t1;
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+ va1
+------------
+ {10.1,2.1}
+(1 row)
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+ va2
+--------------------
+ (10.2,"{0.0,0.0}")
+(1 row)
+
+LET va2.b[1] = 10.3;
+SELECT va2;
+ va2
+---------------------
+ (10.2,"{10.3,0.0}")
+(1 row)
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+ v1
+------------------
+ 6.28318530717958
+(1 row)
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+ v2
+--------------------------
+ (3.14159265358979,Hello)
+(1 row)
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+ERROR: cannot drop type t2 because other objects depend on it
+DETAIL: schema variable v2 depends on type t2
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 16f979c8d9..9bf379b87b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml schema_variables
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 42632be675..42bf4ecb3f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -191,3 +191,4 @@ test: partition_aggregate
test: event_trigger
test: fast_default
test: stats
+test: schema_variables
diff --git a/src/test/regress/sql/schema_variables.sql b/src/test/regress/sql/schema_variables.sql
new file mode 100644
index 0000000000..619b6ee4c0
--- /dev/null
+++ b/src/test/regress/sql/schema_variables.sql
@@ -0,0 +1,213 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+
+DROP VARIABLE var1, var2;
+
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+
+CREATE ROLE var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT READ ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+-- should to work
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to work
+LET var1 = 333;
+
+SET ROLE TO DEFAULT;
+
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+
+SELECT secure_var();
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT public.var1;
+
+-- should to work;
+SELECT secure_var();
+
+SET ROLE TO DEFAULT;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+
+CREATE VIEW schema_var_view AS SELECT var1;
+
+SELECT * FROM schema_var_view;
+
+\c -
+
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+
+LET var1 = pi();
+
+SELECT var1;
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+
+EXECUTE var_pp(100, 1.23456);
+
+SELECT var1;
+
+CREATE VARIABLE var3 AS int;
+
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT inc(1);
+SELECT inc(1);
+SELECT inc(1);
+
+SELECT inc(1) FROM generate_series(1,10);
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+LET var3 = 0;
+
+SET ROLE TO DEFAULT;
+
+DROP VIEW schema_var_view;
+
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+
+-- composite variables
+
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+
+\d v1
+\d v2
+
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+
+SELECT v1;
+SELECT v2;
+SELECT (v1).*;
+SELECT (v2).*;
+
+SELECT v1.x + v1.z;
+SELECT v2.x + v2.z;
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+
+SELECT v2.x;
+
+SET ROLE TO DEFAULT;
+
+
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+DROP ROLE var_test_role;
+
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+
+-- should to fail
+SELECT varx.xxx;
+
+
+-- variables can be updated under RO transaction
+
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+
+SELECT varx;
+
+DROP VARIABLE varx;
+
+CREATE TYPE t1 AS (a int, b numeric, c text);
+
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+LET v1.b = 10.2222;
+SELECT v1;
+
+-- should to fail
+LET v1.x = 10;
+
+DROP VARIABLE v1;
+DROP TYPE t1;
+
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+LET va2.b[1] = 10.3;
+SELECT va2;
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-08-11 05:39 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-08-11 05:39 UTC (permalink / raw)
To: Gilles Darold <gilles.darold@dalibo.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
I am sending updated patch. It should to solve almost all Giles's and
Peter's objections.
I am not happy so executor access values of variables directly. It is most
simple implementation - and I hope so it is good enough, but now the access
to variables is too volatile. But it is works good enough for usability
testing.
I am thinking about some cache of used variables in ExprContext, so the
variable in one ExprContext will look like stable - more like PLpgSQL
variables.
Regards
Pavel
Attachments:
[text/x-patch] schema-variables-180811-01.patch (188.6K, ../../CAFj8pRDnoA3J2RM=WZJdYBXEiJUOfDv-gyJmp81Pq93jmrBb5g@mail.gmail.com/3-schema-variables-180811-01.patch)
download | inline diff:
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3bb48d4ccf..b863823160 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -359,6 +359,11 @@
<entry><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link></entry>
<entry>mappings of users to foreign servers</entry>
</row>
+
+ <row>
+ <entry><link linkend="catalog-pg-variable"><structname>pg_variable</structname></link></entry>
+ <entry>schema variables</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -11311,4 +11316,104 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
+ <sect1 id="catalog-pg-variable">
+ <title><structname>pg_variable</structname></title>
+
+ <indexterm zone="catalog-pg-variable">
+ <primary>pg_variable</primary>
+ </indexterm>
+
+ <para>
+ The table <structname>pg_variable</structname> holds metadata
+ of schema variables.
+ </para>
+
+ <table>
+ <title><structname>pg_views</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><structfield>oid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry></entry>
+ <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Name of the schema variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the namespace that contains this variable
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartype</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the data type of this variable.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartypmod</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ <structfield>vartypmod</structfield> records type-specific data
+ supplied at table creation time (for example, the maximum
+ length of a <type>varchar</type> column). It is passed to
+ type-specific input functions and length coercion functions.
+ The value will generally be -1 for types that do not need <structfield>vartypmod</structfield>.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>varowner</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>Owner of the variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>vardefexpr</structfield></entry>
+ <entry><type>pg_node_tree</type></entry>
+ <entry></entry>
+ <entry>The internal representation of the variable default value</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>
+ Access privileges; see
+ <xref linkend="sql-grant"/> and
+ <xref linkend="sql-revoke"/>
+ for details
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..0631c9ed56 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -47,6 +47,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterType SYSTEM "alter_type.sgml">
<!ENTITY alterUser SYSTEM "alter_user.sgml">
<!ENTITY alterUserMapping SYSTEM "alter_user_mapping.sgml">
+<!ENTITY alterVariable SYSTEM "alter_variable.sgml">
<!ENTITY alterView SYSTEM "alter_view.sgml">
<!ENTITY analyze SYSTEM "analyze.sgml">
<!ENTITY begin SYSTEM "begin.sgml">
@@ -99,6 +100,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createType SYSTEM "create_type.sgml">
<!ENTITY createUser SYSTEM "create_user.sgml">
<!ENTITY createUserMapping SYSTEM "create_user_mapping.sgml">
+<!ENTITY createVariable SYSTEM "create_variable.sgml">
<!ENTITY createView SYSTEM "create_view.sgml">
<!ENTITY deallocate SYSTEM "deallocate.sgml">
<!ENTITY declare SYSTEM "declare.sgml">
@@ -148,6 +150,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropUser SYSTEM "drop_user.sgml">
<!ENTITY dropUserMapping SYSTEM "drop_user_mapping.sgml">
<!ENTITY dropView SYSTEM "drop_view.sgml">
+<!ENTITY dropVariable SYSTEM "drop_variable.sgml">
<!ENTITY end SYSTEM "end.sgml">
<!ENTITY execute SYSTEM "execute.sgml">
<!ENTITY explain SYSTEM "explain.sgml">
@@ -155,6 +158,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY grant SYSTEM "grant.sgml">
<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
<!ENTITY insert SYSTEM "insert.sgml">
+<!ENTITY let SYSTEM "let.sgml">
<!ENTITY listen SYSTEM "listen.sgml">
<!ENTITY load SYSTEM "load.sgml">
<!ENTITY lock SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/alter_variable.sgml b/doc/src/sgml/ref/alter_variable.sgml
new file mode 100644
index 0000000000..6376ac716b
--- /dev/null
+++ b/doc/src/sgml/ref/alter_variable.sgml
@@ -0,0 +1,170 @@
+<!--
+doc/src/sgml/ref/alter_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-altervariable">
+ <indexterm zone="sql-altervariable">
+ <primary>ALTER VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>ALTER VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ALTER VARIABLE</refname>
+ <refpurpose>
+ change the definition of a variable
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="parameter">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>ALTER VARIABLE</command> changes the definition of an existing variable.
+ There are several subforms:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>OWNER</literal></term>
+ <listitem>
+ <para>
+ This form changes the owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RENAME</literal></term>
+ <listitem>
+ <para>
+ This form changes the name of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET SCHEMA</literal></term>
+ <listitem>
+ <para>
+ This form moves the variable into another schema.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </para>
+
+ <para>
+ You must own the variable to use <command>ALTER VARIABLE</command>.
+ To change the schema of a variable, you must also have
+ <literal>CREATE</literal> privilege on the new schema.
+ To alter the owner, you must also be a direct or indirect member of the new
+ owning role, and that role must have <literal>CREATE</literal> privilege on
+ the variable's schema. (These restrictions enforce that altering the owner
+ doesn't do anything you couldn't do by dropping and recreating the variable.
+ However, a superuser can alter ownership of any type anyway.)
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (possibly schema-qualified) of an existing variable to
+ alter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_name</replaceable></term>
+ <listitem>
+ <para>
+ The new name for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_owner</replaceable></term>
+ <listitem>
+ <para>
+ The user name of the new owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_schema</replaceable></term>
+ <listitem>
+ <para>
+ The new schema for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To rename a variable:
+<programlisting>
+ALTER VARIABLE foo RENAME TO boo;
+</programlisting>
+ </para>
+
+ <para>
+ To change the owner of the variable <literal>boo</literal>
+ to <literal>joe</literal>:
+<programlisting>
+ALTER VARIABLE boo OWNER TO joe;
+</programlisting>
+ </para>
+
+ <para>
+ To change the schema of the variable <literal>boo</literal>
+ to <literal>private</literal>:
+<programlisting>
+ALTER VARIABLE boo SET SCHEMA private;
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ This comman is a PostgreSQL extension.
+ </para>
+ </refsect1>
+
+ <refsect1 id="sql-altervariable-see-also">
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
new file mode 100644
index 0000000000..6099538813
--- /dev/null
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -0,0 +1,134 @@
+<!--
+doc/src/sgml/ref/create_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-createvariable">
+ <indexterm zone="sql-createvariable">
+ <primary>CREATE VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE VARIABLE</refname>
+ <refpurpose>define a new permissioned typed schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ]
+</synopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> creates a new schema variable.
+ These variables are scalar typed, non-transactional, and, like relations,
+ exist within a schema with access controlled via
+ <command>GRANT</command> and <command>REVOKE</command>.
+ </para>
+
+ <para>
+ The value of a schema variable is session-local. Retrieving
+ a variable's value will return NULL unless its value has been set
+ to something else in the current session.
+ </para>
+
+ <para>
+ Retrieval is done via the <function>get_schema_variable</function>dunxrion or the SQL
+ command <command>SELECT</command>. Setting of values is done via the
+ <function>set_schema_variable</function> function or the SQL command
+ <command>LET</command>.
+ Notably, while schema variables are in many ways a kind of table you cannot use
+ <command>UPDATE</command> on them.
+ </para>
+
+ <para>
+ For purposes of name uniqueness relation-like objects (e.g., tables, indexes)
+ within the same schema are considered. i.e., you cannot give a table and a
+ schema variable the same name. This is a consequence of them being treated
+ like relations for purposes of <command>SELECT</command>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF NOT EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the name already exists. A notice is issued in this case.
+ Note that type of the variable is not considered, nor could it be since the namespace
+ searched contains non-variable objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the data type of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Use <command>DROP VARIABLE</command> to remove a variable.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Create an integer variable <literal>var1</literal>:
+<programlisting>
+CREATE VARIABLE var1 AS integer;
+SELECT var1;
+</programlisting>
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> is a PostgreSQL feature.
+ <!-- The choice of wording here seems to be left to personal preference... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index 6b909b7232..d83ad811fd 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
+DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES }
</synopsis>
</refsynopsisdiv>
@@ -75,6 +75,17 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>VARIABLES</literal></term>
+ <listitem>
+ <para>
+ Resets the value of all schema variables. When variables
+ will be used later, then will be initialized again to
+ NULL or default value.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
new file mode 100644
index 0000000000..c1c1a2bd67
--- /dev/null
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -0,0 +1,93 @@
+<!--
+doc/src/sgml/ref/drop_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropvariable">
+ <indexterm zone="sql-dropvariable">
+ <primary>DROP VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP VARIABLE</refname>
+ <refpurpose>remove a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP VARIABLE</command> removes a schema variable.
+ A variable can only be dropped by its owner or a superuser.
+ <!-- this would suggest that we need an alter variable owner to command -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the variable does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To remove the schema variable <literal>var1</literal>:
+
+<programlisting>
+DROP VARIABLE var1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ <!-- create variable is a "PostgreSQL feature",
+ this is a "proprietary PostgreSQL command" ... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-createvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index ff64c7a3ba..a83920a7a1 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -79,6 +79,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE <replaceable>type_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
[ GROUP ] <replaceable class="parameter">role_name</replaceable>
@@ -167,6 +171,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
foreign servers,
large objects,
schemas,
+ schema variable
or tablespaces.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
@@ -385,6 +390,24 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>READ</literal></term>
+ <listitem>
+ <para>
+ Allows to read a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WRITE</literal></term>
+ <listitem>
+ <para>
+ Allows to set a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL PRIVILEGES</literal></term>
<listitem>
@@ -550,6 +573,8 @@ rolename=xxxx -- privileges granted to a role
C -- CREATE
c -- CONNECT
T -- TEMPORARY
+ S -- READ
+ w -- WRITE
arwdDxt -- ALL PRIVILEGES (for tables, varies for other objects)
* -- grant option for preceding privilege
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
new file mode 100644
index 0000000000..e8bf3f6dd4
--- /dev/null
+++ b/doc/src/sgml/ref/let.sgml
@@ -0,0 +1,90 @@
+<!--
+doc/src/sgml/ref/let.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-let">
+ <indexterm zone="sql-let">
+ <primary>LET</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>LET</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>LET</refname>
+ <refpurpose>change a schema variable's value</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable class="parameter">sql_expression</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ The <command>LET</command> command updates the specified schema variable' value.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>schema_variable</literal></term>
+ <listitem>
+ <para>
+ The name of schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sql expression</literal></term>
+ <listitem>
+ <para>
+ An SQL expression, the result is cast to the schema variable's type.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Example:
+<programlisting>
+CREATE VARIABLE myvar AS integer;
+LET myvar = 10;
+LET myvar = (SELECT sum(val) FROM tab);
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <!-- this feels like it needs to be more specific,
+ but I don't know enough to make it so -->
+ <literal>LET</literal> extends syntax defined in the SQL
+ standard. The standard knows <literal>SET</literal> command,
+ that is used for different purpouse in PostgreSQL.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 5317f8ccba..8435e05957 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -108,6 +108,12 @@ REVOKE [ GRANT OPTION FOR ]
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_name</replaceable> [, ...]
[ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { READ | WRITE } [, ...] | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..5fb82df51e 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -75,6 +75,7 @@
&alterType;
&alterUser;
&alterUserMapping;
+ &alterVariable;
&alterView;
&analyze;
&begin;
@@ -127,6 +128,7 @@
&createType;
&createUser;
&createUserMapping;
+ &createVariable;
&createView;
&deallocate;
&declare;
@@ -175,6 +177,7 @@
&dropType;
&dropUser;
&dropUserMapping;
+ &dropVariable;
&dropView;
&end;
&execute;
@@ -183,6 +186,7 @@
&grant;
&importForeignSchema;
&insert;
+ &let;
&listen;
&load;
&lock;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 0865240f11..1f7c4d1223 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -19,7 +19,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_publication.o pg_range.o \
pg_db_role_setting.o pg_shdepend.o pg_subscription.o pg_type.o \
- storage.o toasting.o
+ pg_variable.o storage.o toasting.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
@@ -46,7 +46,7 @@ CATALOG_HEADERS := \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
- pg_subscription_rel.h
+ pg_subscription_rel.h pg_variable.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 578e4c6592..86917e15a8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -57,6 +57,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_transform.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
@@ -112,6 +113,7 @@ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
+static void ExecGrant_Variable(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
@@ -284,6 +286,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -507,6 +512,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -609,6 +618,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_VARIABLE:
+ ExecGrant_Variable(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -768,6 +780,16 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_VARIABLE:
+ foreach(cell, objnames)
+ {
+ RangeVar *varvar = (RangeVar *) lfirst(cell);
+ Oid relOid;
+
+ relOid = lookup_variable(varvar->schemaname, varvar->relname, false);
+ objects = lappend_oid(objects, relOid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -855,6 +877,31 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
heap_close(rel, AccessShareLock);
}
break;
+ case OBJECT_VARIABLE:
+ {
+ ScanKeyData key;
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&key,
+ Anum_pg_variable_varnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(namespaceId));
+
+ rel = heap_open(VariableRelationId, AccessShareLock);
+ scan = heap_beginscan_catalog(rel, 1, &key);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ }
+ break;
+
default:
/* should not happen */
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
@@ -1018,6 +1065,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1215,6 +1266,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1441,6 +1498,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -3266,6 +3326,129 @@ ExecGrant_Type(InternalGrant *istmt)
heap_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Variable(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_VARIABLE;
+
+ relation = heap_open(VariableRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid varId = lfirst_oid(cell);
+ Form_pg_variable pg_variable_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_variable];
+ bool nulls[Natts_pg_variable];
+ bool replaces[Natts_pg_variable];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for schema variables %u", varId);
+
+ pg_variable_tuple = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_variable_tuple->varowner;
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple, Anum_pg_variable_varacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_VARIABLE, ownerId);
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ oldmembers = NULL;
+ }
+ else
+ {
+ old_acl = DatumGetAclPCopy(aclDatum);
+ /* Get the roles mentioned in the existing ACL */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+ }
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ varId, grantorId, OBJECT_VARIABLE,
+ NameStr(pg_variable_tuple->varname),
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ */
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_variable_varacl - 1] = true;
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(varId, VariableRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(VariableRelationId, varId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3298,6 +3481,10 @@ string_to_privilege(const char *privname)
return ACL_CONNECT;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
+ if (strcmp(privname, "read") == 0)
+ return ACL_READ;
+ if (strcmp(privname, "write") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized privilege type \"%s\"", privname)));
@@ -3333,6 +3520,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3456,6 +3647,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3566,6 +3760,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3710,6 +3907,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
+ case OBJECT_VARIABLE:
+ return pg_variable_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objtype: %d",
(int) objtype);
@@ -4499,6 +4698,67 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a variable.
+ */
+AclMode
+pg_variable_aclmask(Oid var_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_variable varForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the type's tuple from pg_type
+ */
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable with OID %u does not exist",
+ var_oid)));
+ varForm = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Now get the type's owner and ACL from the tuple
+ */
+ ownerId = varForm->varowner;
+
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple,
+ Anum_pg_variable_varacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_VARIABLE, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+
/*
* Exported routine for checking a user's access privileges to a column
*
@@ -4744,6 +5004,18 @@ pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a variable
+ */
+AclResult
+pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
+{
+ if (pg_variable_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Ownership check for a relation (specified by OID).
*/
@@ -5361,6 +5633,33 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a schema variables (specified by OID).
+ */
+bool
+pg_variable_ownercheck(Oid db_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(db_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("variable with OID %u does not exist", db_oid)));
+
+ ownerId = ((Form_pg_variable) GETSTRUCT(tuple))->varowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+
/*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
@@ -5486,6 +5785,10 @@ get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
+ case OBJECT_VARIABLE:
+ defaclobjtype = DEFACLOBJ_VARIABLE;
+ break;
+
default:
return NULL;
}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 4f1d365357..782ddb1655 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -59,6 +59,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -67,6 +68,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
@@ -1280,6 +1282,10 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_VARIABLE:
+ RemoveVariableById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2537,6 +2543,9 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case VariableRelationId:
+ return OCLASS_VARIABLE;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 0f67a122ed..81aaf454a8 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -39,6 +39,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
@@ -755,6 +756,71 @@ RelationIsVisible(Oid relid)
return visible;
}
+/*
+ * VariableIsVisible
+ * Determine whether a variable (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified variable name".
+ */
+bool
+VariableIsVisible(Oid varid)
+{
+ HeapTuple vartup;
+ Form_pg_variable varform;
+ Oid varnamespace;
+ bool visible;
+
+ vartup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(vartup))
+ elog(ERROR, "cache lookup failed for schema variable %u", varid);
+ varform = (Form_pg_variable) GETSTRUCT(vartup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ varnamespace = varform->varnamespace;
+ if (varnamespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, varnamespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another relation of the same name earlier in the path. So
+ * we must do a slow check for conflicting relations.
+ */
+ char *varname = NameStr(varform->varname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == varnamespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (OidIsValid(get_varname_varid(varname, namespaceId)))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(vartup);
+
+ return visible;
+}
+
+
/*
* TypenameGetTypid
@@ -2776,6 +2842,202 @@ TSConfigIsVisible(Oid cfgid)
return visible;
}
+/*
+ * When we know a variable name, then we can find variable simply
+ */
+Oid
+lookup_variable(const char *nspname, const char *varname, bool missing_ok)
+{
+ Oid namespaceId;
+ Oid varoid = InvalidOid;
+ ListCell *l;
+
+ if (nspname)
+ {
+ namespaceId = LookupExplicitNamespace(nspname, missing_ok);
+ if (!OidIsValid(namespaceId))
+ return InvalidOid;
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+
+ if (OidIsValid(varoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(varoid) && !missing_ok)
+ {
+ if (nspname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\".\"%s\" does not exist",
+ nspname, varname)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\" does not exist",
+ varname)));
+ }
+
+ return varoid;
+}
+
+List *
+NamesFromList(List *names)
+{
+ ListCell *l;
+ List *result = NIL;
+
+ foreach(l, names)
+ {
+ Node *n = lfirst(l);
+
+ if (IsA(n, String))
+ {
+ result = lappend(result, n);
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * identify_variable
+ *
+ * Returns oid of not ambigonuous variable specified by qualified path
+ * or InvalidOid. When the path is ambigonuous, then not_uniq flag is
+ * is true.
+ */
+Oid
+identify_variable(List *names, char **attrname, bool *not_uniq)
+{
+ char *a = NULL;
+ char *b = NULL;
+ char *c = NULL;
+ char *d = NULL;
+ Oid varoid_without_attr;
+ Oid varoid_with_attr;
+
+ *not_uniq = false;
+
+ switch (list_length(names))
+ {
+ case 1:
+ a = strVal(linitial(names));
+ return lookup_variable(NULL, a, true);
+
+ case 2:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+
+ /*
+ * a.b can mean "schema"."variable" or "variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(a, b, true);
+ varoid_with_attr = lookup_variable(NULL, a, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = b;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 3:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+
+ /*
+ * a.b.c can mean "catalog"."schema"."variable" or "schema"."variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(b, c, true);
+ varoid_with_attr = lookup_variable(a, b, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = c;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 4:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+ d = strVal(lfourth(names));
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ *attrname = d;
+ return lookup_variable(b, c, true);
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(names))));
+ break;
+ }
+}
/*
* DeconstructQualifiedName
@@ -4416,3 +4678,14 @@ pg_is_other_temp_schema(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(isOtherTempNamespace(oid));
}
+
+Datum
+pg_variable_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ if (!SearchSysCacheExists1(VARIABLEOID, ObjectIdGetDatum(oid)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_BOOL(VariableIsVisible(oid));
+}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7db942dcba..cc3d415e61 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -58,6 +58,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -489,6 +490,18 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
OBJECT_STATISTIC_EXT,
true
+ },
+ {
+ VariableRelationId,
+ VariableObjectIndexId,
+ VARIABLEOID,
+ VARIABLENAMENSP,
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ Anum_pg_variable_varowner,
+ Anum_pg_variable_varacl,
+ OBJECT_VARIABLE,
+ true
}
};
@@ -714,6 +727,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_VARIABLE */
+ {
+ "schema variable", OBJECT_VARIABLE
}
};
@@ -739,6 +756,7 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
bool missing_ok);
static ObjectAddress get_object_address_type(ObjectType objtype,
TypeName *typename, bool missing_ok);
+static ObjectAddress get_object_address_variable(List *object, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object,
bool missing_ok);
static ObjectAddress get_object_address_opf_member(ObjectType objtype,
@@ -996,6 +1014,10 @@ get_object_address(ObjectType objtype, Node *object,
missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_VARIABLE:
+ address = get_object_address_variable(castNode(List, object), missing_ok);
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
@@ -1848,16 +1870,20 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type \"%c\"", objtype),
- errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
+ errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
DEFACLOBJ_RELATION,
DEFACLOBJ_SEQUENCE,
DEFACLOBJ_FUNCTION,
DEFACLOBJ_TYPE,
- DEFACLOBJ_NAMESPACE)));
+ DEFACLOBJ_NAMESPACE,
+ DEFACLOBJ_VARIABLE)));
}
/*
@@ -1942,6 +1968,24 @@ textarray_to_strvaluelist(ArrayType *arr)
return list;
}
+/*
+ * Find the ObjectAddress for a type or domain
+ */
+static ObjectAddress
+get_object_address_variable(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ char *nspname = NULL;
+ char *varname = NULL;
+
+ ObjectAddressSet(address, VariableRelationId, InvalidOid);
+
+ DeconstructQualifiedName(object, &nspname, &varname);
+ address.objectId = lookup_variable(nspname, varname, missing_ok);
+
+ return address;
+}
+
/*
* SQL-callable version of get_object_address
*/
@@ -2131,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_TABCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
+ case OBJECT_VARIABLE:
objnode = (Node *) name;
break;
case OBJECT_ACCESS_METHOD:
@@ -2415,6 +2460,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
break;
+ case OBJECT_VARIABLE:
+ if (!pg_variable_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
default:
elog(ERROR, "unrecognized object type: %d",
(int) objtype);
@@ -3157,6 +3207,32 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_VARIABLE:
+ {
+ char *nspname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ if (VariableIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ appendStringInfo(&buffer, _("schema variable %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(varform->varname)));
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_TSPARSER:
{
HeapTuple tup;
@@ -3422,6 +3498,16 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new schemas belonging to role %s"),
rolename);
break;
+ case DEFACLOBJ_VARIABLE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s"),
+ rolename);
+ break;
default:
/* shouldn't get here */
if (nspname)
@@ -4070,6 +4156,10 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_VARIABLE:
+ appendStringInfoString(&buffer, "schema variable");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4962,6 +5052,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on variables");
+ break;
}
if (objname)
@@ -5121,6 +5215,33 @@ getObjectIdentityParts(const ObjectAddress *object,
}
break;
+ case OCLASS_VARIABLE:
+ {
+ char *schema;
+ char *varname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ schema = get_namespace_name_or_temp(varform->varnamespace);
+ varname = NameStr(varform->varname);
+
+ appendStringInfo(&buffer, "%s",
+ quote_qualified_identifier(schema, varname));
+
+ if (objname)
+ *objname = list_make2(schema, varname);
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
diff --git a/src/backend/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c
new file mode 100644
index 0000000000..ff71f8bf6a
--- /dev/null
+++ b/src/backend/catalog/pg_variable.c
@@ -0,0 +1,305 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.c
+ * schema variables
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_variable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+
+#include "nodes/makefuncs.h"
+
+#include "storage/lmgr.h"
+
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * Returns name of schema variable. When variable is not on path,
+ * then the name is qualified.
+ */
+char *
+schema_variable_get_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+ char *nspname;
+ char *result;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ if (VariableIsVisible(varid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ result = quote_qualified_identifier(nspname, varname);
+
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/*
+ * Returns varname field of pg_variable
+ */
+char *
+get_schema_variable_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ ReleaseSysCache(tup);
+
+ return varname;
+}
+
+/*
+ * Returns type, typmod of schema variable
+ */
+void
+get_schema_variable_type_typmod(Oid varid, Oid *typid, int32 *typmod)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ *typid = varform->vartype;
+ *typmod = varform->vartypmod;
+
+ ReleaseSysCache(tup);
+
+ return;
+}
+
+/*
+ * Fetch all fields of schema variable from the syscache.
+ */
+Variable *
+GetVariable(Oid varid, bool missing_ok)
+{
+ HeapTuple tup;
+ Variable *var;
+ Form_pg_variable varform;
+ Datum aclDatum;
+ Datum defexprDatum;
+ bool isnull;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (missing_ok)
+ return NULL;
+
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+ }
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ var = (Variable *) palloc(sizeof(Variable));
+ var->oid = varid;
+ var->name = pstrdup(NameStr(varform->varname));
+ var->namespace = varform->varnamespace;
+ var->typid = varform->vartype;
+ var->typmod = varform->vartypmod;
+ var->owner = varform->varowner;
+
+ /* Get defexpr */
+ defexprDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_vardefexpr,
+ &isnull);
+
+ if (!isnull)
+ var->defexpr = stringToNode(TextDatumGetCString(defexprDatum));
+ else
+ var->defexpr = NULL;
+
+ /* Get varacl */
+ aclDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_varacl,
+ &isnull);
+ if (!isnull)
+ var->acl = DatumGetAclPCopy(aclDatum);
+ else
+ var->acl = NULL;
+
+ ReleaseSysCache(tup);
+
+ return var;
+}
+
+ObjectAddress
+VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Node *varDefexpr,
+ bool if_not_exists)
+{
+ Acl *varacl;
+ NameData varname;
+ bool nulls[Natts_pg_variable];
+ Datum values[Natts_pg_variable];
+ Relation rel;
+ HeapTuple tup,
+ oldtup;
+ TupleDesc tupdesc;
+ ObjectAddress myself,
+ referenced;
+ Oid retval;
+ int i;
+
+ for (i = 0; i < Natts_pg_variable; i++)
+ {
+ nulls[i] = false;
+ values[i] = (Datum) 0;
+ }
+
+ namestrcpy(&varname, varName);
+ values[Anum_pg_variable_varname - 1] = NameGetDatum(&varname);
+ values[Anum_pg_variable_varnamespace - 1] = ObjectIdGetDatum(varNamespace);
+ values[Anum_pg_variable_vartype - 1] = ObjectIdGetDatum(varType);
+ values[Anum_pg_variable_vartypmod - 1] = Int32GetDatum(varTypmod);
+ values[Anum_pg_variable_varowner - 1] = ObjectIdGetDatum(varOwner);
+ /* proacl will be determined later */
+
+ if (varDefexpr)
+ values[Anum_pg_variable_vardefexpr - 1] = CStringGetTextDatum(nodeToString(varDefexpr));
+ else
+ nulls[Anum_pg_variable_vardefexpr - 1] = true;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+ tupdesc = RelationGetDescr(rel);
+
+ oldtup = SearchSysCache2(VARIABLENAMENSP,
+ PointerGetDatum(varName),
+ ObjectIdGetDatum(varNamespace));
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ if (if_not_exists)
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists, skipping",
+ varName)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists",
+ varName)));
+
+ heap_freetuple(oldtup);
+ heap_close(rel, RowExclusiveLock);
+
+ return InvalidObjectAddress;
+ }
+
+ varacl = get_user_default_acl(OBJECT_VARIABLE, varOwner,
+ varNamespace);
+
+ if (varacl != NULL)
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(varacl);
+ else
+ nulls[Anum_pg_variable_varacl - 1] = true;
+
+ tup = heap_form_tuple(tupdesc, values, nulls);
+ CatalogTupleInsert(rel, tup);
+
+ retval = HeapTupleGetOid(tup);
+
+ myself.classId = VariableRelationId;
+ myself.objectId = retval;
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = varNamespace;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on used type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = varType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on any roles mentioned in ACL */
+ if (varacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(varacl, &newmembers);
+ updateAclDependencies(VariableRelationId, retval, 0,
+ varOwner,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_freetuple(tup);
+
+ /* Post creation hook for new function */
+ InvokeObjectPostCreateHook(VariableRelationId, retval, 0);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..2cb5b1172d 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,7 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
- schemacmds.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
+ schemacmds.o seclabel.o sequence.o schemavariable.o statscmds.o subscriptioncmds.o \
tablecmds.o tablespace.o trigger.o tsearchcmds.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index eff325cc7d..a9d5e5e0ad 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -387,6 +387,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_VARIABLE:
{
ObjectAddress address;
Relation catalog;
@@ -504,6 +505,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
@@ -594,6 +596,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_TSDICT:
case OCLASS_TSTEMPLATE:
case OCLASS_TSCONFIG:
+ case OCLASS_VARIABLE:
{
Relation catalog;
@@ -852,6 +855,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_TABLESPACE:
case OBJECT_TSDICTIONARY:
case OBJECT_TSCONFIGURATION:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 01a999c2ac..fec2495e93 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -19,6 +19,7 @@
#include "commands/discard.h"
#include "commands/prepare.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "utils/guc.h"
#include "utils/portal.h"
@@ -48,6 +49,10 @@ DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
ResetTempTableNamespace();
break;
+ case DISCARD_VARIABLES:
+ ResetSchemaVariableCache();
+ break;
+
default:
elog(ERROR, "unrecognized DISCARD target: %d", stmt->target);
}
@@ -75,4 +80,5 @@ DiscardAll(bool isTopLevel)
ResetPlanCache();
ResetTempTableNamespace();
ResetSequenceCaches();
+ ResetSchemaVariableCache();
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e..426df246b3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -126,6 +126,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"TEXT SEARCH TEMPLATE", true},
{"TYPE", true},
{"USER MAPPING", true},
+ {"VARIABLE", true},
{"VIEW", true},
{NULL, false}
};
@@ -297,7 +298,8 @@ check_ddl_tag(const char *tag)
pg_strcasecmp(tag, "REVOKE") == 0 ||
pg_strcasecmp(tag, "DROP OWNED") == 0 ||
pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
+ pg_strcasecmp(tag, "SECURITY LABEL") == 0 ||
+ pg_strcasecmp(tag, "CREATE VARIABLE") == 0)
return EVENT_TRIGGER_COMMAND_TAG_OK;
/*
@@ -1146,6 +1148,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -1209,6 +1212,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
return true;
/*
@@ -2244,6 +2248,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2326,6 +2332,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b1556a..eb8c08baf3 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_PLAN_UTILITY:
/* OK */
break;
default:
diff --git a/src/backend/commands/schemavariable.c b/src/backend/commands/schemavariable.c
new file mode 100644
index 0000000000..208d0d20c4
--- /dev/null
+++ b/src/backend/commands/schemavariable.c
@@ -0,0 +1,470 @@
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
+#include "executor/executor.h"
+#include "executor/svariableReceiver.h"
+#include "nodes/execnodes.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/inval.h"
+#include "utils/memutils.h"
+#include "utils/lsyscache.h"
+#include "utils/snapmgr.h"
+#include "utils/syscache.h"
+
+/*
+ * The content of variables is not transactional. Due this fact the
+ * implementation of DROP can be simple, because although DROP VARIABLE
+ * can be reverted, the content of variable can be lost. In this example,
+ * DROP VARIABLE is same like reset variable.
+ */
+
+typedef struct SchemaVariableData
+{
+ Oid varid; /* pg_variable OID of this sequence (hash key) */
+ Oid typid; /* OID of the data type */
+ int32 typmod;
+ int16 typlen;
+ bool typbyval;
+ bool isnull;
+ bool freeval;
+ Datum value;
+ bool is_rowtype; /* true when variable is composite */
+ bool is_valid; /* true when variable was successfuly initialized */
+} SchemaVariableData;
+
+typedef SchemaVariableData *SchemaVariable;
+
+static HTAB *schemavarhashtab = NULL; /* hash table for session variables */
+static MemoryContext SchemaVariableMemoryContext = NULL;
+
+static bool first_time = true;
+static void create_schemavar_hashtable(void);
+static bool clean_cache_req = false;
+
+static void clean_cache(void);
+static void force_clean_cache(XactEvent event, void *arg);
+
+
+/*
+ * Save info about ncessity to clean hash table, because some
+ * schema variable was dropped. Don't do here more, recheck
+ * needs to be in transaction state.
+ */
+static void
+InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ if (cacheid != VARIABLEOID)
+ return;
+
+ clean_cache_req = true;
+}
+
+static void
+force_clean_cache(XactEvent event, void *arg)
+{
+ /*
+ * should continue only in transaction time, when
+ * syscache is available.
+ */
+ if (clean_cache_req && IsTransactionState())
+ {
+ clean_cache();
+ clean_cache_req = false;
+ }
+}
+
+static void
+clean_cache(void)
+{
+ HASH_SEQ_STATUS status;
+ SchemaVariable var;
+
+ if (!schemavarhashtab)
+ return;
+
+ hash_seq_init(&status, schemavarhashtab);
+
+ /*
+ * Every valid variable have to have entry in system
+ * catalog. Removed if there is nothing.
+ */
+ while ((var = (SchemaVariable) hash_seq_search(&status)) != NULL)
+ {
+ HeapTuple tp = InvalidOid;
+
+ tp = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var->varid));
+ if (!HeapTupleIsValid(tp))
+ {
+ elog(DEBUG1, "variable %d is removed from cache", var->varid);
+
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (hash_search(schemavarhashtab,
+ (void *) &var->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(DEBUG1, "hash table corrupted");
+ }
+ else
+ ReleaseSysCache(tp);
+ }
+}
+
+char *
+VariableGetName(Variable *var)
+{
+ char *nspname;
+
+ if (VariableIsVisible(var->oid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(var->namespace);
+
+ return quote_qualified_identifier(nspname, var->name);
+}
+
+/*
+ * Create the hash table for storing schema variables
+ */
+static void
+create_schemavar_hashtable(void)
+{
+ HASHCTL ctl;
+
+ /* set callbacks */
+ if (first_time)
+ {
+ CacheRegisterSyscacheCallback(VARIABLEOID,
+ InvalidateSchemaVarCacheCallback,
+ (Datum) 0);
+
+ RegisterXactCallback(force_clean_cache, NULL);
+
+ first_time = false;
+ }
+
+ /* needs own long life memory context */
+ if (SchemaVariableMemoryContext == NULL)
+ {
+ SchemaVariableMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "schema variables",
+ ALLOCSET_START_SMALL_SIZES);
+ }
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(SchemaVariableData);
+ ctl.hcxt = SchemaVariableMemoryContext;
+
+ schemavarhashtab = hash_create("Schema variables", 64, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/*
+ * Fast drop complete content of schema variables
+ */
+void
+ResetSchemaVariableCache(void)
+{
+ if (schemavarhashtab)
+ {
+ hash_destroy(schemavarhashtab);
+ schemavarhashtab = NULL;
+ }
+
+ if (SchemaVariableMemoryContext != NULL)
+ {
+ MemoryContextReset(SchemaVariableMemoryContext);
+ }
+}
+
+/*
+ * Drop variable by OID
+ */
+void
+RemoveVariableById(Oid varid)
+{
+ Relation rel;
+ HeapTuple tup;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ CatalogTupleDelete(rel, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Creates new variable - entry in pg_catalog.pg_variable table
+ */
+ObjectAddress
+DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt)
+{
+ Oid namespaceid;
+ AclResult aclresult;
+ Oid typid;
+ int32 typmod;
+ Oid varowner = GetUserId();
+
+ Node *cooked_default = NULL;
+
+ namespaceid =
+ RangeVarGetAndCheckCreationNamespace(stmt->variable, NoLock, NULL);
+
+ typenameTypeIdAndMod(pstate, stmt->typeName, &typid, &typmod);
+
+ aclresult = pg_type_aclcheck(typid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error_type(aclresult, typid);
+
+ if (stmt->defexpr)
+ {
+ cooked_default = transformExpr(pstate, stmt->defexpr,
+ EXPR_KIND_VARIABLE_DEFAULT);
+
+ cooked_default = coerce_to_specific_type(pstate,
+ cooked_default, typid, "DEFAULT");
+ }
+
+ return VariableCreate(stmt->variable->relname,
+ namespaceid,
+ typid,
+ typmod,
+ varowner,
+ cooked_default,
+ stmt->if_not_exists);
+}
+
+/*
+ * Try to search value in hash table. If doesn't
+ * exists insert it (and calculate defexpr if exists.
+ */
+static SchemaVariable
+PrepareSchemaVariableForReading(Oid varid)
+{
+ SchemaVariable svar;
+ Variable *var;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+ if (!found)
+ {
+ var = GetVariable(varid, false);
+ get_typlenbyval(var->typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = var->typid;
+ svar->typmod = var->typmod;
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+ svar->is_rowtype = type_is_rowtype(var->typid);
+
+ /* when we don't need calculate defexpr, value is valid already */
+ svar->is_valid = var->defexpr ? false : true;
+ }
+ else if (!svar->is_valid)
+ {
+ /* we need var to recalculate defexpr */
+ var = GetVariable(varid, false);
+ }
+ else
+ /* we don't need to go to sys cache */
+ var = NULL;
+
+ /*
+ * Initialize variable when it is necessary. It is fresh
+ * or last initialization was not successfull.
+ */
+ if (var != NULL && var->defexpr && !svar->is_valid)
+ {
+ MemoryContext oldcontext = NULL;
+
+ Datum value = (Datum) 0;
+ bool null;
+ EState *estate = NULL;
+ Expr *defexpr;
+ ExprState *defexprs;
+
+ /* Prepare default expr */
+ estate = CreateExecutorState();
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ defexpr = expression_planner((Expr *) var->defexpr);
+ defexprs = ExecInitExpr(defexpr, NULL);
+ value = ExecEvalExprSwitchContext(defexprs, GetPerTupleExprContext(estate), &null);
+
+ MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!null)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ FreeExecutorState(estate);
+ }
+
+ if (!svar->is_valid)
+ elog(ERROR, "the content of variable is not valid");
+
+ return svar;
+}
+
+/*
+ * Returns content of variable. We expext secured access now.
+ * Secure check should be done before.
+ */
+Datum
+GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid)
+{
+ SchemaVariable svar;
+
+ svar = PrepareSchemaVariableForReading(varid);
+ *isNull = svar->isnull;
+
+ if (expected_typid != svar->typid)
+ elog(ERROR, "type of variable \"%s\" is different than expected",
+ schema_variable_get_name(varid));
+
+ return (Datum) svar->value;
+}
+
+/*
+ * Write value to variable. We expect secured access in this moment.
+ * In this time, we recheck syschache about used type.
+ */
+void
+SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod)
+{
+ MemoryContext oldcontext = NULL;
+
+ SchemaVariable svar;
+ Oid var_typid;
+ int32 var_typmod;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+
+ get_schema_variable_type_typmod(varid, &var_typid, &var_typmod);
+
+ /* check types first */
+ if (var_typid != typid)
+ elog(ERROR, "type of expression is different than schema variable type");
+
+ if (found)
+ {
+ /* release current content first */
+ if (svar->freeval)
+ {
+ pfree(DatumGetPointer(svar->value));
+ svar->value = (Datum) 0;
+ svar->isnull = true;
+ svar->freeval = false;
+ }
+ }
+
+ get_typlenbyval(typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = typid;
+ svar->typmod = typmod;
+
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+
+ svar->is_rowtype = type_is_rowtype(typid);
+ svar->is_valid = false;
+
+ oldcontext = MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!isNull)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+}
+
+void
+doLetStmt(PlannedStmt *pstmt,
+ ParamListInfo params,
+ QueryEnvironment *queryEnv,
+ const char *queryString)
+{
+ QueryDesc *queryDesc;
+ DestReceiver *dest;
+
+ PushCopiedSnapshot(GetActiveSnapshot());
+ UpdateActiveSnapshotCommandId();
+
+ /* Create dest receiver for LET */
+ dest = CreateDestReceiver(DestVariable);
+
+ SetVariableDestReceiverParams(dest, pstmt->resultVariable);
+
+ /* Create a QueryDesc requesting no output */
+ queryDesc = CreateQueryDesc(pstmt, queryString,
+ GetActiveSnapshot(),
+ InvalidSnapshot,
+ dest, params, queryEnv, 0);
+
+ ExecutorStart(queryDesc, 0);
+ ExecutorRun(queryDesc, ForwardScanDirection, 2L, true);
+ ExecutorFinish(queryDesc);
+ ExecutorEnd(queryDesc);
+
+ FreeQueryDesc(queryDesc);
+
+ PopActiveSnapshot();
+}
+
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cef6632840..30e6c1290b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9656,6 +9656,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
/*
* We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index cc09895fa5..ee8ff7da9e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -29,6 +29,6 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
nodeCtescan.o nodeNamedtuplestorescan.o nodeWorktablescan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o \
- nodeTableFuncscan.o
+ nodeTableFuncscan.o svariableReceiver.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d7..58d4955dd8 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -33,6 +33,7 @@
#include "access/nbtree.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -727,6 +728,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
Param *param = (Param *) node;
ParamListInfo params;
+ AclResult aclresult;
switch (param->paramkind)
{
@@ -736,6 +738,19 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
break;
+ case PARAM_SCHEMA_VARIABLE:
+ /* Check permission to read schema variable */
+ aclresult = pg_variable_aclcheck(param->paramid, GetUserId(), ACL_READ);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE,
+ schema_variable_get_name(param->paramid));
+
+ scratch.opcode = EEOP_PARAM_VARIABLE;
+ scratch.d.param.paramid = param->paramid;
+ scratch.d.param.paramtype = param->paramtype;
+ ExprEvalPushStep(state, &scratch);
+ break;
+
case PARAM_EXTERN:
/*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25aae5..25966ceeeb 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -351,6 +352,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1007,6 +1009,20 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_VARIABLE)
+ {
+ Datum d;
+ bool isnull;
+
+ d = GetSchemaVariable(op->d.param.paramid, &isnull,
+ op->d.param.paramtype);
+
+ *op->resvalue = d;
+ *op->resnull = isnull;
+
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_CASE_TESTVAL)
{
/*
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index b797d064b7..a49deb810c 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,9 +43,11 @@
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_publication.h"
+#include "catalog/pg_variable.h"
#include "commands/matview.h"
#include "commands/trigger.h"
#include "executor/execdebug.h"
+#include "executor/svariableReceiver.h"
#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -204,12 +206,18 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
*/
estate->es_queryEnv = queryDesc->queryEnv;
+ /*
+ * Result can be stored in schema variable.
+ */
+ estate->es_result_variable = queryDesc->plannedstmt->resultVariable;
+
/*
* If non-read-only query, set the command ID to mark output tuples with
*/
switch (queryDesc->operation)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
@@ -345,6 +353,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate->es_lastoid = InvalidOid;
sendTuples = (operation == CMD_SELECT ||
+ OidIsValid(estate->es_result_variable) ||
queryDesc->plannedstmt->hasReturning);
if (sendTuples)
@@ -924,6 +933,17 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_num_root_result_relations = 0;
}
+ if (OidIsValid(estate->es_result_variable))
+ {
+ AclResult aclresult;
+ Oid varid = estate->es_result_variable;
+
+ /* Ensure this variable is writeable */
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, schema_variable_get_name(varid));
+ }
+
/*
* Similarly, we have to lock relations selected FOR [KEY] UPDATE/SHARE
* before we initialize the plan tree, else we'd be risking lock upgrades.
diff --git a/src/backend/executor/svariableReceiver.c b/src/backend/executor/svariableReceiver.c
new file mode 100644
index 0000000000..0eac4b5d0c
--- /dev/null
+++ b/src/backend/executor/svariableReceiver.c
@@ -0,0 +1,145 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.c
+ * An implementation of DestReceiver that stores the result value in
+ * a schema variable.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/svariableReceiver.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/tuptoaster.h"
+#include "executor/svariableReceiver.h"
+#include "commands/schemavariable.h"
+
+typedef struct
+{
+ DestReceiver pub;
+ Oid varid;
+ Oid typid;
+ int32 typmod;
+ int typlen;
+ int slot_offset;
+ int rows;
+} svariableState;
+
+
+/*
+ * Prepare to receive tuples from executor.
+ */
+static void
+svariableStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+ svariableState *myState = (svariableState *) self;
+ int natts = typeinfo->natts;
+ int outcols = 0;
+ int i;
+
+ for (i = 0; i < natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+
+ if (attr->attisdropped)
+ continue;
+
+ if (++outcols > 1)
+ elog(ERROR, "svariable DestReceiver can take only one attribute");
+
+ myState->typid = attr->atttypid;
+ myState->typmod = attr->atttypmod;
+ myState->typlen = attr->attlen;
+ myState->slot_offset = i;
+ }
+
+ myState->rows = 0;
+}
+
+/*
+ * Receive a tuple from the executor and store it in schema variable.
+ */
+static bool
+svariableReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
+{
+ svariableState *myState = (svariableState *) self;
+ Datum value;
+ bool isnull;
+ bool freeval = false;
+
+ /* Make sure the tuple is fully deconstructed */
+ slot_getallattrs(slot);
+
+ value = slot->tts_values[myState->slot_offset];
+ isnull = slot->tts_isnull[myState->slot_offset];
+
+ if (myState->typlen == -1 && !isnull && VARATT_IS_EXTERNAL(DatumGetPointer(value)))
+ {
+ value = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+ DatumGetPointer(value)));
+ freeval = true;
+ }
+
+ SetSchemaVariable(myState->varid, value, isnull, myState->typid, myState->typmod);
+
+ if (freeval)
+ pfree(DatumGetPointer(value));
+
+ return true;
+}
+
+/*
+ * Clean up at end of an executor run
+ */
+static void
+svariableShutdownReceiver(DestReceiver *self)
+{
+ /* Do nothing */
+}
+
+/*
+ * Destroy receiver when done with it
+ */
+static void
+svariableDestroyReceiver(DestReceiver *self)
+{
+ pfree(self);
+}
+
+/*
+ * Initially create a DestReceiver object.
+ */
+DestReceiver *
+CreateVariableDestReceiver(void)
+{
+ svariableState *self = (svariableState *) palloc0(sizeof(svariableState));
+
+ self->pub.receiveSlot = svariableReceiveSlot;
+ self->pub.rStartup = svariableStartupReceiver;
+ self->pub.rShutdown = svariableShutdownReceiver;
+ self->pub.rDestroy = svariableDestroyReceiver;
+ self->pub.mydest = DestVariable;
+
+ /* private fields will be set by SetVariableDestReceiverParams */
+
+ return (DestReceiver *) self;
+}
+
+/*
+ * Set parameters for a VariableDestReceiver
+ */
+void
+SetVariableDestReceiverParams(DestReceiver *self, Oid varid)
+{
+ svariableState *myState = (svariableState *) self;
+
+ Assert(myState->pub.mydest == DestVariable);
+ Assert(OidIsValid(varid));
+
+ myState->varid = varid;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c8220cf65..fcaa2db51a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(nonleafResultRelations);
COPY_NODE_FIELD(rootResultRelations);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks);
@@ -3000,6 +3001,7 @@ _copyQuery(const Query *from)
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_SCALAR_FIELD(hasAggs);
COPY_SCALAR_FIELD(hasWindowFuncs);
COPY_SCALAR_FIELD(hasTargetSRFs);
@@ -3118,6 +3120,18 @@ _copySelectStmt(const SelectStmt *from)
return newnode;
}
+static LetStmt *
+_copyLetStmt(const LetStmt *from)
+{
+ LetStmt *newnode = makeNode(LetStmt);
+
+ COPY_NODE_FIELD(target);
+ COPY_NODE_FIELD(selectStmt);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
static SetOperationStmt *
_copySetOperationStmt(const SetOperationStmt *from)
{
@@ -5166,6 +5180,9 @@ copyObjectImpl(const void *from)
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
+ case T_LetStmt:
+ retval = _copyLetStmt(from);
+ break;
case T_SetOperationStmt:
retval = _copySetOperationStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 378f2facb8..3ec472e19b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -949,6 +949,7 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_SCALAR_FIELD(canSetTag);
COMPARE_NODE_FIELD(utilityStmt);
COMPARE_SCALAR_FIELD(resultRelation);
+ COMPARE_SCALAR_FIELD(resultVariable);
COMPARE_SCALAR_FIELD(hasAggs);
COMPARE_SCALAR_FIELD(hasWindowFuncs);
COMPARE_SCALAR_FIELD(hasTargetSRFs);
@@ -1057,6 +1058,16 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
return true;
}
+static bool
+_equalLetStmt(const LetStmt *a, const LetStmt *b)
+{
+ COMPARE_NODE_FIELD(target);
+ COMPARE_NODE_FIELD(selectStmt);
+
+ return true;
+}
+
+
static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
@@ -3225,6 +3236,9 @@ equal(const void *a, const void *b)
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
break;
+ case T_LetStmt:
+ retval = _equalLetStmt(a, b);
+ break;
case T_SetOperationStmt:
retval = _equalSetOperationStmt(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6269f474d2..46404ff9ac 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -278,6 +278,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(nonleafResultRelations);
WRITE_NODE_FIELD(rootResultRelations);
+ WRITE_OID_FIELD(resultVariable);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks);
@@ -2793,6 +2794,16 @@ _outSelectStmt(StringInfo str, const SelectStmt *node)
WRITE_NODE_FIELD(rarg);
}
+static void
+_outLetStmt(StringInfo str, const LetStmt *node)
+{
+ WRITE_NODE_TYPE("LET");
+
+ WRITE_NODE_FIELD(target);
+ WRITE_NODE_FIELD(selectStmt);
+ WRITE_LOCATION_FIELD(location);
+}
+
static void
_outFuncCall(StringInfo str, const FuncCall *node)
{
@@ -2971,6 +2982,7 @@ _outQuery(StringInfo str, const Query *node)
appendStringInfoString(str, " :utilityStmt <>");
WRITE_INT_FIELD(resultRelation);
+ WRITE_INT_FIELD(resultVariable);
WRITE_BOOL_FIELD(hasAggs);
WRITE_BOOL_FIELD(hasWindowFuncs);
WRITE_BOOL_FIELD(hasTargetSRFs);
@@ -4191,6 +4203,9 @@ outNode(StringInfo str, const void *obj)
case T_SelectStmt:
_outSelectStmt(str, obj);
break;
+ case T_LetStmt:
+ _outLetStmt(str, obj);
+ break;
case T_ColumnDef:
_outColumnDef(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3254524223..4454327549 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -242,6 +242,7 @@ _readQuery(void)
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
+ READ_INT_FIELD(resultVariable);
READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasWindowFuncs);
READ_BOOL_FIELD(hasTargetSRFs);
@@ -1485,6 +1486,7 @@ _readPlannedStmt(void)
READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(nonleafResultRelations);
READ_NODE_FIELD(rootResultRelations);
+ READ_OID_FIELD(resultVariable);
READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index fd06da98b9..01f97f2d86 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -335,7 +335,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
*/
if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
IsUnderPostmaster &&
- parse->commandType == CMD_SELECT &&
+ (parse->commandType == CMD_SELECT ||
+ parse->commandType == CMD_PLAN_UTILITY) &&
!parse->hasModifyingCTE &&
max_parallel_workers_per_gather > 0 &&
!IsParallelWorker() &&
@@ -352,6 +353,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->parallelModeOK = false;
}
+
+
/*
* glob->parallelModeNeeded is normally set to false here and changed to
* true during plan creation if a Gather or Gather Merge plan is actually
@@ -521,6 +524,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->resultRelations = glob->resultRelations;
result->nonleafResultRelations = glob->nonleafResultRelations;
result->rootResultRelations = glob->rootResultRelations;
+ result->resultVariable = parse->resultVariable;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks;
@@ -2167,7 +2171,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
* inheritance_planner, add the ModifyTable node.
*/
- if (parse->commandType != CMD_SELECT && !inheritance_update)
+ if (parse->commandType != CMD_SELECT && parse->commandType != CMD_PLAN_UTILITY && !inheritance_update)
{
List *withCheckOptionLists;
List *returningLists;
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 8603feef2b..2923e3fcc7 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -71,6 +71,7 @@ preprocess_targetlist(PlannerInfo *root)
{
Query *parse = root->parse;
int result_relation = parse->resultRelation;
+ int result_variable = parse->resultVariable;
List *range_table = parse->rtable;
CmdType command_type = parse->commandType;
RangeTblEntry *target_rte = NULL;
@@ -96,6 +97,10 @@ preprocess_targetlist(PlannerInfo *root)
target_relation = heap_open(target_rte->relid, NoLock);
}
+ else if (result_variable)
+ {
+ Assert(command_type == CMD_PLAN_UTILITY);
+ }
else
Assert(command_type == CMD_SELECT);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a04ad6e99e..da570bb23b 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1254,7 +1254,8 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
{
Param *param = (Param *) node;
- if (param->paramkind == PARAM_EXTERN)
+ if (param->paramkind == PARAM_EXTERN ||
+ param->paramkind == PARAM_SCHEMA_VARIABLE)
return false;
if (param->paramkind != PARAM_EXEC ||
@@ -4799,7 +4800,7 @@ substitute_actual_parameters_mutator(Node *node,
{
if (node == NULL)
return NULL;
- if (IsA(node, Param))
+ if (IsA(node, Param) && ((Param *) node)->paramkind != PARAM_SCHEMA_VARIABLE)
{
Param *param = (Param *) node;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8369e3ad62..fc0cf34c7d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1272,7 +1272,7 @@ get_relation_constraints(PlannerInfo *root,
* descriptor, instead of constraint exclusion which is driven by the
* individual partition's partition constraint.
*/
- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
+ if (enable_partition_pruning && root->parse->commandType != CMD_SELECT && root->parse->commandType != CMD_PLAN_UTILITY)
{
List *pcqual = RelationGetPartitionQual(relation);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c601b6d40d..441b298693 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,7 +25,10 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/namespace.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -44,6 +47,8 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -78,6 +83,8 @@ static Query *transformCreateTableAsStmt(ParseState *pstate,
CreateTableAsStmt *stmt);
static Query *transformCallStmt(ParseState *pstate,
CallStmt *stmt);
+static Query *transformLetStmt(ParseState *pstate,
+ LetStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown);
#ifdef RAW_EXPRESSION_COVERAGE_TEST
@@ -267,6 +274,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
+ case T_LetStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
@@ -327,6 +335,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
(CallStmt *) parseTree);
break;
+ case T_LetStmt:
+ result = transformLetStmt(pstate,
+ (LetStmt *) parseTree);
+ break;
+
default:
/*
@@ -367,6 +380,7 @@ analyze_requires_snapshot(RawStmt *parseTree)
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
+ case T_LetStmt:
result = true;
break;
@@ -1567,6 +1581,203 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
return qry;
}
+/*
+ * transformLetStmt -
+ * transform an Let Statement
+ */
+static Query *
+transformLetStmt(ParseState *pstate, LetStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ List *exprList = NIL;
+ List *exprListCoer = NIL;
+ List *indirection = NIL;
+ ListCell *lc;
+ Query *selectQuery;
+ int i = 0;
+
+ Oid varid;
+
+ ParseExprKind sv_expr_kind;
+ char *attrname = NULL;
+ bool not_unique;
+ bool is_rowtype;
+ Oid typid;
+ int32 typmod;
+
+ AclResult aclresult;
+ List *names = NULL;
+ int indirection_start;
+
+ sv_expr_kind = pstate->p_expr_kind;
+ pstate->p_expr_kind = EXPR_KIND_LET;
+
+ /* There can't be any outer WITH to worry about */
+ Assert(pstate->p_ctenamespace == NIL);
+
+ /* Exec this command as utility */
+ qry->commandType = CMD_PLAN_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ names = NamesFromList(stmt->target);
+
+ varid = identify_variable(names, &attrname, ¬_unique);
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("target \"%s\" of LET command is ambiguous",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ if (!OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("schema variable \"%s\" doesn't exists",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ qry->resultVariable = varid;
+
+ get_schema_variable_type_typmod(varid, &typid, &typmod);
+
+ is_rowtype = type_is_rowtype(typid);
+
+ if (attrname && !is_rowtype)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("target variable \"%s\" is not row type",
+ schema_variable_get_name(varid)),
+ parser_errposition(pstate, stmt->location)));
+
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, NameListToString(names));
+
+ selectQuery = transformStmt(pstate, stmt->selectStmt);
+
+ /* The grammar should have produced a SELECT */
+ if (!IsA(selectQuery, Query) ||
+ selectQuery->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in LET ... SELECT");
+
+ /*----------
+ * Generate an expression list for the LET that selects all the
+ * non-resjunk columns from the subquery.
+ *----------
+ */
+ exprList = NIL;
+ foreach(lc, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->resjunk)
+ continue;
+
+ exprList = lappend(exprList, tle->expr);
+ }
+
+ /*
+ * Because doesn't support pattern matching, don't allow multicolumn result
+ */
+ if (list_length(exprList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expression is not scalar value"),
+ parser_errposition(pstate,
+ exprLocation((Node *) exprList))));
+
+ indirection_start = list_length(names) - (attrname ? 1 : 0);
+ indirection = list_copy_tail(stmt->target, indirection_start);
+
+ exprListCoer = NIL;
+ foreach(lc, exprList)
+ {
+ Node *orig_expr = (Node*) lfirst(lc);
+ Oid exprtypid = exprType((Node *) orig_expr);
+ Param *param = makeNode(Param);
+ Expr *expr = NULL;
+
+ param->paramkind = PARAM_SCHEMA_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (indirection != NULL)
+ {
+ bool targetIsArray;
+ char *targetName;
+
+ targetName = attrname != NULL ? attrname : get_schema_variable_name(varid);
+ targetIsArray = OidIsValid(get_element_type(typid));
+
+ expr = (Expr *)
+ transformAssignmentIndirection(pstate,
+ (Node *) param,
+ targetName,
+ targetIsArray,
+ typid,
+ typmod,
+ InvalidOid,
+ list_head(indirection),
+ (Node *) orig_expr,
+ stmt->location);
+ }
+ else
+ expr = (Expr *)
+ coerce_to_target_type(pstate,
+ (Node *) orig_expr,
+ exprtypid,
+ typid, typmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ stmt->location);
+
+ if (expr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable \"%s\" is of type %s,"
+ " but expression is of type %s",
+ schema_variable_get_name(varid),
+ format_type_be(typid),
+ format_type_be(exprtypid)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *) orig_expr))));
+
+ exprListCoer = lappend(exprListCoer, expr);
+ }
+
+ /*
+ * Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
+ */
+ qry->targetList = NIL;
+ foreach(lc, exprListCoer)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ tle = makeTargetEntry(expr,
+ i + 1,
+ FigureColname((Node *)expr),
+ false);
+ qry->targetList = lappend(qry->targetList, tle);
+ }
+
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+ qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+
+ assign_query_collations(pstate, qry);
+
+ pstate->p_expr_kind = sv_expr_kind;
+
+ return qry;
+}
+
+
/*
* transformSetOperationStmt -
* transforms a set-operations tree
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 87f5e95827..25036669c1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -257,8 +257,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSchemaVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -268,7 +268,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DropTransformStmt
DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
- ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
+ LetStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -400,6 +400,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
TriggerTransitions TriggerReferencing
publication_name_list
vacuum_relation_list opt_vacuum_relation_list
+ let_target
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -584,6 +585,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> optSchemaVarDefExpr
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -649,7 +651,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+ LEADING LEAKPROOF LEAST LEFT LET LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -687,8 +689,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -878,6 +880,7 @@ stmt :
| CreatePolicyStmt
| CreatePLangStmt
| CreateSchemaStmt
+ | CreateSchemaVarStmt
| CreateSeqStmt
| CreateStmt
| CreateSubscriptionStmt
@@ -917,6 +920,7 @@ stmt :
| ImportForeignSchemaStmt
| IndexStmt
| InsertStmt
+ | LetStmt
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
@@ -1808,7 +1812,12 @@ DiscardStmt:
n->target = DISCARD_SEQUENCES;
$$ = (Node *) n;
}
-
+ | DISCARD VARIABLES
+ {
+ DiscardStmt *n = makeNode(DiscardStmt);
+ n->target = DISCARD_VARIABLES;
+ $$ = (Node *) n;
+ }
;
@@ -4479,6 +4488,42 @@ create_extension_opt_item:
}
;
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE VARIABLE varname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSchemaVarStmt:
+ CREATE OptTemp VARIABLE qualified_name opt_as Typename optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $4->relpersistence = $2;
+ n->variable = $4;
+ n->typeName = $6;
+ n->defexpr = $7;
+ n->if_not_exists = false;
+ $$ = (Node *) n;
+ }
+ | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $7->relpersistence = $2;
+ n->variable = $7;
+ n->typeName = $9;
+ n->defexpr = $10;
+ n->if_not_exists = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+optSchemaVarDefExpr: DEFAULT b_expr { $$ = $2; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+
+
/*****************************************************************************
*
* ALTER EXTENSION name UPDATE [ TO version ]
@@ -6335,6 +6380,7 @@ drop_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name_list */
@@ -6604,6 +6650,7 @@ comment_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -6742,6 +6789,7 @@ security_label_type_any_name:
| TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -7163,6 +7211,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| ALL TABLES IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7203,6 +7259,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
;
@@ -7363,6 +7427,7 @@ defacl_privilege_target:
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
;
@@ -8959,6 +9024,25 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newname = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
opt_column: COLUMN { $$ = COLUMN; }
@@ -9277,6 +9361,25 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newschema = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
/*****************************************************************************
@@ -9512,6 +9615,14 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
@@ -10693,6 +10804,7 @@ ExplainableStmt:
| CreateMatViewStmt
| RefreshMatViewStmt
| ExecuteStmt /* by default all are $$=$1 */
+ | LetStmt
;
explain_option_list:
@@ -10750,6 +10862,7 @@ PreparableStmt:
| InsertStmt
| UpdateStmt
| DeleteStmt /* by default all are $$=$1 */
+ | LetStmt
;
/*****************************************************************************
@@ -11148,6 +11261,44 @@ opt_hold: /* EMPTY */ { $$ = 0; }
| WITHOUT HOLD { $$ = 0; }
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * LET STATEMENTS
+ *
+ *****************************************************************************/
+LetStmt: LET let_target '=' a_expr
+ {
+ LetStmt *n = makeNode(LetStmt);
+ SelectStmt *select = makeNode(SelectStmt);
+ ResTarget *res = makeNode(ResTarget);
+
+ n->target = $2;
+
+ /* Create target list for implicit query */
+ res->name = NULL;
+ res->indirection = NIL;
+ res->val = (Node *) $4;
+ res->location = @4;
+
+ select->targetList = list_make1(res);
+ n->selectStmt = (Node *) select;
+
+ n->location = @2;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+let_target:
+ ColId opt_indirection
+ {
+ $$ = list_make1(makeString($1));
+ if ($2)
+ $$ = list_concat($$,
+ check_indirection($2, yyscanner));
+ }
+
/*****************************************************************************
*
* QUERY:
@@ -15127,6 +15278,7 @@ unreserved_keyword:
| LARGE_P
| LAST_P
| LEAKPROOF
+ | LET
| LEVEL
| LISTEN
| LOAD
@@ -15275,6 +15427,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 61727e1d71..6823612fba 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -349,6 +349,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
Assert(false); /* can't happen */
break;
case EXPR_KIND_OTHER:
+ case EXPR_KIND_LET:
/*
* Accept aggregate/grouping here; caller must throw error if
@@ -465,6 +466,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
if (isAgg)
err = _("aggregate functions are not allowed in DEFAULT expressions");
@@ -879,6 +881,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("window functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -902,6 +905,8 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_CALL_ARGUMENT:
err = _("window functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("window functions are not allowed in LET statement");
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a9b6..bcdda0fb4a 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -37,6 +38,7 @@
#include "utils/date.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
+#include "utils/typcache.h"
#include "utils/xml.h"
@@ -116,6 +118,9 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
+static Node *makeParamSchemaVariable(ParseState *pstate,
+ Oid varid, Oid typid, int32 typmod,
+ char *attrname, int location);
static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
int location);
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
@@ -512,6 +517,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
+ Oid varid = InvalidOid;
+ char *attrname = NULL;
+ bool not_unique;
+
RangeTblEntry *rte;
int levels_up;
enum
@@ -749,6 +758,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break;
}
+ varid = identify_variable(cref->fields, &attrname, ¬_unique);
+
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("schema variable reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+
/*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
@@ -773,6 +791,71 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
parser_errposition(pstate, cref->location)));
}
+ if (OidIsValid(varid))
+ {
+ Oid typid;
+ int32 typmod;
+
+ get_schema_variable_type_typmod(varid, &typid, &typmod);
+
+ if (node != NULL)
+ {
+ /*
+ * some collision can be solved simply here to reduce errors
+ * based on simply existence of some variables. Often error
+ * can be using alias same like variable name. In this case,
+ * when we found column reference, and we found reference to
+ * possible composite variable, but the variable is not composite,
+ * then we can ignore the variable as simply improper, and we
+ * use column reference only.
+ */
+ if (attrname)
+ {
+ if (type_is_rowtype(typid))
+ {
+ TupleDesc tupdesc;
+ bool found = false;
+ int i;
+
+ /* slow part, I hope it will not be to often */
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ if (namestrcmp(&(TupleDescAttr(tupdesc, i)->attname), attrname) == 0 &&
+ !TupleDescAttr(tupdesc, i)->attisdropped)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ FreeTupleDesc(tupdesc);
+
+ /* there are not composite variable with this field */
+ if (!found)
+ varid = InvalidOid;
+ }
+ else
+ /* there are not composite variable with this name */
+ varid = InvalidOid;
+ }
+
+ /* Raise error if varid is still valid. It should be really amigonuous */
+ if (OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ errdetail("The qualified identifier can be column reference or schema variable reference"),
+ parser_errposition(pstate, cref->location)));
+ }
+
+ if (OidIsValid(varid))
+ node = makeParamSchemaVariable(pstate,
+ varid, typid, typmod,
+ attrname, cref->location);
+ }
+
/*
* Throw error if no translation found.
*/
@@ -807,6 +890,59 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return node;
}
+/*
+ * Generate param variable for reference to schema variable
+ */
+static Node *
+makeParamSchemaVariable(ParseState *pstate, Oid varid, Oid typid, int32 typmod, char *attrname, int location)
+{
+ Param *param;
+
+ param = makeNode(Param);
+
+ param->paramkind = PARAM_SCHEMA_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (attrname != NULL)
+ {
+ TupleDesc tupdesc;
+ int i;
+
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+
+ if (strcmp(attrname, NameStr(att->attname)) == 0 &&
+ !att->attisdropped)
+ {
+ /* Success, so generate a FieldSelect expression */
+ FieldSelect *fselect = makeNode(FieldSelect);
+
+ fselect->arg = (Expr *) param;
+ fselect->fieldnum = i + 1;
+ fselect->resulttype = att->atttypid;
+ fselect->resulttypmod = att->atttypmod;
+ /* save attribute's collation for parse_collate.c */
+ fselect->resultcollid = att->attcollation;
+
+ ReleaseTupleDesc(tupdesc);
+ return (Node *) fselect;
+ }
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("could not identify column \"%s\" in variable", attrname),
+ parser_errposition(pstate, location)));
+ }
+
+ return (Node *) param;
+}
+
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
@@ -1818,6 +1954,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
case EXPR_KIND_VALUES_SINGLE:
+ case EXPR_KIND_LET:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
@@ -1826,6 +1963,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -3460,6 +3598,7 @@ ParseExprKindName(ParseExprKind exprKind)
return "CHECK";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
return "DEFAULT";
case EXPR_KIND_INDEX_EXPRESSION:
return "index expression";
@@ -3475,6 +3614,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "PARTITION BY";
case EXPR_KIND_CALL_ARGUMENT:
return "CALL";
+ case EXPR_KIND_LET:
+ return "LET";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 44257154b8..b2c9900e00 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2347,6 +2347,7 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("set-returning functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -2370,6 +2371,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_CALL_ARGUMENT:
err = _("set-returning functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("set-returning functions are not allowed in CALL arguments");
+ break;
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4932e58022..c60fe011f7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -35,16 +35,6 @@
static void markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
Var *var, int levelsup);
-static Node *transformAssignmentIndirection(ParseState *pstate,
- Node *basenode,
- const char *targetName,
- bool targetIsArray,
- Oid targetTypeId,
- int32 targetTypMod,
- Oid targetCollation,
- ListCell *indirection,
- Node *rhs,
- int location);
static Node *transformAssignmentSubscripts(ParseState *pstate,
Node *basenode,
const char *targetName,
@@ -672,7 +662,7 @@ updateTargetListEntry(ParseState *pstate,
* might want to decorate indirection cells with their own location info,
* in which case the location argument could probably be dropped.)
*/
-static Node *
+Node *
transformAssignmentIndirection(ParseState *pstate,
Node *basenode,
const char *targetName,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 3123ee274d..10737d422d 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3350,7 +3350,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
* get executed. Also, utilities aren't rewritten at all (do we still
* need that check?)
*/
- if (event != CMD_SELECT && event != CMD_UTILITY)
+ if (event != CMD_SELECT && event != CMD_UTILITY && event != CMD_PLAN_UTILITY)
{
int result_relation;
RangeTblEntry *rt_entry;
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 61ef396d8a..6a068af799 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -212,7 +212,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
}
/*
- * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
+ * For SELECT, LET, UPDATE and DELETE, add security quals to enforce the USING
* policies. These security quals control access to existing table rows.
* Restrictive policies are combined together using AND, and permissive
* policies are combined together using OR.
@@ -222,6 +222,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
&restrictive_policies);
if (commandType == CMD_SELECT ||
+ commandType == CMD_PLAN_UTILITY ||
commandType == CMD_UPDATE ||
commandType == CMD_DELETE)
add_security_quals(rt_index,
@@ -423,6 +424,7 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
switch (cmd)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
if (policy->polcmd == ACL_SELECT_CHR)
cmd_matches = true;
break;
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index c95a4d519d..47fb0f38b1 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -37,6 +37,7 @@
#include "executor/functions.h"
#include "executor/tqueue.h"
#include "executor/tstoreReceiver.h"
+#include "executor/svariableReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/portal.h"
@@ -143,6 +144,9 @@ CreateDestReceiver(CommandDest dest)
case DestTupleQueue:
return CreateTupleQueueDestReceiver(NULL);
+
+ case DestVariable:
+ return CreateVariableDestReceiver();
}
/* should never get here */
@@ -178,6 +182,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -222,6 +227,7 @@ NullCommand(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -268,6 +274,7 @@ ReadyForQuery(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b5804f64ad..35199fd0dc 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
@@ -344,7 +345,7 @@ ProcessUtility(PlannedStmt *pstmt,
char *completionTag)
{
Assert(IsA(pstmt, PlannedStmt));
- Assert(pstmt->commandType == CMD_UTILITY);
+ Assert(pstmt->commandType == CMD_UTILITY || pstmt->commandType == CMD_PLAN_UTILITY);
Assert(queryString != NULL); /* required as of 8.4 */
/*
@@ -915,6 +916,14 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
}
+ case T_LetStmt:
+ {
+ doLetStmt(pstmt, params, queryEnv, queryString);
+ if (completionTag)
+ strcpy(completionTag, "LET");
+ }
+ break;
+
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
@@ -1221,6 +1230,10 @@ ProcessUtilitySlow(ParseState *pstate,
}
break;
+ case T_CreateSchemaVarStmt:
+ address = DefineSchemaVariable(pstate, (CreateSchemaVarStmt *) parsetree);
+ break;
+
/*
* ************* object creation / destruction **************
*/
@@ -2055,6 +2068,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_STATISTIC_EXT:
tag = "ALTER STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "ALTER VARIABLE";
+ break;
default:
tag = "???";
break;
@@ -2104,6 +2120,10 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ case T_LetStmt:
+ tag = "LET";
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
@@ -2358,6 +2378,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "DROP VARIABLE";
+ break;
default:
tag = "???";
}
@@ -2639,6 +2662,9 @@ CreateCommandTag(Node *parsetree)
case DISCARD_SEQUENCES:
tag = "DISCARD SEQUENCES";
break;
+ case DISCARD_VARIABLES:
+ tag = "DISCARD VARIABLES";
+ break;
default:
tag = "???";
}
@@ -2844,6 +2870,7 @@ CreateCommandTag(Node *parsetree)
tag = "DELETE";
break;
case CMD_UTILITY:
+ case CMD_PLAN_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
default:
@@ -2915,6 +2942,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_CreateSchemaVarStmt:
+ tag = "CREATE VARIABLE";
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -2961,6 +2992,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ case T_LetStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a45e093de7..952c0d9628 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -315,6 +315,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_READ_CHR:
+ read = ACL_READ;
+ break;
+ case ACL_WRITE_CHR:
+ read = ACL_WRITE;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -808,6 +814,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -903,6 +913,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'T':
objtype = OBJECT_TYPE;
break;
+ case 'V':
+ objtype = OBJECT_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
}
@@ -1627,6 +1640,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CONNECT;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
+ if (pg_strcasecmp(priv_type, "READ") == 0)
+ return ACL_READ;
+ if (pg_strcasecmp(priv_type, "WRITE") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1721,6 +1738,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 03e9a28a63..488cb26d3f 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7362,6 +7362,14 @@ get_parameter(Param *param, deparse_context *context)
return;
}
+ /* translate paramid to original schema variable name */
+ if (param->paramkind == PARAM_SCHEMA_VARIABLE)
+ {
+ appendStringInfo(context->buf, "%s",
+ schema_variable_get_name(param->paramid));
+ return;
+ }
+
/*
* Not PARAM_EXEC, or couldn't find referent: just print $N.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bba595ad1d..858a6dd4be 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1691,6 +1691,18 @@ get_relname_relid(const char *relname, Oid relnamespace)
ObjectIdGetDatum(relnamespace));
}
+/*
+ * get_varname_varid
+ * Given name and namespace of variable, look up the OID.
+ */
+Oid
+get_varname_varid(const char *varname, Oid varnamespace)
+{
+ return GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(varnamespace));
+}
+
#ifdef NOT_USED
/*
* get_relnatts
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b381782a3..35dc32f649 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -73,6 +73,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "utils/rel.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
@@ -968,6 +969,28 @@ static const struct cachedesc cacheinfo[] = {
0
},
2
+ },
+ {VariableRelationId, /* VARIABLENAMENSP */
+ VariableNameNspIndexId,
+ 2,
+ {
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ 0,
+ 0
+ },
+ 8
+ },
+ {VariableRelationId, /* VARIABLEOID */
+ VariableObjectIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
}
};
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 0d147cb08d..6d97931d85 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -296,6 +296,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading variables\n");
+ getVariables(fout);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 83c976eaf7..c9bc91ca68 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3471,6 +3471,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(type, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(type, "STATISTICS") == 0 ||
+ strcmp(type, "VARIABLE") == 0 ||
/* non-schema-specified objects */
strcmp(type, "DATABASE") == 0 ||
strcmp(type, "PROCEDURAL LANGUAGE") == 0 ||
@@ -3670,7 +3671,8 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
strcmp(te->desc, "SERVER") == 0 ||
strcmp(te->desc, "STATISTICS") == 0 ||
strcmp(te->desc, "PUBLICATION") == 0 ||
- strcmp(te->desc, "SUBSCRIPTION") == 0)
+ strcmp(te->desc, "SUBSCRIPTION") == 0 ||
+ strcmp(te->desc, "VARIABLE") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 9baf7b2fde..f825a00c9d 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -260,6 +260,7 @@ static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
+static void dumpVariable(Archive *fout, VariableInfo *varinfo);
static void dumpDatabase(Archive *AH);
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
const char *dbname, Oid dboid);
@@ -4221,6 +4222,208 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+/*
+ * getVariables
+ * get information about variables
+ */
+void
+getVariables(Archive *fout)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer init_acl_subquery = createPQExpBuffer();
+ PQExpBuffer init_racl_subquery = createPQExpBuffer();
+ PGresult *res;
+ VariableInfo *varinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_varname;
+ int i_varnamespace;
+ int i_vartype;
+ int i_vartypname;
+ int i_vardefexpr;
+ int i_rolname;
+ int i_varacl;
+ int i_rvaracl;
+ int i_initvaracl;
+ int i_initrvaracl;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion <= 110000)
+ return;
+
+ acl_subquery = createPQExpBuffer();
+ racl_subquery = createPQExpBuffer();
+ init_acl_subquery = createPQExpBuffer();
+ init_racl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
+ init_racl_subquery, "v.varacl", "v.varowner", "'V'",
+ dopt->binary_upgrade);
+
+ query = createPQExpBuffer();
+
+ resetPQExpBuffer(query);
+
+ /* Get the variables in current database. */
+ appendPQExpBuffer(query,
+ "SELECT v.tableoid, v.oid, v.varname, "
+ "v.varnamespace,"
+ "(%s varowner) AS rolname, "
+ "%s as varacl, "
+ "%s as rvaracl, "
+ "%s as initvaracl, "
+ "%s as initrvaracl, "
+ "v.vartype, "
+ "pg_catalog.format_type(v.vartype, v.vartypmod) as vartypname, "
+ "pg_catalog.pg_get_expr(v.vardefexpr,0) as vardefexpr "
+ "FROM pg_variable v "
+ "LEFT JOIN pg_init_privs pip "
+ "ON (v.oid = pip.objoid "
+ "AND pip.classoid = 'pg_variable'::regclass "
+ "AND pip.objsubid = 0)",
+ username_subquery,
+ acl_subquery->data,
+ racl_subquery->data,
+ init_acl_subquery->data,
+ init_racl_subquery->data);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(init_acl_subquery);
+ destroyPQExpBuffer(init_racl_subquery);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_varname = PQfnumber(res, "varname");
+ i_varnamespace = PQfnumber(res, "varnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_vartype = PQfnumber(res, "vartype");
+ i_vartypname = PQfnumber(res, "vartypname");
+ i_vardefexpr = PQfnumber(res, "vardefexpr");
+ i_varacl = PQfnumber(res, "varacl");
+ i_rvaracl = PQfnumber(res, "rvaracl");
+ i_initvaracl = PQfnumber(res, "initvaracl");
+ i_initrvaracl = PQfnumber(res, "initrvaracl");
+
+ varinfo = pg_malloc(ntups * sizeof(VariableInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ TypeInfo *vtype;
+
+ varinfo[i].dobj.objType = DO_VARIABLE;
+ varinfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ varinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&varinfo[i].dobj);
+ varinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_varname));
+ varinfo[i].dobj.namespace =
+ findNamespace(fout,
+ atooid(PQgetvalue(res, i, i_varnamespace)));
+
+ varinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ varinfo[i].vartype = atooid(PQgetvalue(res, i, i_vartype));
+ varinfo[i].vartypname = pg_strdup(PQgetvalue(res, i, i_vartypname));
+
+ varinfo[i].varacl = pg_strdup(PQgetvalue(res, i, i_varacl));
+ varinfo[i].rvaracl = pg_strdup(PQgetvalue(res, i, i_rvaracl));
+ varinfo[i].initvaracl = pg_strdup(PQgetvalue(res, i, i_initvaracl));
+ varinfo[i].initrvaracl = pg_strdup(PQgetvalue(res, i, i_initrvaracl));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ /* Do not try to dump ACL if no ACL exists. */
+ if (PQgetisnull(res, i, i_varacl) && PQgetisnull(res, i, i_rvaracl) &&
+ PQgetisnull(res, i, i_initvaracl) &&
+ PQgetisnull(res, i, i_initrvaracl))
+ varinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+
+ if (PQgetisnull(res, i, i_vardefexpr))
+ varinfo[i].vardefexpr = NULL;
+ else
+ varinfo[i].vardefexpr = pg_strdup(PQgetvalue(res, i, i_vardefexpr));
+
+ if (strlen(varinfo[i].rolname) == 0)
+ write_msg(NULL, "WARNING: owner of variable \"%s\" appears to be invalid\n",
+ varinfo[i].dobj.name);
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ vtype = findTypeByOid(varinfo[i].vartype);
+ addObjectDependency(&varinfo[i].dobj, vtype->dobj.dumpId);
+ }
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpVariable
+ * dump the definition of the given variables
+ */
+static void
+dumpVariable(Archive *fout, VariableInfo *varinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ const char *varname;
+ const char *vartypname;
+ const char *vardefexpr;
+
+ /* Skip if not to be dumped */
+ if (!varinfo->dobj.dump || dopt->dataOnly)
+ return;
+
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ varname = fmtQualifiedDumpable(varinfo);
+ vartypname = varinfo->vartypname;
+ vardefexpr = varinfo->vardefexpr;
+
+ appendPQExpBuffer(delq, "DROP VARIABLE %s;\n",
+ varname);
+
+ appendPQExpBuffer(query, "CREATE VARIABLE %s AS %s",
+ varname, vartypname);
+
+ if (vardefexpr)
+ appendPQExpBuffer(query, " DEFAULT %s",
+ vardefexpr);
+
+ appendPQExpBuffer(query, ";\n");
+
+ ArchiveEntry(fout, varinfo->dobj.catId, varinfo->dobj.dumpId,
+ varinfo->dobj.name,
+ NULL,
+ NULL,
+ varinfo->rolname, false,
+ "VARIABLE", SECTION_PRE_DATA,
+ query->data, delq->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ if (varinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, "VARIABLE", varname,
+ NULL, varinfo->rolname,
+ varinfo->dobj.catId, 0, varinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -9849,6 +10052,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_VARIABLE:
+ dumpVariable(fout, (VariableInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -17935,6 +18141,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_OPFAMILY:
case DO_COLLATION:
case DO_CONVERSION:
+ case DO_VARIABLE:
case DO_TABLE:
case DO_ATTRDEF:
case DO_PROCLANG:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 1448005f30..0d49bb7ed7 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -84,7 +84,8 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_VARIABLE
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -625,6 +626,22 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/*
+ * The VariableInfo struct is used to represent schema variables
+ */
+typedef struct _VariableInfo
+{
+ DumpableObject dobj;
+ Oid vartype;
+ char *vartypname;
+ char *rolname; /* name of owner, or empty string */
+ char *vardefexpr;
+ char *varacl;
+ char *rvaracl;
+ char *initvaracl;
+ char *initrvaracl;
+} VariableInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -725,5 +742,6 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern void getVariables(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index ec751a7c23..2a67766ed4 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2601,6 +2601,38 @@ my %tests = (
},
},
+ 'CREATE VARIABLE test_variable' => {
+ all_runs => 1,
+ catch_all => 'CREATE ... commands',
+ create_order => 61,
+ create_sql => 'CREATE VARIABLE dump_test.variable AS integer DEFAULT 0;',
+ regexp => qr/^
+ \QCREATE VARIABLE dump_test.variable AS integer DEFAULT 0;\E/xm,
+ like => {
+ binary_upgrade => 1,
+ clean => 1,
+ clean_if_exists => 1,
+ createdb => 1,
+ defaults => 1,
+ exclude_test_table => 1,
+ exclude_test_table_data => 1,
+ no_blobs => 1,
+ no_privs => 1,
+ no_owner => 1,
+ only_dump_test_schema => 1,
+ pg_dumpall_dbprivs => 1,
+ schema_only => 1,
+ section_pre_data => 1,
+ test_schema_plus_blobs => 1,
+ with_oids => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_test_table => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1,
+ role => 1,
+ section_post_data => 1, }, },
+
'CREATE VIEW test_view' => {
create_order => 61,
create_sql => 'CREATE VIEW dump_test.test_view
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..73a752fd7e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -853,6 +853,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
break;
}
break;
+ case 'V': /* Variables */
+ success = listVariables(pattern, show_verbose);
+ break;
case 'x': /* Extensions */
if (show_verbose)
success = listExtensionContents(pattern);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 80d8338b96..d645bba7af 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4178,6 +4178,80 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
return true;
}
+/*
+ * \dV
+ *
+ * listVariables()
+ */
+bool
+listVariables(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false, false, false, false, false};
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname as \"%s\",\n"
+ " v.varname as \"%s\",\n"
+ " pg_catalog.format_type(v.vartype, v.vartypmod) as \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(v.varowner) as \"%s\",\n"
+ " pg_catalog.pg_get_expr(v.vardefexpr, 0) as \"%s\"",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Type"),
+ gettext_noop("Owner"),
+ gettext_noop("Default"));
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_variable v"
+ "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = v.varnamespace");
+
+ appendPQExpBufferStr(&buf, "\nWHERE true\n");
+ if (!pattern)
+ appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
+ " AND n.nspname <> 'information_schema'\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "v.varname", NULL,
+ "pg_catalog.pg_variable_is_visible(v.oid)");
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ /*
+ * Most functions in this file are content to print an empty table when
+ * there are no matching objects. We intentionally deviate from that
+ * here, but only in !quiet mode, for historical reasons.
+ */
+ if (PQntuples(res) == 0 && !pset.quiet)
+ {
+ if (pattern)
+ psql_error("Did not find any schema variable named \"%s\".\n",
+ pattern);
+ else
+ psql_error("Did not find any schema variables.\n");
+ }
+ else
+ {
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of variables");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+ }
+
+ PQclear(res);
+ return true;
+}
/*
* \dFp
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..ecc4e3a531 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
/* \dt, \di, \ds, \dS, etc. */
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
+/* \dV */
+extern bool listVariables(const char *pattern, bool varbose);
+
/* \dD */
extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 316030d358..adcc36cb6e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
@@ -257,6 +257,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[S+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dV [PATTERN] list variables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index bb696f8ee9..a7583810e8 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -805,6 +805,22 @@ static const SchemaQuery Query_for_list_of_statistics = {
NULL
};
+static const SchemaQuery Query_for_list_of_variables = {
+ /* min_server_version */
+ 0,
+ /* catname */
+ "pg_catalog.pg_variable v",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_variable_is_visible(v.oid)",
+ /* namespace */
+ "v.varnamespace",
+ /* result */
+ "pg_catalog.quote_ident(v.varname)",
+ /* qualresult */
+ NULL
+};
/*
* Queries to get lists of names of various kinds of things, possibly
@@ -1249,6 +1265,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
{"USER MAPPING FOR", NULL, NULL, NULL},
+ {"VARIABLE", NULL, NULL, &Query_for_list_of_variables},
{"VIEW", NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
@@ -1604,7 +1621,7 @@ psql_completion(const char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
- "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LET", "LISTEN", "LOAD", "LOCK",
"MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
"RESET", "REVOKE", "ROLLBACK",
@@ -1621,9 +1638,9 @@ psql_completion(const char *text, int start, int end)
"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
- "\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+ "\\dm", "\\dn", "\\do", "\\dO", "\\dp"
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+ "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", "\\dV",
"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
"\\endif", "\\errverbose", "\\ev",
"\\f",
@@ -1988,6 +2005,9 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
else if (Matches4("ALTER", "SYSTEM", "SET", MatchAny))
COMPLETE_WITH_CONST("TO");
+ /* ALTER VARIABLE <name> */
+ else if (Matches3("ALTER", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
/* ALTER VIEW <name> */
else if (Matches3("ALTER", "VIEW", MatchAny))
COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
@@ -2837,6 +2857,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
COMPLETE_WITH_LIST2("GROUP", "ROLE");
+/* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
+ /* Complete CREATE VARIABLE <name> with AS */
+ else if (TailMatches3("CREATE", "VARIABLE", MatchAny))
+ COMPLETE_WITH_CONST("AS");
+ /* Complete CREATE VARIABLE <name> with AS types*/
+ else if (TailMatches4("CREATE", "VARIABLE", MatchAny, "AS"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE VIEW <name> with AS */
else if (TailMatches3("CREATE", "VIEW", MatchAny))
@@ -2890,7 +2918,7 @@ psql_completion(const char *text, int start, int end)
/* DISCARD */
else if (Matches1("DISCARD"))
- COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP");
+ COMPLETE_WITH_LIST5("ALL", "PLANS", "SEQUENCES", "TEMP", "VARIABLES");
/* DO */
else if (Matches1("DO"))
@@ -2992,6 +3020,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP VARIABLE */
+ else if (Matches2("DROP", "VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ else if (Matches3("DROP", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3002,14 +3036,14 @@ psql_completion(const char *text, int start, int end)
* Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "ANALYZE", "VERBOSE");
+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "ANALYZE", "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "ANALYZE"))
- COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "VERBOSE");
+ COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "VERBOSE") ||
Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
- COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
+ COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "LET");
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
@@ -3118,6 +3152,7 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'ALL ROUTINES IN SCHEMA'"
" UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
" UNION SELECT 'ALL TABLES IN SCHEMA'"
+ " UNION SELECT 'ALL VARIABLES IN SCHEMA'"
" UNION SELECT 'DATABASE'"
" UNION SELECT 'DOMAIN'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
@@ -3131,14 +3166,16 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'SEQUENCE'"
" UNION SELECT 'TABLE'"
" UNION SELECT 'TABLESPACE'"
- " UNION SELECT 'TYPE'");
+ " UNION SELECT 'TYPE'"
+ " UNION SELECT 'VARIABLE'");
}
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
- COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA",
+ COMPLETE_WITH_LIST6("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
- "TABLES IN SCHEMA");
+ "TABLES IN SCHEMA",
+ "VARIABLES IN SCHEMA");
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
@@ -3172,6 +3209,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches1("TYPE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+ else if (TailMatches1("VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
COMPLETE_WITH_CONST("TO");
else
@@ -3324,7 +3363,7 @@ psql_completion(const char *text, int start, int end)
/* PREPARE xx AS */
else if (Matches3("PREPARE", MatchAny, "AS"))
- COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
+ COMPLETE_WITH_LIST5("SELECT", "UPDATE", "INSERT", "DELETE FROM", "LET");
/*
* PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -3547,6 +3586,14 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
COMPLETE_WITH_CONST("=");
+/* LET --- can be inside EXPLAIN, PREPARE etc */
+ /* If prev. word is LET suggest a list of variables */
+ else if (TailMatches1("LET"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ /* Complete LET <variable> with "=" */
+ else if (TailMatches2("LET", MatchAny))
+ COMPLETE_WITH_CONST("=");
+
/* USER MAPPING */
else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 46c271a46c..3e38a05e55 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -180,7 +180,8 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_VARIABLE /* pg_variable */
} ObjectClass;
#define LAST_OCLASS OCLASS_TRANSFORM
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 24915824ca..dae80c20a8 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -360,4 +360,10 @@ DECLARE_UNIQUE_INDEX(pg_subscription_subname_index, 6115, on pg_subscription usi
DECLARE_UNIQUE_INDEX(pg_subscription_rel_srrelid_srsubid_index, 6117, on pg_subscription_rel using btree(srrelid oid_ops, srsubid oid_ops));
#define SubscriptionRelSrrelidSrsubidIndexId 6117
+DECLARE_UNIQUE_INDEX(pg_variable_oid_index, 4288, on pg_variable using btree(oid oid_ops));
+#define VariableObjectIndexId 4288
+
+DECLARE_UNIQUE_INDEX(pg_variable_varname_nsp_index, 4289, on pg_variable using btree(varname name_ops, varnamespace oid_ops));
+#define VariableNameNspIndexId 4289
+
#endif /* INDEXING_H */
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 7991de5e21..75068d7e92 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -75,10 +75,13 @@ extern Oid RangeVarGetAndCheckCreationNamespace(RangeVar *newRelation,
extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
+extern bool VariableIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
+extern bool VariableIsVisible(Oid varid);
+
extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
bool expand_variadic,
@@ -145,6 +148,10 @@ extern void SetTempNamespaceState(Oid tempNamespaceId,
Oid tempToastNamespaceId);
extern void ResetTempTableNamespace(void);
+extern List *NamesFromList(List *names);
+extern Oid lookup_variable(const char *nspname, const char *varname, bool missing_ok);
+extern Oid identify_variable(List *names, char **attrname, bool *not_uniq);
+
extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
extern OverrideSearchPath *CopyOverrideSearchPath(OverrideSearchPath *path);
extern bool OverrideSearchPathMatchesCurrent(OverrideSearchPath *path);
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index d0410f5586..56deef1a45 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -57,6 +57,7 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a14651010f..61cbe65805 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5961,6 +5961,9 @@
proname => 'pg_collation_is_visible', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid',
prosrc => 'pg_collation_is_visible' },
+{ oid => '4187', descr => 'is schema variable visible in search path?',
+ proname => 'pg_variable_is_visible', procost => '10', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid', prosrc => 'pg_variable_is_visible' },
{ oid => '2854', descr => 'get OID of current session\'s temp schema, if any',
proname => 'pg_my_temp_schema', provolatile => 's', proparallel => 'r',
diff --git a/src/include/catalog/pg_variable.h b/src/include/catalog/pg_variable.h
new file mode 100644
index 0000000000..34f4c34202
--- /dev/null
+++ b/src/include/catalog/pg_variable.h
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.h
+ * definition of schema variables system catalog (pg_variables)
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_variable.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VARIABLE_H
+#define PG_VARIABLE_H
+
+#include "catalog/genbki.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable_d.h"
+#include "utils/acl.h"
+
+/* ----------------
+ * pg_variable definition. cpp turns this into
+ * typedef struct FormData_pg_variable
+ * ----------------
+ */
+CATALOG(pg_variable,4287,VariableRelationId)
+{
+ NameData varname; /* variable name */
+ Oid varnamespace; /* OID of namespace containing variable class */
+ Oid vartype; /* OID of entry in pg_type for variable's type */
+ int32 vartypmod; /* typmode for variable's type */
+ Oid varowner; /* class owner */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+
+ /* list of expression trees for variable default (NULL if none) */
+ pg_node_tree vardefexpr BKI_DEFAULT(_null_);
+
+ aclitem varacl[1] BKI_DEFAULT(_null_); /* access permissions */
+
+#endif
+} FormData_pg_variable;
+
+/* ----------------
+ * Form_pg_variable corresponds to a pointer to a tuple with
+ * the format of pg_variable relation.
+ * ----------------
+ */
+typedef FormData_pg_variable *Form_pg_variable;
+
+typedef struct Variable
+{
+ Oid oid;
+ char *name;
+ Oid namespace;
+ Oid typid;
+ int32 typmod;
+ Oid owner;
+ Node *defexpr;
+ Acl *acl;
+} Variable;
+
+/* returns fields from pg_variable table */
+extern char *get_schema_variable_name(Oid varid);
+extern void get_schema_variable_type_typmod(Oid varid, Oid *typid, int32 *typmod);
+
+/* returns name of variable based on current search path */
+extern char *schema_variable_get_name(Oid varid);
+
+extern Variable *GetVariable(Oid varid, bool missing_ok);
+extern ObjectAddress VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Node *varDefexpr,
+ bool if_not_exists);
+
+
+#endif /* PG_VARIABLE_H */
diff --git a/src/include/commands/schemavariable.h b/src/include/commands/schemavariable.h
new file mode 100644
index 0000000000..dd3239b236
--- /dev/null
+++ b/src/include/commands/schemavariable.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavariable.h
+ * prototypes for schemavariable.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/schemavariable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SCHEMAVARIABLE_H
+#define SCHEMAVARIABLE_H
+
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable.h"
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "utils/queryenvironment.h"
+
+extern char *VariableGetName(Variable *var);
+
+extern void ResetSchemaVariableCache(void);
+
+extern void RemoveVariableById(Oid varid);
+extern ObjectAddress DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt);
+
+extern Datum GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid);
+extern void SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod);
+
+extern void doLetStmt(PlannedStmt *pstmt, ParamListInfo params, QueryEnvironment *queryEnv, const char *queryString);
+
+#endif
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77616..cca30f275b 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -344,11 +345,11 @@ typedef struct ExprEvalStep
TupleDesc argdesc;
} nulltest_row;
- /* for EEOP_PARAM_EXEC/EXTERN */
+ /* for EEOP_PARAM_EXEC/EXTERN/VARIABLE */
struct
{
- int paramid; /* numeric ID for parameter */
- Oid paramtype; /* OID of parameter's datatype */
+ int paramid; /* numeric ID for parameter */
+ Oid paramtype; /* OID of parameter's datatype */
} param;
/* for EEOP_PARAM_CALLBACK */
diff --git a/src/include/executor/svariableReceiver.h b/src/include/executor/svariableReceiver.h
new file mode 100644
index 0000000000..8c8117701f
--- /dev/null
+++ b/src/include/executor/svariableReceiver.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.h
+ * prototypes for svariableReceiver.c
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/svariableReceiver.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SVARIABLE_RECEIVER_H
+#define SVARIABLE_RECEIVER_H
+
+#include "tcop/dest.h"
+
+
+extern DestReceiver *CreateVariableDestReceiver(void);
+
+extern void SetVariableDestReceiverParams(DestReceiver *self, Oid varid);
+
+#endif /* SVARIABLE_RECEIVER_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 018f50bbb7..08b4b2c2f2 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -564,6 +564,8 @@ typedef struct EState
/* The per-query shared memory area to use for parallel execution. */
struct dsa_area *es_query_dsa;
+ int es_result_variable; /* Oid of target variable */
+
/*
* JIT information. es_jit_flags indicates whether JIT should be performed
* and with which options. es_jit is created on-demand when JITing is
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 697d3d7a5f..dd7fd8ed42 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -348,6 +348,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSchemaVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
@@ -419,6 +420,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
+ T_LetStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -663,6 +665,7 @@ typedef enum CmdType
CMD_DELETE,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
+ CMD_PLAN_UTILITY, /* only let stmt now, requires planning */
CMD_NOTHING /* dummy command for instead nothing rules
* with qual */
} CmdType;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07ab1a3dde..2d4a3cb1b6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -84,7 +84,9 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
-#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_READ (1<<12) /* for variables */
+#define ACL_WRITE (1<<13) /* for variables */
+#define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -121,6 +123,7 @@ typedef struct Query
int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */
+ int resultVariable; /* Oid of target variable or 0 */
bool hasAggs; /* has aggregates in tlist or havingQual */
bool hasWindowFuncs; /* has window functions in tlist */
@@ -1505,6 +1508,18 @@ typedef struct UpdateStmt
WithClause *withClause; /* WITH clause */
} UpdateStmt;
+/* ----------------------
+ * Let Statement
+ * ----------------------
+ */
+typedef struct LetStmt
+{
+ NodeTag type;
+ List *target; /* target variable */
+ Node *selectStmt; /* source expression */
+ int location;
+} LetStmt;
+
/* ----------------------
* Select Statement
*
@@ -1682,6 +1697,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -2497,6 +2513,19 @@ typedef struct AlterSeqStmt
bool missing_ok; /* skip error if a role is missing? */
} AlterSeqStmt;
+/* ----------------------
+ * {Create|Alter} VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSchemaVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the type of variable */
+ Node *defexpr; /* default expression */
+ bool if_not_exists; /* do nothing if it already exists */
+} CreateSchemaVarStmt;
+
/* ----------------------
* Create {Aggregate|Operator|Type} Statement
* ----------------------
@@ -3238,7 +3267,8 @@ typedef enum DiscardMode
DISCARD_ALL,
DISCARD_PLANS,
DISCARD_SEQUENCES,
- DISCARD_TEMP
+ DISCARD_TEMP,
+ DISCARD_VARIABLES
} DiscardMode;
typedef struct DiscardStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd03a..2588f1455f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -43,7 +43,7 @@ typedef struct PlannedStmt
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete|utility */
+ CmdType commandType; /* select|let|insert|update|delete|utility */
uint64 queryId; /* query identifier (copied from Query) */
@@ -81,6 +81,9 @@ typedef struct PlannedStmt
*/
List *rootResultRelations;
+ /* Oid of target variable for LET command */
+ Oid resultVariable;
+
List *subplans; /* Plan trees for SubPlan expressions; note
* that some could be NULL */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..b366471940 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -229,13 +229,17 @@ typedef struct Const
* of the `paramid' field contain the SubLink's subLinkId, and
* the low-order 16 bits contain the column number. (This type
* of Param is also converted to PARAM_EXEC during planning.)
+ *
+ * PARAM_SCHEMA_VARIABLE: The parameter is a access to schema variable
+ * paramid holds varid.
*/
typedef enum ParamKind
{
PARAM_EXTERN,
PARAM_EXEC,
PARAM_SUBLINK,
- PARAM_MULTIEXPR
+ PARAM_MULTIEXPR,
+ PARAM_SCHEMA_VARIABLE
} ParamKind;
typedef struct Param
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db40147b..d3ed3f4d0f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -231,6 +231,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("let", LET, UNRESERVED_KEYWORD)
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -434,6 +435,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 0230543810..f7c2e67f33 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -69,7 +69,9 @@ typedef enum ParseExprKind
EXPR_KIND_TRIGGER_WHEN, /* WHEN condition in CREATE TRIGGER */
EXPR_KIND_POLICY, /* USING or WITH CHECK expr in policy */
EXPR_KIND_PARTITION_EXPRESSION, /* PARTITION BY expression */
- EXPR_KIND_CALL_ARGUMENT /* procedure argument in CALL */
+ EXPR_KIND_CALL_ARGUMENT, /* procedure argument in CALL */
+ EXPR_KIND_VARIABLE_DEFAULT, /* default value for schema variable */
+ EXPR_KIND_LET /* LET assignment (should be same like UPDATE) */
} ParseExprKind;
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index ec6e0c102f..1ee199ed8f 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -32,6 +32,16 @@ extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
int attrno,
List *indirection,
int location);
+extern Node *transformAssignmentIndirection(ParseState *pstate,
+ Node *basenode,
+ const char *targetName,
+ bool targetIsArray,
+ Oid targetTypeId,
+ int32 targetTypMod,
+ Oid targetCollation,
+ ListCell *indirection,
+ Node *rhs,
+ int location);
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
char *colname, int attrno,
List *indirection,
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 82f0f2e741..c49b653555 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -96,7 +96,8 @@ typedef enum
DestCopyOut, /* results sent to COPY TO code */
DestSQLFunction, /* results sent to SQL-language func mgr */
DestTransientRel, /* results sent to transient relation */
- DestTupleQueue /* results sent to tuple queue */
+ DestTupleQueue, /* results sent to tuple queue */
+ DestVariable /* results sents to schema variable */
} CommandDest;
/* ----------------
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f4d4be8d0d..c624d8dd0b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -147,9 +147,11 @@ typedef ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
+#define ACL_READ_CHR 'S' /* 'R' is occupated by old RULE priv */
+#define ACL_WRITE_CHR 'W'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcSW"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -166,6 +168,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_READ|ACL_WRITE)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -253,6 +256,8 @@ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_variable_aclmask(Oid var_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode);
@@ -269,6 +274,7 @@ extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
+extern AclResult pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, ObjectType objtype,
const char *objectname);
@@ -305,6 +311,7 @@ extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool pg_publication_ownercheck(Oid pub_oid, Oid roleid);
extern bool pg_subscription_ownercheck(Oid sub_oid, Oid roleid);
extern bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid);
+extern bool pg_variable_ownercheck(Oid stat_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index e55ea4035b..cb3f4aaca9 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -122,6 +122,7 @@ extern bool get_func_leakproof(Oid funcid);
extern float4 get_func_cost(Oid funcid);
extern float4 get_func_rows(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
+extern Oid get_varname_varid(const char *varname, Oid varnamespace);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4f333586ee..453699be3c 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -107,9 +107,11 @@ enum SysCacheIdentifier
TYPENAMENSP,
TYPEOID,
USERMAPPINGOID,
- USERMAPPINGUSERSERVER
+ USERMAPPINGUSERSERVER,
+ VARIABLENAMENSP,
+ VARIABLEOID
-#define SysCacheSize (USERMAPPINGUSERSERVER + 1)
+#define SysCacheSize (VARIABLEOID + 1)
};
extern void InitCatalogCache(void);
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b500..48286f8e1a 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -105,5 +105,7 @@ ORDER BY 1, 2;
pg_index | indpred | pg_node_tree
pg_largeobject | data | bytea
pg_largeobject_metadata | lomacl | aclitem[]
-(11 rows)
+ pg_variable | varacl | aclitem[]
+ pg_variable | vardefexpr | pg_node_tree
+(13 rows)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 0aa5357917..848b041a4b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -163,6 +163,7 @@ pg_ts_parser|t
pg_ts_template|t
pg_type|t
pg_user_mapping|t
+pg_variable|t
point_tbl|t
polygon_tbl|t
quad_box_tbl|t
diff --git a/src/test/regress/expected/schema_variables.out b/src/test/regress/expected/schema_variables.out
new file mode 100644
index 0000000000..8cddf66573
--- /dev/null
+++ b/src/test/regress/expected/schema_variables.out
@@ -0,0 +1,344 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+DROP VARIABLE var1, var2;
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT var1;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT READ ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT var1;
+ var1
+------
+
+(1 row)
+
+SET ROLE TO DEFAULT;
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to work
+LET var1 = 333;
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var1;
+ERROR: permission denied for schema variable var1
+-- should to work;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: ((g.v)::numeric = var1)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var1;
+SELECT * FROM schema_var_view;
+ var1
+------
+ 333
+(1 row)
+
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ var1
+------
+
+(1 row)
+
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+----------------------------
+ Result
+ Output: 3.14159265358979
+(2 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+SELECT inc(1);
+ inc
+-----
+ 1
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 2
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 3
+(1 row)
+
+SELECT inc(1) FROM generate_series(1,10);
+ inc
+-----
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+(10 rows)
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+-- composite variables
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+\d v1
+\d v2
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+SELECT v1;
+ v1
+------------
+ (1,2,3.14)
+(1 row)
+
+SELECT v2;
+ v2
+---------------
+ (10,20,31.40)
+(1 row)
+
+SELECT (v1).*;
+ x | y | z
+---+---+------
+ 1 | 2 | 3.14
+(1 row)
+
+SELECT (v2).*;
+ x | y | z
+----+----+-------
+ 10 | 20 | 31.40
+(1 row)
+
+SELECT v1.x + v1.z;
+ ?column?
+----------
+ 4.14
+(1 row)
+
+SELECT v2.x + v2.z;
+ ?column?
+----------
+ 41.40
+(1 row)
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+SELECT v2.x;
+ERROR: permission denied for schema variable v2
+SET ROLE TO DEFAULT;
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+DROP ROLE var_test_role;
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+ relname
+----------
+ pg_class
+(1 row)
+
+-- should to fail
+SELECT varx.xxx;
+ERROR: type text is not composite
+-- variables can be updated under RO transaction
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+SELECT varx;
+ varx
+-------
+ hello
+(1 row)
+
+DROP VARIABLE varx;
+CREATE TYPE t1 AS (a int, b numeric, c text);
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+ v1
+----------------------------
+ (1,3.14159265358979,hello)
+(1 row)
+
+LET v1.b = 10.2222;
+SELECT v1;
+ v1
+-------------------
+ (1,10.2222,hello)
+(1 row)
+
+-- should to fail
+LET v1.x = 10;
+ERROR: cannot assign to field "x" of column "x" because there is no such column in data type t1
+LINE 1: LET v1.x = 10;
+ ^
+DROP VARIABLE v1;
+DROP TYPE t1;
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+ va1
+------------
+ {10.1,2.1}
+(1 row)
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+ va2
+--------------------
+ (10.2,"{0.0,0.0}")
+(1 row)
+
+LET va2.b[1] = 10.3;
+SELECT va2;
+ va2
+---------------------
+ (10.2,"{10.3,0.0}")
+(1 row)
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+ v1
+------------------
+ 6.28318530717958
+(1 row)
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+ v2
+--------------------------
+ (3.14159265358979,Hello)
+(1 row)
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+ERROR: cannot drop type t2 because other objects depend on it
+DETAIL: schema variable v2 depends on type t2
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+ var1
+------
+ 1000
+(1 row)
+
+CREATE ROLE var_test_role;
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+DROP VARIABLE var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+SET ROLE TO DEFAULT;
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+SET ROLE TO var_test_role;
+SELECT public.var1;
+ var1
+------
+ 1000
+(1 row)
+
+DROP VARIABLE public.var1;
+SET ROLE TO DEFAULt;
+DROP ROLE var_test_role;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 16f979c8d9..9bf379b87b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml schema_variables
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 42632be675..42bf4ecb3f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -191,3 +191,4 @@ test: partition_aggregate
test: event_trigger
test: fast_default
test: stats
+test: schema_variables
diff --git a/src/test/regress/sql/schema_variables.sql b/src/test/regress/sql/schema_variables.sql
new file mode 100644
index 0000000000..91b2bbb28b
--- /dev/null
+++ b/src/test/regress/sql/schema_variables.sql
@@ -0,0 +1,247 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+
+DROP VARIABLE var1, var2;
+
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+
+CREATE ROLE var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT READ ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+-- should to work
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to work
+LET var1 = 333;
+
+SET ROLE TO DEFAULT;
+
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+
+SELECT secure_var();
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT public.var1;
+
+-- should to work;
+SELECT secure_var();
+
+SET ROLE TO DEFAULT;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+
+CREATE VIEW schema_var_view AS SELECT var1;
+
+SELECT * FROM schema_var_view;
+
+\c -
+
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+
+LET var1 = pi();
+
+SELECT var1;
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+
+EXECUTE var_pp(100, 1.23456);
+
+SELECT var1;
+
+CREATE VARIABLE var3 AS int;
+
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT inc(1);
+SELECT inc(1);
+SELECT inc(1);
+
+SELECT inc(1) FROM generate_series(1,10);
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+LET var3 = 0;
+
+SET ROLE TO DEFAULT;
+
+DROP VIEW schema_var_view;
+
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+
+-- composite variables
+
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+
+\d v1
+\d v2
+
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+
+SELECT v1;
+SELECT v2;
+SELECT (v1).*;
+SELECT (v2).*;
+
+SELECT v1.x + v1.z;
+SELECT v2.x + v2.z;
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+
+SELECT v2.x;
+
+SET ROLE TO DEFAULT;
+
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+DROP ROLE var_test_role;
+
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+
+-- should to fail
+SELECT varx.xxx;
+
+-- variables can be updated under RO transaction
+
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+
+SELECT varx;
+
+DROP VARIABLE varx;
+
+CREATE TYPE t1 AS (a int, b numeric, c text);
+
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+LET v1.b = 10.2222;
+SELECT v1;
+
+-- should to fail
+LET v1.x = 10;
+
+DROP VARIABLE v1;
+DROP TYPE t1;
+
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+LET va2.b[1] = 10.3;
+SELECT va2;
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+
+CREATE ROLE var_test_role;
+
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+DROP VARIABLE var_schema2.var1;
+
+SET ROLE TO DEFAULT;
+
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+
+SET ROLE TO var_test_role;
+SELECT public.var1;
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+
+SELECT public.var1_renamed;
+
+DROP VARIABLE public.var1_renamed;
+
+SET ROLE TO DEFAULt;
+
+DROP ROLE var_test_role;
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-08-11 18:46 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-08-11 18:46 UTC (permalink / raw)
To: Gilles Darold <gilles.darold@dalibo.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
2018-08-11 7:39 GMT+02:00 Pavel Stehule <pavel.stehule@gmail.com>:
> Hi
>
> I am sending updated patch. It should to solve almost all Giles's and
> Peter's objections.
>
> I am not happy so executor access values of variables directly. It is most
> simple implementation - and I hope so it is good enough, but now the access
> to variables is too volatile. But it is works good enough for usability
> testing.
>
> I am thinking about some cache of used variables in ExprContext, so the
> variable in one ExprContext will look like stable - more like PLpgSQL
> variables.
>
I wrote EState based schema variable values cache, so now the variables in
queries are stable (like PARAM_EXTERN) and can be used for optimization.
Regards
Pavel
>
> Regards
>
> Pavel
>
Attachments:
[text/x-patch] schema-variables-180811-02.patch (193.6K, ../../CAFj8pRCTz_CRez3vFo_Ta_m=KtOxBGHE9+T1QG3UgRbuURfzjA@mail.gmail.com/3-schema-variables-180811-02.patch)
download | inline diff:
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3bb48d4ccf..b863823160 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -359,6 +359,11 @@
<entry><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link></entry>
<entry>mappings of users to foreign servers</entry>
</row>
+
+ <row>
+ <entry><link linkend="catalog-pg-variable"><structname>pg_variable</structname></link></entry>
+ <entry>schema variables</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -11311,4 +11316,104 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
+ <sect1 id="catalog-pg-variable">
+ <title><structname>pg_variable</structname></title>
+
+ <indexterm zone="catalog-pg-variable">
+ <primary>pg_variable</primary>
+ </indexterm>
+
+ <para>
+ The table <structname>pg_variable</structname> holds metadata
+ of schema variables.
+ </para>
+
+ <table>
+ <title><structname>pg_views</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><structfield>oid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry></entry>
+ <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Name of the schema variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the namespace that contains this variable
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartype</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the data type of this variable.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartypmod</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ <structfield>vartypmod</structfield> records type-specific data
+ supplied at table creation time (for example, the maximum
+ length of a <type>varchar</type> column). It is passed to
+ type-specific input functions and length coercion functions.
+ The value will generally be -1 for types that do not need <structfield>vartypmod</structfield>.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>varowner</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>Owner of the variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>vardefexpr</structfield></entry>
+ <entry><type>pg_node_tree</type></entry>
+ <entry></entry>
+ <entry>The internal representation of the variable default value</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>
+ Access privileges; see
+ <xref linkend="sql-grant"/> and
+ <xref linkend="sql-revoke"/>
+ for details
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..0631c9ed56 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -47,6 +47,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterType SYSTEM "alter_type.sgml">
<!ENTITY alterUser SYSTEM "alter_user.sgml">
<!ENTITY alterUserMapping SYSTEM "alter_user_mapping.sgml">
+<!ENTITY alterVariable SYSTEM "alter_variable.sgml">
<!ENTITY alterView SYSTEM "alter_view.sgml">
<!ENTITY analyze SYSTEM "analyze.sgml">
<!ENTITY begin SYSTEM "begin.sgml">
@@ -99,6 +100,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createType SYSTEM "create_type.sgml">
<!ENTITY createUser SYSTEM "create_user.sgml">
<!ENTITY createUserMapping SYSTEM "create_user_mapping.sgml">
+<!ENTITY createVariable SYSTEM "create_variable.sgml">
<!ENTITY createView SYSTEM "create_view.sgml">
<!ENTITY deallocate SYSTEM "deallocate.sgml">
<!ENTITY declare SYSTEM "declare.sgml">
@@ -148,6 +150,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropUser SYSTEM "drop_user.sgml">
<!ENTITY dropUserMapping SYSTEM "drop_user_mapping.sgml">
<!ENTITY dropView SYSTEM "drop_view.sgml">
+<!ENTITY dropVariable SYSTEM "drop_variable.sgml">
<!ENTITY end SYSTEM "end.sgml">
<!ENTITY execute SYSTEM "execute.sgml">
<!ENTITY explain SYSTEM "explain.sgml">
@@ -155,6 +158,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY grant SYSTEM "grant.sgml">
<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
<!ENTITY insert SYSTEM "insert.sgml">
+<!ENTITY let SYSTEM "let.sgml">
<!ENTITY listen SYSTEM "listen.sgml">
<!ENTITY load SYSTEM "load.sgml">
<!ENTITY lock SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/alter_variable.sgml b/doc/src/sgml/ref/alter_variable.sgml
new file mode 100644
index 0000000000..6376ac716b
--- /dev/null
+++ b/doc/src/sgml/ref/alter_variable.sgml
@@ -0,0 +1,170 @@
+<!--
+doc/src/sgml/ref/alter_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-altervariable">
+ <indexterm zone="sql-altervariable">
+ <primary>ALTER VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>ALTER VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ALTER VARIABLE</refname>
+ <refpurpose>
+ change the definition of a variable
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="parameter">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>ALTER VARIABLE</command> changes the definition of an existing variable.
+ There are several subforms:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>OWNER</literal></term>
+ <listitem>
+ <para>
+ This form changes the owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RENAME</literal></term>
+ <listitem>
+ <para>
+ This form changes the name of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET SCHEMA</literal></term>
+ <listitem>
+ <para>
+ This form moves the variable into another schema.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </para>
+
+ <para>
+ You must own the variable to use <command>ALTER VARIABLE</command>.
+ To change the schema of a variable, you must also have
+ <literal>CREATE</literal> privilege on the new schema.
+ To alter the owner, you must also be a direct or indirect member of the new
+ owning role, and that role must have <literal>CREATE</literal> privilege on
+ the variable's schema. (These restrictions enforce that altering the owner
+ doesn't do anything you couldn't do by dropping and recreating the variable.
+ However, a superuser can alter ownership of any type anyway.)
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (possibly schema-qualified) of an existing variable to
+ alter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_name</replaceable></term>
+ <listitem>
+ <para>
+ The new name for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_owner</replaceable></term>
+ <listitem>
+ <para>
+ The user name of the new owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_schema</replaceable></term>
+ <listitem>
+ <para>
+ The new schema for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To rename a variable:
+<programlisting>
+ALTER VARIABLE foo RENAME TO boo;
+</programlisting>
+ </para>
+
+ <para>
+ To change the owner of the variable <literal>boo</literal>
+ to <literal>joe</literal>:
+<programlisting>
+ALTER VARIABLE boo OWNER TO joe;
+</programlisting>
+ </para>
+
+ <para>
+ To change the schema of the variable <literal>boo</literal>
+ to <literal>private</literal>:
+<programlisting>
+ALTER VARIABLE boo SET SCHEMA private;
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ This comman is a PostgreSQL extension.
+ </para>
+ </refsect1>
+
+ <refsect1 id="sql-altervariable-see-also">
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
new file mode 100644
index 0000000000..6099538813
--- /dev/null
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -0,0 +1,134 @@
+<!--
+doc/src/sgml/ref/create_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-createvariable">
+ <indexterm zone="sql-createvariable">
+ <primary>CREATE VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE VARIABLE</refname>
+ <refpurpose>define a new permissioned typed schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ]
+</synopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> creates a new schema variable.
+ These variables are scalar typed, non-transactional, and, like relations,
+ exist within a schema with access controlled via
+ <command>GRANT</command> and <command>REVOKE</command>.
+ </para>
+
+ <para>
+ The value of a schema variable is session-local. Retrieving
+ a variable's value will return NULL unless its value has been set
+ to something else in the current session.
+ </para>
+
+ <para>
+ Retrieval is done via the <function>get_schema_variable</function>dunxrion or the SQL
+ command <command>SELECT</command>. Setting of values is done via the
+ <function>set_schema_variable</function> function or the SQL command
+ <command>LET</command>.
+ Notably, while schema variables are in many ways a kind of table you cannot use
+ <command>UPDATE</command> on them.
+ </para>
+
+ <para>
+ For purposes of name uniqueness relation-like objects (e.g., tables, indexes)
+ within the same schema are considered. i.e., you cannot give a table and a
+ schema variable the same name. This is a consequence of them being treated
+ like relations for purposes of <command>SELECT</command>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF NOT EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the name already exists. A notice is issued in this case.
+ Note that type of the variable is not considered, nor could it be since the namespace
+ searched contains non-variable objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the data type of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Use <command>DROP VARIABLE</command> to remove a variable.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Create an integer variable <literal>var1</literal>:
+<programlisting>
+CREATE VARIABLE var1 AS integer;
+SELECT var1;
+</programlisting>
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> is a PostgreSQL feature.
+ <!-- The choice of wording here seems to be left to personal preference... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index 6b909b7232..d83ad811fd 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
+DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES }
</synopsis>
</refsynopsisdiv>
@@ -75,6 +75,17 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>VARIABLES</literal></term>
+ <listitem>
+ <para>
+ Resets the value of all schema variables. When variables
+ will be used later, then will be initialized again to
+ NULL or default value.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
new file mode 100644
index 0000000000..c1c1a2bd67
--- /dev/null
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -0,0 +1,93 @@
+<!--
+doc/src/sgml/ref/drop_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropvariable">
+ <indexterm zone="sql-dropvariable">
+ <primary>DROP VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP VARIABLE</refname>
+ <refpurpose>remove a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP VARIABLE</command> removes a schema variable.
+ A variable can only be dropped by its owner or a superuser.
+ <!-- this would suggest that we need an alter variable owner to command -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the variable does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To remove the schema variable <literal>var1</literal>:
+
+<programlisting>
+DROP VARIABLE var1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ <!-- create variable is a "PostgreSQL feature",
+ this is a "proprietary PostgreSQL command" ... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-createvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index ff64c7a3ba..a83920a7a1 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -79,6 +79,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE <replaceable>type_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
[ GROUP ] <replaceable class="parameter">role_name</replaceable>
@@ -167,6 +171,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
foreign servers,
large objects,
schemas,
+ schema variable
or tablespaces.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
@@ -385,6 +390,24 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>READ</literal></term>
+ <listitem>
+ <para>
+ Allows to read a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WRITE</literal></term>
+ <listitem>
+ <para>
+ Allows to set a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL PRIVILEGES</literal></term>
<listitem>
@@ -550,6 +573,8 @@ rolename=xxxx -- privileges granted to a role
C -- CREATE
c -- CONNECT
T -- TEMPORARY
+ S -- READ
+ w -- WRITE
arwdDxt -- ALL PRIVILEGES (for tables, varies for other objects)
* -- grant option for preceding privilege
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
new file mode 100644
index 0000000000..e8bf3f6dd4
--- /dev/null
+++ b/doc/src/sgml/ref/let.sgml
@@ -0,0 +1,90 @@
+<!--
+doc/src/sgml/ref/let.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-let">
+ <indexterm zone="sql-let">
+ <primary>LET</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>LET</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>LET</refname>
+ <refpurpose>change a schema variable's value</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable class="parameter">sql_expression</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ The <command>LET</command> command updates the specified schema variable' value.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>schema_variable</literal></term>
+ <listitem>
+ <para>
+ The name of schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sql expression</literal></term>
+ <listitem>
+ <para>
+ An SQL expression, the result is cast to the schema variable's type.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Example:
+<programlisting>
+CREATE VARIABLE myvar AS integer;
+LET myvar = 10;
+LET myvar = (SELECT sum(val) FROM tab);
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <!-- this feels like it needs to be more specific,
+ but I don't know enough to make it so -->
+ <literal>LET</literal> extends syntax defined in the SQL
+ standard. The standard knows <literal>SET</literal> command,
+ that is used for different purpouse in PostgreSQL.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 5317f8ccba..8435e05957 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -108,6 +108,12 @@ REVOKE [ GRANT OPTION FOR ]
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_name</replaceable> [, ...]
[ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { READ | WRITE } [, ...] | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..5fb82df51e 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -75,6 +75,7 @@
&alterType;
&alterUser;
&alterUserMapping;
+ &alterVariable;
&alterView;
&analyze;
&begin;
@@ -127,6 +128,7 @@
&createType;
&createUser;
&createUserMapping;
+ &createVariable;
&createView;
&deallocate;
&declare;
@@ -175,6 +177,7 @@
&dropType;
&dropUser;
&dropUserMapping;
+ &dropVariable;
&dropView;
&end;
&execute;
@@ -183,6 +186,7 @@
&grant;
&importForeignSchema;
&insert;
+ &let;
&listen;
&load;
&lock;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 0865240f11..1f7c4d1223 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -19,7 +19,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_publication.o pg_range.o \
pg_db_role_setting.o pg_shdepend.o pg_subscription.o pg_type.o \
- storage.o toasting.o
+ pg_variable.o storage.o toasting.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
@@ -46,7 +46,7 @@ CATALOG_HEADERS := \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
- pg_subscription_rel.h
+ pg_subscription_rel.h pg_variable.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 578e4c6592..86917e15a8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -57,6 +57,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_transform.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
@@ -112,6 +113,7 @@ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
+static void ExecGrant_Variable(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
@@ -284,6 +286,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -507,6 +512,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -609,6 +618,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_VARIABLE:
+ ExecGrant_Variable(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -768,6 +780,16 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_VARIABLE:
+ foreach(cell, objnames)
+ {
+ RangeVar *varvar = (RangeVar *) lfirst(cell);
+ Oid relOid;
+
+ relOid = lookup_variable(varvar->schemaname, varvar->relname, false);
+ objects = lappend_oid(objects, relOid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -855,6 +877,31 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
heap_close(rel, AccessShareLock);
}
break;
+ case OBJECT_VARIABLE:
+ {
+ ScanKeyData key;
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&key,
+ Anum_pg_variable_varnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(namespaceId));
+
+ rel = heap_open(VariableRelationId, AccessShareLock);
+ scan = heap_beginscan_catalog(rel, 1, &key);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ }
+ break;
+
default:
/* should not happen */
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
@@ -1018,6 +1065,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1215,6 +1266,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1441,6 +1498,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -3266,6 +3326,129 @@ ExecGrant_Type(InternalGrant *istmt)
heap_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Variable(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_VARIABLE;
+
+ relation = heap_open(VariableRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid varId = lfirst_oid(cell);
+ Form_pg_variable pg_variable_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_variable];
+ bool nulls[Natts_pg_variable];
+ bool replaces[Natts_pg_variable];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for schema variables %u", varId);
+
+ pg_variable_tuple = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_variable_tuple->varowner;
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple, Anum_pg_variable_varacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_VARIABLE, ownerId);
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ oldmembers = NULL;
+ }
+ else
+ {
+ old_acl = DatumGetAclPCopy(aclDatum);
+ /* Get the roles mentioned in the existing ACL */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+ }
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ varId, grantorId, OBJECT_VARIABLE,
+ NameStr(pg_variable_tuple->varname),
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ */
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_variable_varacl - 1] = true;
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(varId, VariableRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(VariableRelationId, varId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3298,6 +3481,10 @@ string_to_privilege(const char *privname)
return ACL_CONNECT;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
+ if (strcmp(privname, "read") == 0)
+ return ACL_READ;
+ if (strcmp(privname, "write") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized privilege type \"%s\"", privname)));
@@ -3333,6 +3520,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3456,6 +3647,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3566,6 +3760,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3710,6 +3907,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
+ case OBJECT_VARIABLE:
+ return pg_variable_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objtype: %d",
(int) objtype);
@@ -4499,6 +4698,67 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a variable.
+ */
+AclMode
+pg_variable_aclmask(Oid var_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_variable varForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the type's tuple from pg_type
+ */
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable with OID %u does not exist",
+ var_oid)));
+ varForm = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Now get the type's owner and ACL from the tuple
+ */
+ ownerId = varForm->varowner;
+
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple,
+ Anum_pg_variable_varacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_VARIABLE, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+
/*
* Exported routine for checking a user's access privileges to a column
*
@@ -4744,6 +5004,18 @@ pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a variable
+ */
+AclResult
+pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
+{
+ if (pg_variable_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Ownership check for a relation (specified by OID).
*/
@@ -5361,6 +5633,33 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a schema variables (specified by OID).
+ */
+bool
+pg_variable_ownercheck(Oid db_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(db_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("variable with OID %u does not exist", db_oid)));
+
+ ownerId = ((Form_pg_variable) GETSTRUCT(tuple))->varowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+
/*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
@@ -5486,6 +5785,10 @@ get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
+ case OBJECT_VARIABLE:
+ defaclobjtype = DEFACLOBJ_VARIABLE;
+ break;
+
default:
return NULL;
}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 4f1d365357..782ddb1655 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -59,6 +59,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -67,6 +68,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
@@ -1280,6 +1282,10 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_VARIABLE:
+ RemoveVariableById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2537,6 +2543,9 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case VariableRelationId:
+ return OCLASS_VARIABLE;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 0f67a122ed..81aaf454a8 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -39,6 +39,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
@@ -755,6 +756,71 @@ RelationIsVisible(Oid relid)
return visible;
}
+/*
+ * VariableIsVisible
+ * Determine whether a variable (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified variable name".
+ */
+bool
+VariableIsVisible(Oid varid)
+{
+ HeapTuple vartup;
+ Form_pg_variable varform;
+ Oid varnamespace;
+ bool visible;
+
+ vartup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(vartup))
+ elog(ERROR, "cache lookup failed for schema variable %u", varid);
+ varform = (Form_pg_variable) GETSTRUCT(vartup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ varnamespace = varform->varnamespace;
+ if (varnamespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, varnamespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another relation of the same name earlier in the path. So
+ * we must do a slow check for conflicting relations.
+ */
+ char *varname = NameStr(varform->varname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == varnamespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (OidIsValid(get_varname_varid(varname, namespaceId)))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(vartup);
+
+ return visible;
+}
+
+
/*
* TypenameGetTypid
@@ -2776,6 +2842,202 @@ TSConfigIsVisible(Oid cfgid)
return visible;
}
+/*
+ * When we know a variable name, then we can find variable simply
+ */
+Oid
+lookup_variable(const char *nspname, const char *varname, bool missing_ok)
+{
+ Oid namespaceId;
+ Oid varoid = InvalidOid;
+ ListCell *l;
+
+ if (nspname)
+ {
+ namespaceId = LookupExplicitNamespace(nspname, missing_ok);
+ if (!OidIsValid(namespaceId))
+ return InvalidOid;
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+
+ if (OidIsValid(varoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(varoid) && !missing_ok)
+ {
+ if (nspname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\".\"%s\" does not exist",
+ nspname, varname)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\" does not exist",
+ varname)));
+ }
+
+ return varoid;
+}
+
+List *
+NamesFromList(List *names)
+{
+ ListCell *l;
+ List *result = NIL;
+
+ foreach(l, names)
+ {
+ Node *n = lfirst(l);
+
+ if (IsA(n, String))
+ {
+ result = lappend(result, n);
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * identify_variable
+ *
+ * Returns oid of not ambigonuous variable specified by qualified path
+ * or InvalidOid. When the path is ambigonuous, then not_uniq flag is
+ * is true.
+ */
+Oid
+identify_variable(List *names, char **attrname, bool *not_uniq)
+{
+ char *a = NULL;
+ char *b = NULL;
+ char *c = NULL;
+ char *d = NULL;
+ Oid varoid_without_attr;
+ Oid varoid_with_attr;
+
+ *not_uniq = false;
+
+ switch (list_length(names))
+ {
+ case 1:
+ a = strVal(linitial(names));
+ return lookup_variable(NULL, a, true);
+
+ case 2:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+
+ /*
+ * a.b can mean "schema"."variable" or "variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(a, b, true);
+ varoid_with_attr = lookup_variable(NULL, a, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = b;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 3:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+
+ /*
+ * a.b.c can mean "catalog"."schema"."variable" or "schema"."variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(b, c, true);
+ varoid_with_attr = lookup_variable(a, b, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = c;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 4:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+ d = strVal(lfourth(names));
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ *attrname = d;
+ return lookup_variable(b, c, true);
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(names))));
+ break;
+ }
+}
/*
* DeconstructQualifiedName
@@ -4416,3 +4678,14 @@ pg_is_other_temp_schema(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(isOtherTempNamespace(oid));
}
+
+Datum
+pg_variable_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ if (!SearchSysCacheExists1(VARIABLEOID, ObjectIdGetDatum(oid)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_BOOL(VariableIsVisible(oid));
+}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7db942dcba..cc3d415e61 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -58,6 +58,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -489,6 +490,18 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
OBJECT_STATISTIC_EXT,
true
+ },
+ {
+ VariableRelationId,
+ VariableObjectIndexId,
+ VARIABLEOID,
+ VARIABLENAMENSP,
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ Anum_pg_variable_varowner,
+ Anum_pg_variable_varacl,
+ OBJECT_VARIABLE,
+ true
}
};
@@ -714,6 +727,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_VARIABLE */
+ {
+ "schema variable", OBJECT_VARIABLE
}
};
@@ -739,6 +756,7 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
bool missing_ok);
static ObjectAddress get_object_address_type(ObjectType objtype,
TypeName *typename, bool missing_ok);
+static ObjectAddress get_object_address_variable(List *object, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object,
bool missing_ok);
static ObjectAddress get_object_address_opf_member(ObjectType objtype,
@@ -996,6 +1014,10 @@ get_object_address(ObjectType objtype, Node *object,
missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_VARIABLE:
+ address = get_object_address_variable(castNode(List, object), missing_ok);
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
@@ -1848,16 +1870,20 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type \"%c\"", objtype),
- errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
+ errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
DEFACLOBJ_RELATION,
DEFACLOBJ_SEQUENCE,
DEFACLOBJ_FUNCTION,
DEFACLOBJ_TYPE,
- DEFACLOBJ_NAMESPACE)));
+ DEFACLOBJ_NAMESPACE,
+ DEFACLOBJ_VARIABLE)));
}
/*
@@ -1942,6 +1968,24 @@ textarray_to_strvaluelist(ArrayType *arr)
return list;
}
+/*
+ * Find the ObjectAddress for a type or domain
+ */
+static ObjectAddress
+get_object_address_variable(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ char *nspname = NULL;
+ char *varname = NULL;
+
+ ObjectAddressSet(address, VariableRelationId, InvalidOid);
+
+ DeconstructQualifiedName(object, &nspname, &varname);
+ address.objectId = lookup_variable(nspname, varname, missing_ok);
+
+ return address;
+}
+
/*
* SQL-callable version of get_object_address
*/
@@ -2131,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_TABCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
+ case OBJECT_VARIABLE:
objnode = (Node *) name;
break;
case OBJECT_ACCESS_METHOD:
@@ -2415,6 +2460,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
break;
+ case OBJECT_VARIABLE:
+ if (!pg_variable_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
default:
elog(ERROR, "unrecognized object type: %d",
(int) objtype);
@@ -3157,6 +3207,32 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_VARIABLE:
+ {
+ char *nspname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ if (VariableIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ appendStringInfo(&buffer, _("schema variable %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(varform->varname)));
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_TSPARSER:
{
HeapTuple tup;
@@ -3422,6 +3498,16 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new schemas belonging to role %s"),
rolename);
break;
+ case DEFACLOBJ_VARIABLE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s"),
+ rolename);
+ break;
default:
/* shouldn't get here */
if (nspname)
@@ -4070,6 +4156,10 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_VARIABLE:
+ appendStringInfoString(&buffer, "schema variable");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4962,6 +5052,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on variables");
+ break;
}
if (objname)
@@ -5121,6 +5215,33 @@ getObjectIdentityParts(const ObjectAddress *object,
}
break;
+ case OCLASS_VARIABLE:
+ {
+ char *schema;
+ char *varname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ schema = get_namespace_name_or_temp(varform->varnamespace);
+ varname = NameStr(varform->varname);
+
+ appendStringInfo(&buffer, "%s",
+ quote_qualified_identifier(schema, varname));
+
+ if (objname)
+ *objname = list_make2(schema, varname);
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
diff --git a/src/backend/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c
new file mode 100644
index 0000000000..ff71f8bf6a
--- /dev/null
+++ b/src/backend/catalog/pg_variable.c
@@ -0,0 +1,305 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.c
+ * schema variables
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_variable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+
+#include "nodes/makefuncs.h"
+
+#include "storage/lmgr.h"
+
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * Returns name of schema variable. When variable is not on path,
+ * then the name is qualified.
+ */
+char *
+schema_variable_get_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+ char *nspname;
+ char *result;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ if (VariableIsVisible(varid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ result = quote_qualified_identifier(nspname, varname);
+
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/*
+ * Returns varname field of pg_variable
+ */
+char *
+get_schema_variable_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ ReleaseSysCache(tup);
+
+ return varname;
+}
+
+/*
+ * Returns type, typmod of schema variable
+ */
+void
+get_schema_variable_type_typmod(Oid varid, Oid *typid, int32 *typmod)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ *typid = varform->vartype;
+ *typmod = varform->vartypmod;
+
+ ReleaseSysCache(tup);
+
+ return;
+}
+
+/*
+ * Fetch all fields of schema variable from the syscache.
+ */
+Variable *
+GetVariable(Oid varid, bool missing_ok)
+{
+ HeapTuple tup;
+ Variable *var;
+ Form_pg_variable varform;
+ Datum aclDatum;
+ Datum defexprDatum;
+ bool isnull;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (missing_ok)
+ return NULL;
+
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+ }
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ var = (Variable *) palloc(sizeof(Variable));
+ var->oid = varid;
+ var->name = pstrdup(NameStr(varform->varname));
+ var->namespace = varform->varnamespace;
+ var->typid = varform->vartype;
+ var->typmod = varform->vartypmod;
+ var->owner = varform->varowner;
+
+ /* Get defexpr */
+ defexprDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_vardefexpr,
+ &isnull);
+
+ if (!isnull)
+ var->defexpr = stringToNode(TextDatumGetCString(defexprDatum));
+ else
+ var->defexpr = NULL;
+
+ /* Get varacl */
+ aclDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_varacl,
+ &isnull);
+ if (!isnull)
+ var->acl = DatumGetAclPCopy(aclDatum);
+ else
+ var->acl = NULL;
+
+ ReleaseSysCache(tup);
+
+ return var;
+}
+
+ObjectAddress
+VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Node *varDefexpr,
+ bool if_not_exists)
+{
+ Acl *varacl;
+ NameData varname;
+ bool nulls[Natts_pg_variable];
+ Datum values[Natts_pg_variable];
+ Relation rel;
+ HeapTuple tup,
+ oldtup;
+ TupleDesc tupdesc;
+ ObjectAddress myself,
+ referenced;
+ Oid retval;
+ int i;
+
+ for (i = 0; i < Natts_pg_variable; i++)
+ {
+ nulls[i] = false;
+ values[i] = (Datum) 0;
+ }
+
+ namestrcpy(&varname, varName);
+ values[Anum_pg_variable_varname - 1] = NameGetDatum(&varname);
+ values[Anum_pg_variable_varnamespace - 1] = ObjectIdGetDatum(varNamespace);
+ values[Anum_pg_variable_vartype - 1] = ObjectIdGetDatum(varType);
+ values[Anum_pg_variable_vartypmod - 1] = Int32GetDatum(varTypmod);
+ values[Anum_pg_variable_varowner - 1] = ObjectIdGetDatum(varOwner);
+ /* proacl will be determined later */
+
+ if (varDefexpr)
+ values[Anum_pg_variable_vardefexpr - 1] = CStringGetTextDatum(nodeToString(varDefexpr));
+ else
+ nulls[Anum_pg_variable_vardefexpr - 1] = true;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+ tupdesc = RelationGetDescr(rel);
+
+ oldtup = SearchSysCache2(VARIABLENAMENSP,
+ PointerGetDatum(varName),
+ ObjectIdGetDatum(varNamespace));
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ if (if_not_exists)
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists, skipping",
+ varName)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists",
+ varName)));
+
+ heap_freetuple(oldtup);
+ heap_close(rel, RowExclusiveLock);
+
+ return InvalidObjectAddress;
+ }
+
+ varacl = get_user_default_acl(OBJECT_VARIABLE, varOwner,
+ varNamespace);
+
+ if (varacl != NULL)
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(varacl);
+ else
+ nulls[Anum_pg_variable_varacl - 1] = true;
+
+ tup = heap_form_tuple(tupdesc, values, nulls);
+ CatalogTupleInsert(rel, tup);
+
+ retval = HeapTupleGetOid(tup);
+
+ myself.classId = VariableRelationId;
+ myself.objectId = retval;
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = varNamespace;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on used type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = varType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on any roles mentioned in ACL */
+ if (varacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(varacl, &newmembers);
+ updateAclDependencies(VariableRelationId, retval, 0,
+ varOwner,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_freetuple(tup);
+
+ /* Post creation hook for new function */
+ InvokeObjectPostCreateHook(VariableRelationId, retval, 0);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..2cb5b1172d 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,7 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
- schemacmds.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
+ schemacmds.o seclabel.o sequence.o schemavariable.o statscmds.o subscriptioncmds.o \
tablecmds.o tablespace.o trigger.o tsearchcmds.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index eff325cc7d..a9d5e5e0ad 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -387,6 +387,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_VARIABLE:
{
ObjectAddress address;
Relation catalog;
@@ -504,6 +505,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
@@ -594,6 +596,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_TSDICT:
case OCLASS_TSTEMPLATE:
case OCLASS_TSCONFIG:
+ case OCLASS_VARIABLE:
{
Relation catalog;
@@ -852,6 +855,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_TABLESPACE:
case OBJECT_TSDICTIONARY:
case OBJECT_TSCONFIGURATION:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 01a999c2ac..fec2495e93 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -19,6 +19,7 @@
#include "commands/discard.h"
#include "commands/prepare.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "utils/guc.h"
#include "utils/portal.h"
@@ -48,6 +49,10 @@ DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
ResetTempTableNamespace();
break;
+ case DISCARD_VARIABLES:
+ ResetSchemaVariableCache();
+ break;
+
default:
elog(ERROR, "unrecognized DISCARD target: %d", stmt->target);
}
@@ -75,4 +80,5 @@ DiscardAll(bool isTopLevel)
ResetPlanCache();
ResetTempTableNamespace();
ResetSequenceCaches();
+ ResetSchemaVariableCache();
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e..426df246b3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -126,6 +126,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"TEXT SEARCH TEMPLATE", true},
{"TYPE", true},
{"USER MAPPING", true},
+ {"VARIABLE", true},
{"VIEW", true},
{NULL, false}
};
@@ -297,7 +298,8 @@ check_ddl_tag(const char *tag)
pg_strcasecmp(tag, "REVOKE") == 0 ||
pg_strcasecmp(tag, "DROP OWNED") == 0 ||
pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
+ pg_strcasecmp(tag, "SECURITY LABEL") == 0 ||
+ pg_strcasecmp(tag, "CREATE VARIABLE") == 0)
return EVENT_TRIGGER_COMMAND_TAG_OK;
/*
@@ -1146,6 +1148,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -1209,6 +1212,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
return true;
/*
@@ -2244,6 +2248,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2326,6 +2332,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b1556a..eb8c08baf3 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_PLAN_UTILITY:
/* OK */
break;
default:
diff --git a/src/backend/commands/schemavariable.c b/src/backend/commands/schemavariable.c
new file mode 100644
index 0000000000..5b65d762d4
--- /dev/null
+++ b/src/backend/commands/schemavariable.c
@@ -0,0 +1,490 @@
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
+#include "executor/executor.h"
+#include "executor/svariableReceiver.h"
+#include "nodes/execnodes.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/inval.h"
+#include "utils/memutils.h"
+#include "utils/lsyscache.h"
+#include "utils/snapmgr.h"
+#include "utils/syscache.h"
+
+/*
+ * The content of variables is not transactional. Due this fact the
+ * implementation of DROP can be simple, because although DROP VARIABLE
+ * can be reverted, the content of variable can be lost. In this example,
+ * DROP VARIABLE is same like reset variable.
+ */
+
+typedef struct SchemaVariableData
+{
+ Oid varid; /* pg_variable OID of this sequence (hash key) */
+ Oid typid; /* OID of the data type */
+ int32 typmod;
+ int16 typlen;
+ bool typbyval;
+ bool isnull;
+ bool freeval;
+ Datum value;
+ bool is_rowtype; /* true when variable is composite */
+ bool is_valid; /* true when variable was successfuly initialized */
+} SchemaVariableData;
+
+typedef SchemaVariableData *SchemaVariable;
+
+static HTAB *schemavarhashtab = NULL; /* hash table for session variables */
+static MemoryContext SchemaVariableMemoryContext = NULL;
+
+static bool first_time = true;
+static void create_schemavar_hashtable(void);
+static bool clean_cache_req = false;
+
+static void clean_cache(void);
+static void force_clean_cache(XactEvent event, void *arg);
+
+
+/*
+ * Save info about ncessity to clean hash table, because some
+ * schema variable was dropped. Don't do here more, recheck
+ * needs to be in transaction state.
+ */
+static void
+InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ if (cacheid != VARIABLEOID)
+ return;
+
+ clean_cache_req = true;
+}
+
+static void
+force_clean_cache(XactEvent event, void *arg)
+{
+ /*
+ * should continue only in transaction time, when
+ * syscache is available.
+ */
+ if (clean_cache_req && IsTransactionState())
+ {
+ clean_cache();
+ clean_cache_req = false;
+ }
+}
+
+static void
+clean_cache(void)
+{
+ HASH_SEQ_STATUS status;
+ SchemaVariable var;
+
+ if (!schemavarhashtab)
+ return;
+
+ hash_seq_init(&status, schemavarhashtab);
+
+ /*
+ * Every valid variable have to have entry in system
+ * catalog. Removed if there is nothing.
+ */
+ while ((var = (SchemaVariable) hash_seq_search(&status)) != NULL)
+ {
+ HeapTuple tp = InvalidOid;
+
+ tp = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var->varid));
+ if (!HeapTupleIsValid(tp))
+ {
+ elog(DEBUG1, "variable %d is removed from cache", var->varid);
+
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (hash_search(schemavarhashtab,
+ (void *) &var->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(DEBUG1, "hash table corrupted");
+ }
+ else
+ ReleaseSysCache(tp);
+ }
+}
+
+char *
+VariableGetName(Variable *var)
+{
+ char *nspname;
+
+ if (VariableIsVisible(var->oid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(var->namespace);
+
+ return quote_qualified_identifier(nspname, var->name);
+}
+
+/*
+ * Create the hash table for storing schema variables
+ */
+static void
+create_schemavar_hashtable(void)
+{
+ HASHCTL ctl;
+
+ /* set callbacks */
+ if (first_time)
+ {
+ CacheRegisterSyscacheCallback(VARIABLEOID,
+ InvalidateSchemaVarCacheCallback,
+ (Datum) 0);
+
+ RegisterXactCallback(force_clean_cache, NULL);
+
+ first_time = false;
+ }
+
+ /* needs own long life memory context */
+ if (SchemaVariableMemoryContext == NULL)
+ {
+ SchemaVariableMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "schema variables",
+ ALLOCSET_START_SMALL_SIZES);
+ }
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(SchemaVariableData);
+ ctl.hcxt = SchemaVariableMemoryContext;
+
+ schemavarhashtab = hash_create("Schema variables", 64, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/*
+ * Fast drop complete content of schema variables
+ */
+void
+ResetSchemaVariableCache(void)
+{
+ if (schemavarhashtab)
+ {
+ hash_destroy(schemavarhashtab);
+ schemavarhashtab = NULL;
+ }
+
+ if (SchemaVariableMemoryContext != NULL)
+ {
+ MemoryContextReset(SchemaVariableMemoryContext);
+ }
+}
+
+/*
+ * Drop variable by OID
+ */
+void
+RemoveVariableById(Oid varid)
+{
+ Relation rel;
+ HeapTuple tup;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ CatalogTupleDelete(rel, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Creates new variable - entry in pg_catalog.pg_variable table
+ */
+ObjectAddress
+DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt)
+{
+ Oid namespaceid;
+ AclResult aclresult;
+ Oid typid;
+ int32 typmod;
+ Oid varowner = GetUserId();
+
+ Node *cooked_default = NULL;
+
+ namespaceid =
+ RangeVarGetAndCheckCreationNamespace(stmt->variable, NoLock, NULL);
+
+ typenameTypeIdAndMod(pstate, stmt->typeName, &typid, &typmod);
+
+ aclresult = pg_type_aclcheck(typid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error_type(aclresult, typid);
+
+ if (stmt->defexpr)
+ {
+ cooked_default = transformExpr(pstate, stmt->defexpr,
+ EXPR_KIND_VARIABLE_DEFAULT);
+
+ cooked_default = coerce_to_specific_type(pstate,
+ cooked_default, typid, "DEFAULT");
+ }
+
+ return VariableCreate(stmt->variable->relname,
+ namespaceid,
+ typid,
+ typmod,
+ varowner,
+ cooked_default,
+ stmt->if_not_exists);
+}
+
+/*
+ * Try to search value in hash table. If doesn't
+ * exists insert it (and calculate defexpr if exists.
+ */
+static SchemaVariable
+PrepareSchemaVariableForReading(Oid varid)
+{
+ SchemaVariable svar;
+ Variable *var;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+ if (!found)
+ {
+ var = GetVariable(varid, false);
+ get_typlenbyval(var->typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = var->typid;
+ svar->typmod = var->typmod;
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+ svar->is_rowtype = type_is_rowtype(var->typid);
+
+ /* when we don't need calculate defexpr, value is valid already */
+ svar->is_valid = var->defexpr ? false : true;
+ }
+ else if (!svar->is_valid)
+ {
+ /* we need var to recalculate defexpr */
+ var = GetVariable(varid, false);
+ }
+ else
+ /* we don't need to go to sys cache */
+ var = NULL;
+
+ /*
+ * Initialize variable when it is necessary. It is fresh
+ * or last initialization was not successfull.
+ */
+ if (var != NULL && var->defexpr && !svar->is_valid)
+ {
+ MemoryContext oldcontext = NULL;
+
+ Datum value = (Datum) 0;
+ bool null;
+ EState *estate = NULL;
+ Expr *defexpr;
+ ExprState *defexprs;
+
+ /* Prepare default expr */
+ estate = CreateExecutorState();
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ defexpr = expression_planner((Expr *) var->defexpr);
+ defexprs = ExecInitExpr(defexpr, NULL);
+ value = ExecEvalExprSwitchContext(defexprs, GetPerTupleExprContext(estate), &null);
+
+ MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!null)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ FreeExecutorState(estate);
+ }
+
+ if (!svar->is_valid)
+ elog(ERROR, "the content of variable is not valid");
+
+ return svar;
+}
+
+/*
+ * Returns content of variable. We expext secured access now.
+ * Secure check should be done before.
+ */
+Datum
+GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid)
+{
+ SchemaVariable svar;
+
+ svar = PrepareSchemaVariableForReading(varid);
+ *isNull = svar->isnull;
+
+ if (expected_typid != svar->typid)
+ elog(ERROR, "type of variable \"%s\" is different than expected",
+ schema_variable_get_name(varid));
+
+ return (Datum) svar->value;
+}
+
+
+Datum
+GetSchemaVariableCopy(Oid varid, bool *isNull, Oid expected_typid)
+{
+ SchemaVariable svar;
+
+ svar = PrepareSchemaVariableForReading(varid);
+ *isNull = svar->isnull;
+
+ if (expected_typid != svar->typid)
+ elog(ERROR, "type of variable \"%s\" is different than expected",
+ schema_variable_get_name(varid));
+
+ if (!svar->isnull)
+ return datumCopy(svar->value, svar->typbyval, svar->typlen);
+
+ return (Datum) 0;
+}
+
+
+/*
+ * Write value to variable. We expect secured access in this moment.
+ * In this time, we recheck syschache about used type.
+ */
+void
+SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod)
+{
+ MemoryContext oldcontext = NULL;
+
+ SchemaVariable svar;
+ Oid var_typid;
+ int32 var_typmod;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+
+ get_schema_variable_type_typmod(varid, &var_typid, &var_typmod);
+
+ /* check types first */
+ if (var_typid != typid)
+ elog(ERROR, "type of expression is different than schema variable type");
+
+ if (found)
+ {
+ /* release current content first */
+ if (svar->freeval)
+ {
+ pfree(DatumGetPointer(svar->value));
+ svar->value = (Datum) 0;
+ svar->isnull = true;
+ svar->freeval = false;
+ }
+ }
+
+ get_typlenbyval(typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = typid;
+ svar->typmod = typmod;
+
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+
+ svar->is_rowtype = type_is_rowtype(typid);
+ svar->is_valid = false;
+
+ oldcontext = MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!isNull)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+}
+
+void
+doLetStmt(PlannedStmt *pstmt,
+ ParamListInfo params,
+ QueryEnvironment *queryEnv,
+ const char *queryString)
+{
+ QueryDesc *queryDesc;
+ DestReceiver *dest;
+
+ PushCopiedSnapshot(GetActiveSnapshot());
+ UpdateActiveSnapshotCommandId();
+
+ /* Create dest receiver for LET */
+ dest = CreateDestReceiver(DestVariable);
+
+ SetVariableDestReceiverParams(dest, pstmt->resultVariable);
+
+ /* Create a QueryDesc requesting no output */
+ queryDesc = CreateQueryDesc(pstmt, queryString,
+ GetActiveSnapshot(),
+ InvalidSnapshot,
+ dest, params, queryEnv, 0);
+
+ ExecutorStart(queryDesc, 0);
+ ExecutorRun(queryDesc, ForwardScanDirection, 2L, true);
+ ExecutorFinish(queryDesc);
+ ExecutorEnd(queryDesc);
+
+ FreeQueryDesc(queryDesc);
+
+ PopActiveSnapshot();
+}
+
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cef6632840..30e6c1290b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9656,6 +9656,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
/*
* We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index cc09895fa5..ee8ff7da9e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -29,6 +29,6 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
nodeCtescan.o nodeNamedtuplestorescan.o nodeWorktablescan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o \
- nodeTableFuncscan.o
+ nodeTableFuncscan.o svariableReceiver.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d7..442c539991 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -33,6 +33,7 @@
#include "access/nbtree.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -727,6 +728,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
Param *param = (Param *) node;
ParamListInfo params;
+ AclResult aclresult;
switch (param->paramkind)
{
@@ -736,6 +738,26 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
break;
+ case PARAM_SCHEMA_VARIABLE:
+ /* Check permission to read schema variable */
+ aclresult = pg_variable_aclcheck(param->paramid, GetUserId(), ACL_READ);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE,
+ schema_variable_get_name(param->paramid));
+
+ /*
+ * Using varoid as paramid is not practical. Better to recount
+ * used schema variables from zero, and later to use paramid like
+ * offset.
+ */
+ scratch.opcode = EEOP_PARAM_VARIABLE;
+ scratch.d.vparam.paramid = state->nvariables++;
+ scratch.d.vparam.varoid = param->paramid;
+ scratch.d.vparam.paramtype = param->paramtype;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+
case PARAM_EXTERN:
/*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25aae5..6c25689a2a 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -351,6 +352,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1007,6 +1009,71 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_VARIABLE)
+ {
+ EState *estate = econtext->ecxt_estate;
+ MemoryContext old_cxt;
+
+ if (estate && !estate->es_shared)
+ {
+ int paramid = op->d.vparam.paramid;
+
+ if (estate->es_nvariables == 0)
+ {
+ /*
+ * A query's schema variable's cache should be initialized. This cache
+ * is related to EState. When estate is used per query, then this caching
+ * is not surprise. A unexpected behave can be with some PLpgSQL expressions
+ * where EState is reused, so in this case, don't use this cache.
+ */
+ old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ estate->es_nvariables = state->nvariables;
+ estate->es_varnulls = palloc(sizeof(bool) * state->nvariables);
+ estate->es_vartypes = palloc0(sizeof(Oid) * state->nvariables);
+ estate->es_varvalues = palloc(sizeof(Datum) * state->nvariables);
+
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ Assert(estate->es_nvariables == state->nvariables);
+ Assert(estate->es_nvariables > paramid);
+
+ if (!OidIsValid(estate->es_vartypes[paramid]))
+ {
+ old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ estate->es_varvalues[paramid] =
+ GetSchemaVariableCopy(op->d.vparam.varoid,
+ &estate->es_varnulls[paramid],
+ op->d.vparam.paramtype);
+ estate->es_vartypes[paramid] = op->d.vparam.paramtype;
+
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ Assert(OidIsValid(estate->es_vartypes[paramid]));
+
+ *op->resvalue = estate->es_varvalues[paramid];
+ *op->resnull = estate->es_varnulls[paramid];
+ }
+ else
+ {
+ Datum d;
+ bool isnull;
+
+ /* read content of variable every time */
+ d = GetSchemaVariable(op->d.vparam.varoid,
+ &isnull,
+ op->d.vparam.paramtype);
+
+ *op->resvalue = d;
+ *op->resnull = isnull;
+ }
+
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_CASE_TESTVAL)
{
/*
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index b797d064b7..a49deb810c 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,9 +43,11 @@
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_publication.h"
+#include "catalog/pg_variable.h"
#include "commands/matview.h"
#include "commands/trigger.h"
#include "executor/execdebug.h"
+#include "executor/svariableReceiver.h"
#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -204,12 +206,18 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
*/
estate->es_queryEnv = queryDesc->queryEnv;
+ /*
+ * Result can be stored in schema variable.
+ */
+ estate->es_result_variable = queryDesc->plannedstmt->resultVariable;
+
/*
* If non-read-only query, set the command ID to mark output tuples with
*/
switch (queryDesc->operation)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
@@ -345,6 +353,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate->es_lastoid = InvalidOid;
sendTuples = (operation == CMD_SELECT ||
+ OidIsValid(estate->es_result_variable) ||
queryDesc->plannedstmt->hasReturning);
if (sendTuples)
@@ -924,6 +933,17 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_num_root_result_relations = 0;
}
+ if (OidIsValid(estate->es_result_variable))
+ {
+ AclResult aclresult;
+ Oid varid = estate->es_result_variable;
+
+ /* Ensure this variable is writeable */
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, schema_variable_get_name(varid));
+ }
+
/*
* Similarly, we have to lock relations selected FOR [KEY] UPDATE/SHARE
* before we initialize the plan tree, else we'd be risking lock upgrades.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5b3eaec80b..eca7805517 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -102,6 +102,7 @@ CreateExecutorState(void)
/*
* Initialize all fields of the Executor State structure
*/
+ estate->es_shared = false;
estate->es_direction = ForwardScanDirection;
estate->es_snapshot = InvalidSnapshot; /* caller must initialize this */
estate->es_crosscheck_snapshot = InvalidSnapshot; /* no crosscheck */
diff --git a/src/backend/executor/svariableReceiver.c b/src/backend/executor/svariableReceiver.c
new file mode 100644
index 0000000000..0eac4b5d0c
--- /dev/null
+++ b/src/backend/executor/svariableReceiver.c
@@ -0,0 +1,145 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.c
+ * An implementation of DestReceiver that stores the result value in
+ * a schema variable.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/svariableReceiver.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/tuptoaster.h"
+#include "executor/svariableReceiver.h"
+#include "commands/schemavariable.h"
+
+typedef struct
+{
+ DestReceiver pub;
+ Oid varid;
+ Oid typid;
+ int32 typmod;
+ int typlen;
+ int slot_offset;
+ int rows;
+} svariableState;
+
+
+/*
+ * Prepare to receive tuples from executor.
+ */
+static void
+svariableStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+ svariableState *myState = (svariableState *) self;
+ int natts = typeinfo->natts;
+ int outcols = 0;
+ int i;
+
+ for (i = 0; i < natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+
+ if (attr->attisdropped)
+ continue;
+
+ if (++outcols > 1)
+ elog(ERROR, "svariable DestReceiver can take only one attribute");
+
+ myState->typid = attr->atttypid;
+ myState->typmod = attr->atttypmod;
+ myState->typlen = attr->attlen;
+ myState->slot_offset = i;
+ }
+
+ myState->rows = 0;
+}
+
+/*
+ * Receive a tuple from the executor and store it in schema variable.
+ */
+static bool
+svariableReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
+{
+ svariableState *myState = (svariableState *) self;
+ Datum value;
+ bool isnull;
+ bool freeval = false;
+
+ /* Make sure the tuple is fully deconstructed */
+ slot_getallattrs(slot);
+
+ value = slot->tts_values[myState->slot_offset];
+ isnull = slot->tts_isnull[myState->slot_offset];
+
+ if (myState->typlen == -1 && !isnull && VARATT_IS_EXTERNAL(DatumGetPointer(value)))
+ {
+ value = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+ DatumGetPointer(value)));
+ freeval = true;
+ }
+
+ SetSchemaVariable(myState->varid, value, isnull, myState->typid, myState->typmod);
+
+ if (freeval)
+ pfree(DatumGetPointer(value));
+
+ return true;
+}
+
+/*
+ * Clean up at end of an executor run
+ */
+static void
+svariableShutdownReceiver(DestReceiver *self)
+{
+ /* Do nothing */
+}
+
+/*
+ * Destroy receiver when done with it
+ */
+static void
+svariableDestroyReceiver(DestReceiver *self)
+{
+ pfree(self);
+}
+
+/*
+ * Initially create a DestReceiver object.
+ */
+DestReceiver *
+CreateVariableDestReceiver(void)
+{
+ svariableState *self = (svariableState *) palloc0(sizeof(svariableState));
+
+ self->pub.receiveSlot = svariableReceiveSlot;
+ self->pub.rStartup = svariableStartupReceiver;
+ self->pub.rShutdown = svariableShutdownReceiver;
+ self->pub.rDestroy = svariableDestroyReceiver;
+ self->pub.mydest = DestVariable;
+
+ /* private fields will be set by SetVariableDestReceiverParams */
+
+ return (DestReceiver *) self;
+}
+
+/*
+ * Set parameters for a VariableDestReceiver
+ */
+void
+SetVariableDestReceiverParams(DestReceiver *self, Oid varid)
+{
+ svariableState *myState = (svariableState *) self;
+
+ Assert(myState->pub.mydest == DestVariable);
+ Assert(OidIsValid(varid));
+
+ myState->varid = varid;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c8220cf65..fcaa2db51a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(nonleafResultRelations);
COPY_NODE_FIELD(rootResultRelations);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks);
@@ -3000,6 +3001,7 @@ _copyQuery(const Query *from)
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_SCALAR_FIELD(hasAggs);
COPY_SCALAR_FIELD(hasWindowFuncs);
COPY_SCALAR_FIELD(hasTargetSRFs);
@@ -3118,6 +3120,18 @@ _copySelectStmt(const SelectStmt *from)
return newnode;
}
+static LetStmt *
+_copyLetStmt(const LetStmt *from)
+{
+ LetStmt *newnode = makeNode(LetStmt);
+
+ COPY_NODE_FIELD(target);
+ COPY_NODE_FIELD(selectStmt);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
static SetOperationStmt *
_copySetOperationStmt(const SetOperationStmt *from)
{
@@ -5166,6 +5180,9 @@ copyObjectImpl(const void *from)
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
+ case T_LetStmt:
+ retval = _copyLetStmt(from);
+ break;
case T_SetOperationStmt:
retval = _copySetOperationStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 378f2facb8..3ec472e19b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -949,6 +949,7 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_SCALAR_FIELD(canSetTag);
COMPARE_NODE_FIELD(utilityStmt);
COMPARE_SCALAR_FIELD(resultRelation);
+ COMPARE_SCALAR_FIELD(resultVariable);
COMPARE_SCALAR_FIELD(hasAggs);
COMPARE_SCALAR_FIELD(hasWindowFuncs);
COMPARE_SCALAR_FIELD(hasTargetSRFs);
@@ -1057,6 +1058,16 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
return true;
}
+static bool
+_equalLetStmt(const LetStmt *a, const LetStmt *b)
+{
+ COMPARE_NODE_FIELD(target);
+ COMPARE_NODE_FIELD(selectStmt);
+
+ return true;
+}
+
+
static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
@@ -3225,6 +3236,9 @@ equal(const void *a, const void *b)
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
break;
+ case T_LetStmt:
+ retval = _equalLetStmt(a, b);
+ break;
case T_SetOperationStmt:
retval = _equalSetOperationStmt(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6269f474d2..46404ff9ac 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -278,6 +278,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(nonleafResultRelations);
WRITE_NODE_FIELD(rootResultRelations);
+ WRITE_OID_FIELD(resultVariable);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks);
@@ -2793,6 +2794,16 @@ _outSelectStmt(StringInfo str, const SelectStmt *node)
WRITE_NODE_FIELD(rarg);
}
+static void
+_outLetStmt(StringInfo str, const LetStmt *node)
+{
+ WRITE_NODE_TYPE("LET");
+
+ WRITE_NODE_FIELD(target);
+ WRITE_NODE_FIELD(selectStmt);
+ WRITE_LOCATION_FIELD(location);
+}
+
static void
_outFuncCall(StringInfo str, const FuncCall *node)
{
@@ -2971,6 +2982,7 @@ _outQuery(StringInfo str, const Query *node)
appendStringInfoString(str, " :utilityStmt <>");
WRITE_INT_FIELD(resultRelation);
+ WRITE_INT_FIELD(resultVariable);
WRITE_BOOL_FIELD(hasAggs);
WRITE_BOOL_FIELD(hasWindowFuncs);
WRITE_BOOL_FIELD(hasTargetSRFs);
@@ -4191,6 +4203,9 @@ outNode(StringInfo str, const void *obj)
case T_SelectStmt:
_outSelectStmt(str, obj);
break;
+ case T_LetStmt:
+ _outLetStmt(str, obj);
+ break;
case T_ColumnDef:
_outColumnDef(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3254524223..4454327549 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -242,6 +242,7 @@ _readQuery(void)
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
+ READ_INT_FIELD(resultVariable);
READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasWindowFuncs);
READ_BOOL_FIELD(hasTargetSRFs);
@@ -1485,6 +1486,7 @@ _readPlannedStmt(void)
READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(nonleafResultRelations);
READ_NODE_FIELD(rootResultRelations);
+ READ_OID_FIELD(resultVariable);
READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index fd06da98b9..01f97f2d86 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -335,7 +335,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
*/
if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
IsUnderPostmaster &&
- parse->commandType == CMD_SELECT &&
+ (parse->commandType == CMD_SELECT ||
+ parse->commandType == CMD_PLAN_UTILITY) &&
!parse->hasModifyingCTE &&
max_parallel_workers_per_gather > 0 &&
!IsParallelWorker() &&
@@ -352,6 +353,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->parallelModeOK = false;
}
+
+
/*
* glob->parallelModeNeeded is normally set to false here and changed to
* true during plan creation if a Gather or Gather Merge plan is actually
@@ -521,6 +524,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->resultRelations = glob->resultRelations;
result->nonleafResultRelations = glob->nonleafResultRelations;
result->rootResultRelations = glob->rootResultRelations;
+ result->resultVariable = parse->resultVariable;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks;
@@ -2167,7 +2171,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
* inheritance_planner, add the ModifyTable node.
*/
- if (parse->commandType != CMD_SELECT && !inheritance_update)
+ if (parse->commandType != CMD_SELECT && parse->commandType != CMD_PLAN_UTILITY && !inheritance_update)
{
List *withCheckOptionLists;
List *returningLists;
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 8603feef2b..2923e3fcc7 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -71,6 +71,7 @@ preprocess_targetlist(PlannerInfo *root)
{
Query *parse = root->parse;
int result_relation = parse->resultRelation;
+ int result_variable = parse->resultVariable;
List *range_table = parse->rtable;
CmdType command_type = parse->commandType;
RangeTblEntry *target_rte = NULL;
@@ -96,6 +97,10 @@ preprocess_targetlist(PlannerInfo *root)
target_relation = heap_open(target_rte->relid, NoLock);
}
+ else if (result_variable)
+ {
+ Assert(command_type == CMD_PLAN_UTILITY);
+ }
else
Assert(command_type == CMD_SELECT);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a04ad6e99e..da570bb23b 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1254,7 +1254,8 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
{
Param *param = (Param *) node;
- if (param->paramkind == PARAM_EXTERN)
+ if (param->paramkind == PARAM_EXTERN ||
+ param->paramkind == PARAM_SCHEMA_VARIABLE)
return false;
if (param->paramkind != PARAM_EXEC ||
@@ -4799,7 +4800,7 @@ substitute_actual_parameters_mutator(Node *node,
{
if (node == NULL)
return NULL;
- if (IsA(node, Param))
+ if (IsA(node, Param) && ((Param *) node)->paramkind != PARAM_SCHEMA_VARIABLE)
{
Param *param = (Param *) node;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8369e3ad62..fc0cf34c7d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1272,7 +1272,7 @@ get_relation_constraints(PlannerInfo *root,
* descriptor, instead of constraint exclusion which is driven by the
* individual partition's partition constraint.
*/
- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
+ if (enable_partition_pruning && root->parse->commandType != CMD_SELECT && root->parse->commandType != CMD_PLAN_UTILITY)
{
List *pcqual = RelationGetPartitionQual(relation);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c601b6d40d..441b298693 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,7 +25,10 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/namespace.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -44,6 +47,8 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -78,6 +83,8 @@ static Query *transformCreateTableAsStmt(ParseState *pstate,
CreateTableAsStmt *stmt);
static Query *transformCallStmt(ParseState *pstate,
CallStmt *stmt);
+static Query *transformLetStmt(ParseState *pstate,
+ LetStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown);
#ifdef RAW_EXPRESSION_COVERAGE_TEST
@@ -267,6 +274,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
+ case T_LetStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
@@ -327,6 +335,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
(CallStmt *) parseTree);
break;
+ case T_LetStmt:
+ result = transformLetStmt(pstate,
+ (LetStmt *) parseTree);
+ break;
+
default:
/*
@@ -367,6 +380,7 @@ analyze_requires_snapshot(RawStmt *parseTree)
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
+ case T_LetStmt:
result = true;
break;
@@ -1567,6 +1581,203 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
return qry;
}
+/*
+ * transformLetStmt -
+ * transform an Let Statement
+ */
+static Query *
+transformLetStmt(ParseState *pstate, LetStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ List *exprList = NIL;
+ List *exprListCoer = NIL;
+ List *indirection = NIL;
+ ListCell *lc;
+ Query *selectQuery;
+ int i = 0;
+
+ Oid varid;
+
+ ParseExprKind sv_expr_kind;
+ char *attrname = NULL;
+ bool not_unique;
+ bool is_rowtype;
+ Oid typid;
+ int32 typmod;
+
+ AclResult aclresult;
+ List *names = NULL;
+ int indirection_start;
+
+ sv_expr_kind = pstate->p_expr_kind;
+ pstate->p_expr_kind = EXPR_KIND_LET;
+
+ /* There can't be any outer WITH to worry about */
+ Assert(pstate->p_ctenamespace == NIL);
+
+ /* Exec this command as utility */
+ qry->commandType = CMD_PLAN_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ names = NamesFromList(stmt->target);
+
+ varid = identify_variable(names, &attrname, ¬_unique);
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("target \"%s\" of LET command is ambiguous",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ if (!OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("schema variable \"%s\" doesn't exists",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ qry->resultVariable = varid;
+
+ get_schema_variable_type_typmod(varid, &typid, &typmod);
+
+ is_rowtype = type_is_rowtype(typid);
+
+ if (attrname && !is_rowtype)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("target variable \"%s\" is not row type",
+ schema_variable_get_name(varid)),
+ parser_errposition(pstate, stmt->location)));
+
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, NameListToString(names));
+
+ selectQuery = transformStmt(pstate, stmt->selectStmt);
+
+ /* The grammar should have produced a SELECT */
+ if (!IsA(selectQuery, Query) ||
+ selectQuery->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in LET ... SELECT");
+
+ /*----------
+ * Generate an expression list for the LET that selects all the
+ * non-resjunk columns from the subquery.
+ *----------
+ */
+ exprList = NIL;
+ foreach(lc, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->resjunk)
+ continue;
+
+ exprList = lappend(exprList, tle->expr);
+ }
+
+ /*
+ * Because doesn't support pattern matching, don't allow multicolumn result
+ */
+ if (list_length(exprList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expression is not scalar value"),
+ parser_errposition(pstate,
+ exprLocation((Node *) exprList))));
+
+ indirection_start = list_length(names) - (attrname ? 1 : 0);
+ indirection = list_copy_tail(stmt->target, indirection_start);
+
+ exprListCoer = NIL;
+ foreach(lc, exprList)
+ {
+ Node *orig_expr = (Node*) lfirst(lc);
+ Oid exprtypid = exprType((Node *) orig_expr);
+ Param *param = makeNode(Param);
+ Expr *expr = NULL;
+
+ param->paramkind = PARAM_SCHEMA_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (indirection != NULL)
+ {
+ bool targetIsArray;
+ char *targetName;
+
+ targetName = attrname != NULL ? attrname : get_schema_variable_name(varid);
+ targetIsArray = OidIsValid(get_element_type(typid));
+
+ expr = (Expr *)
+ transformAssignmentIndirection(pstate,
+ (Node *) param,
+ targetName,
+ targetIsArray,
+ typid,
+ typmod,
+ InvalidOid,
+ list_head(indirection),
+ (Node *) orig_expr,
+ stmt->location);
+ }
+ else
+ expr = (Expr *)
+ coerce_to_target_type(pstate,
+ (Node *) orig_expr,
+ exprtypid,
+ typid, typmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ stmt->location);
+
+ if (expr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable \"%s\" is of type %s,"
+ " but expression is of type %s",
+ schema_variable_get_name(varid),
+ format_type_be(typid),
+ format_type_be(exprtypid)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *) orig_expr))));
+
+ exprListCoer = lappend(exprListCoer, expr);
+ }
+
+ /*
+ * Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
+ */
+ qry->targetList = NIL;
+ foreach(lc, exprListCoer)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ tle = makeTargetEntry(expr,
+ i + 1,
+ FigureColname((Node *)expr),
+ false);
+ qry->targetList = lappend(qry->targetList, tle);
+ }
+
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+ qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+
+ assign_query_collations(pstate, qry);
+
+ pstate->p_expr_kind = sv_expr_kind;
+
+ return qry;
+}
+
+
/*
* transformSetOperationStmt -
* transforms a set-operations tree
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 87f5e95827..25036669c1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -257,8 +257,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSchemaVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -268,7 +268,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DropTransformStmt
DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
- ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
+ LetStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -400,6 +400,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
TriggerTransitions TriggerReferencing
publication_name_list
vacuum_relation_list opt_vacuum_relation_list
+ let_target
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -584,6 +585,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> optSchemaVarDefExpr
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -649,7 +651,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+ LEADING LEAKPROOF LEAST LEFT LET LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -687,8 +689,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -878,6 +880,7 @@ stmt :
| CreatePolicyStmt
| CreatePLangStmt
| CreateSchemaStmt
+ | CreateSchemaVarStmt
| CreateSeqStmt
| CreateStmt
| CreateSubscriptionStmt
@@ -917,6 +920,7 @@ stmt :
| ImportForeignSchemaStmt
| IndexStmt
| InsertStmt
+ | LetStmt
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
@@ -1808,7 +1812,12 @@ DiscardStmt:
n->target = DISCARD_SEQUENCES;
$$ = (Node *) n;
}
-
+ | DISCARD VARIABLES
+ {
+ DiscardStmt *n = makeNode(DiscardStmt);
+ n->target = DISCARD_VARIABLES;
+ $$ = (Node *) n;
+ }
;
@@ -4479,6 +4488,42 @@ create_extension_opt_item:
}
;
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE VARIABLE varname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSchemaVarStmt:
+ CREATE OptTemp VARIABLE qualified_name opt_as Typename optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $4->relpersistence = $2;
+ n->variable = $4;
+ n->typeName = $6;
+ n->defexpr = $7;
+ n->if_not_exists = false;
+ $$ = (Node *) n;
+ }
+ | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $7->relpersistence = $2;
+ n->variable = $7;
+ n->typeName = $9;
+ n->defexpr = $10;
+ n->if_not_exists = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+optSchemaVarDefExpr: DEFAULT b_expr { $$ = $2; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+
+
/*****************************************************************************
*
* ALTER EXTENSION name UPDATE [ TO version ]
@@ -6335,6 +6380,7 @@ drop_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name_list */
@@ -6604,6 +6650,7 @@ comment_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -6742,6 +6789,7 @@ security_label_type_any_name:
| TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -7163,6 +7211,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| ALL TABLES IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7203,6 +7259,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
;
@@ -7363,6 +7427,7 @@ defacl_privilege_target:
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
;
@@ -8959,6 +9024,25 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newname = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
opt_column: COLUMN { $$ = COLUMN; }
@@ -9277,6 +9361,25 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newschema = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
/*****************************************************************************
@@ -9512,6 +9615,14 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
@@ -10693,6 +10804,7 @@ ExplainableStmt:
| CreateMatViewStmt
| RefreshMatViewStmt
| ExecuteStmt /* by default all are $$=$1 */
+ | LetStmt
;
explain_option_list:
@@ -10750,6 +10862,7 @@ PreparableStmt:
| InsertStmt
| UpdateStmt
| DeleteStmt /* by default all are $$=$1 */
+ | LetStmt
;
/*****************************************************************************
@@ -11148,6 +11261,44 @@ opt_hold: /* EMPTY */ { $$ = 0; }
| WITHOUT HOLD { $$ = 0; }
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * LET STATEMENTS
+ *
+ *****************************************************************************/
+LetStmt: LET let_target '=' a_expr
+ {
+ LetStmt *n = makeNode(LetStmt);
+ SelectStmt *select = makeNode(SelectStmt);
+ ResTarget *res = makeNode(ResTarget);
+
+ n->target = $2;
+
+ /* Create target list for implicit query */
+ res->name = NULL;
+ res->indirection = NIL;
+ res->val = (Node *) $4;
+ res->location = @4;
+
+ select->targetList = list_make1(res);
+ n->selectStmt = (Node *) select;
+
+ n->location = @2;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+let_target:
+ ColId opt_indirection
+ {
+ $$ = list_make1(makeString($1));
+ if ($2)
+ $$ = list_concat($$,
+ check_indirection($2, yyscanner));
+ }
+
/*****************************************************************************
*
* QUERY:
@@ -15127,6 +15278,7 @@ unreserved_keyword:
| LARGE_P
| LAST_P
| LEAKPROOF
+ | LET
| LEVEL
| LISTEN
| LOAD
@@ -15275,6 +15427,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 61727e1d71..6823612fba 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -349,6 +349,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
Assert(false); /* can't happen */
break;
case EXPR_KIND_OTHER:
+ case EXPR_KIND_LET:
/*
* Accept aggregate/grouping here; caller must throw error if
@@ -465,6 +466,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
if (isAgg)
err = _("aggregate functions are not allowed in DEFAULT expressions");
@@ -879,6 +881,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("window functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -902,6 +905,8 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_CALL_ARGUMENT:
err = _("window functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("window functions are not allowed in LET statement");
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a9b6..bcdda0fb4a 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -37,6 +38,7 @@
#include "utils/date.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
+#include "utils/typcache.h"
#include "utils/xml.h"
@@ -116,6 +118,9 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
+static Node *makeParamSchemaVariable(ParseState *pstate,
+ Oid varid, Oid typid, int32 typmod,
+ char *attrname, int location);
static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
int location);
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
@@ -512,6 +517,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
+ Oid varid = InvalidOid;
+ char *attrname = NULL;
+ bool not_unique;
+
RangeTblEntry *rte;
int levels_up;
enum
@@ -749,6 +758,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break;
}
+ varid = identify_variable(cref->fields, &attrname, ¬_unique);
+
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("schema variable reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+
/*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
@@ -773,6 +791,71 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
parser_errposition(pstate, cref->location)));
}
+ if (OidIsValid(varid))
+ {
+ Oid typid;
+ int32 typmod;
+
+ get_schema_variable_type_typmod(varid, &typid, &typmod);
+
+ if (node != NULL)
+ {
+ /*
+ * some collision can be solved simply here to reduce errors
+ * based on simply existence of some variables. Often error
+ * can be using alias same like variable name. In this case,
+ * when we found column reference, and we found reference to
+ * possible composite variable, but the variable is not composite,
+ * then we can ignore the variable as simply improper, and we
+ * use column reference only.
+ */
+ if (attrname)
+ {
+ if (type_is_rowtype(typid))
+ {
+ TupleDesc tupdesc;
+ bool found = false;
+ int i;
+
+ /* slow part, I hope it will not be to often */
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ if (namestrcmp(&(TupleDescAttr(tupdesc, i)->attname), attrname) == 0 &&
+ !TupleDescAttr(tupdesc, i)->attisdropped)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ FreeTupleDesc(tupdesc);
+
+ /* there are not composite variable with this field */
+ if (!found)
+ varid = InvalidOid;
+ }
+ else
+ /* there are not composite variable with this name */
+ varid = InvalidOid;
+ }
+
+ /* Raise error if varid is still valid. It should be really amigonuous */
+ if (OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ errdetail("The qualified identifier can be column reference or schema variable reference"),
+ parser_errposition(pstate, cref->location)));
+ }
+
+ if (OidIsValid(varid))
+ node = makeParamSchemaVariable(pstate,
+ varid, typid, typmod,
+ attrname, cref->location);
+ }
+
/*
* Throw error if no translation found.
*/
@@ -807,6 +890,59 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return node;
}
+/*
+ * Generate param variable for reference to schema variable
+ */
+static Node *
+makeParamSchemaVariable(ParseState *pstate, Oid varid, Oid typid, int32 typmod, char *attrname, int location)
+{
+ Param *param;
+
+ param = makeNode(Param);
+
+ param->paramkind = PARAM_SCHEMA_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (attrname != NULL)
+ {
+ TupleDesc tupdesc;
+ int i;
+
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+
+ if (strcmp(attrname, NameStr(att->attname)) == 0 &&
+ !att->attisdropped)
+ {
+ /* Success, so generate a FieldSelect expression */
+ FieldSelect *fselect = makeNode(FieldSelect);
+
+ fselect->arg = (Expr *) param;
+ fselect->fieldnum = i + 1;
+ fselect->resulttype = att->atttypid;
+ fselect->resulttypmod = att->atttypmod;
+ /* save attribute's collation for parse_collate.c */
+ fselect->resultcollid = att->attcollation;
+
+ ReleaseTupleDesc(tupdesc);
+ return (Node *) fselect;
+ }
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("could not identify column \"%s\" in variable", attrname),
+ parser_errposition(pstate, location)));
+ }
+
+ return (Node *) param;
+}
+
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
@@ -1818,6 +1954,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
case EXPR_KIND_VALUES_SINGLE:
+ case EXPR_KIND_LET:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
@@ -1826,6 +1963,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -3460,6 +3598,7 @@ ParseExprKindName(ParseExprKind exprKind)
return "CHECK";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
return "DEFAULT";
case EXPR_KIND_INDEX_EXPRESSION:
return "index expression";
@@ -3475,6 +3614,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "PARTITION BY";
case EXPR_KIND_CALL_ARGUMENT:
return "CALL";
+ case EXPR_KIND_LET:
+ return "LET";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 44257154b8..b2c9900e00 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2347,6 +2347,7 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("set-returning functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -2370,6 +2371,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_CALL_ARGUMENT:
err = _("set-returning functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("set-returning functions are not allowed in CALL arguments");
+ break;
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4932e58022..c60fe011f7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -35,16 +35,6 @@
static void markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
Var *var, int levelsup);
-static Node *transformAssignmentIndirection(ParseState *pstate,
- Node *basenode,
- const char *targetName,
- bool targetIsArray,
- Oid targetTypeId,
- int32 targetTypMod,
- Oid targetCollation,
- ListCell *indirection,
- Node *rhs,
- int location);
static Node *transformAssignmentSubscripts(ParseState *pstate,
Node *basenode,
const char *targetName,
@@ -672,7 +662,7 @@ updateTargetListEntry(ParseState *pstate,
* might want to decorate indirection cells with their own location info,
* in which case the location argument could probably be dropped.)
*/
-static Node *
+Node *
transformAssignmentIndirection(ParseState *pstate,
Node *basenode,
const char *targetName,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 3123ee274d..10737d422d 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3350,7 +3350,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
* get executed. Also, utilities aren't rewritten at all (do we still
* need that check?)
*/
- if (event != CMD_SELECT && event != CMD_UTILITY)
+ if (event != CMD_SELECT && event != CMD_UTILITY && event != CMD_PLAN_UTILITY)
{
int result_relation;
RangeTblEntry *rt_entry;
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 61ef396d8a..6a068af799 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -212,7 +212,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
}
/*
- * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
+ * For SELECT, LET, UPDATE and DELETE, add security quals to enforce the USING
* policies. These security quals control access to existing table rows.
* Restrictive policies are combined together using AND, and permissive
* policies are combined together using OR.
@@ -222,6 +222,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
&restrictive_policies);
if (commandType == CMD_SELECT ||
+ commandType == CMD_PLAN_UTILITY ||
commandType == CMD_UPDATE ||
commandType == CMD_DELETE)
add_security_quals(rt_index,
@@ -423,6 +424,7 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
switch (cmd)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
if (policy->polcmd == ACL_SELECT_CHR)
cmd_matches = true;
break;
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index c95a4d519d..47fb0f38b1 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -37,6 +37,7 @@
#include "executor/functions.h"
#include "executor/tqueue.h"
#include "executor/tstoreReceiver.h"
+#include "executor/svariableReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/portal.h"
@@ -143,6 +144,9 @@ CreateDestReceiver(CommandDest dest)
case DestTupleQueue:
return CreateTupleQueueDestReceiver(NULL);
+
+ case DestVariable:
+ return CreateVariableDestReceiver();
}
/* should never get here */
@@ -178,6 +182,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -222,6 +227,7 @@ NullCommand(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -268,6 +274,7 @@ ReadyForQuery(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b5804f64ad..35199fd0dc 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
@@ -344,7 +345,7 @@ ProcessUtility(PlannedStmt *pstmt,
char *completionTag)
{
Assert(IsA(pstmt, PlannedStmt));
- Assert(pstmt->commandType == CMD_UTILITY);
+ Assert(pstmt->commandType == CMD_UTILITY || pstmt->commandType == CMD_PLAN_UTILITY);
Assert(queryString != NULL); /* required as of 8.4 */
/*
@@ -915,6 +916,14 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
}
+ case T_LetStmt:
+ {
+ doLetStmt(pstmt, params, queryEnv, queryString);
+ if (completionTag)
+ strcpy(completionTag, "LET");
+ }
+ break;
+
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
@@ -1221,6 +1230,10 @@ ProcessUtilitySlow(ParseState *pstate,
}
break;
+ case T_CreateSchemaVarStmt:
+ address = DefineSchemaVariable(pstate, (CreateSchemaVarStmt *) parsetree);
+ break;
+
/*
* ************* object creation / destruction **************
*/
@@ -2055,6 +2068,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_STATISTIC_EXT:
tag = "ALTER STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "ALTER VARIABLE";
+ break;
default:
tag = "???";
break;
@@ -2104,6 +2120,10 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ case T_LetStmt:
+ tag = "LET";
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
@@ -2358,6 +2378,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "DROP VARIABLE";
+ break;
default:
tag = "???";
}
@@ -2639,6 +2662,9 @@ CreateCommandTag(Node *parsetree)
case DISCARD_SEQUENCES:
tag = "DISCARD SEQUENCES";
break;
+ case DISCARD_VARIABLES:
+ tag = "DISCARD VARIABLES";
+ break;
default:
tag = "???";
}
@@ -2844,6 +2870,7 @@ CreateCommandTag(Node *parsetree)
tag = "DELETE";
break;
case CMD_UTILITY:
+ case CMD_PLAN_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
default:
@@ -2915,6 +2942,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_CreateSchemaVarStmt:
+ tag = "CREATE VARIABLE";
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -2961,6 +2992,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ case T_LetStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a45e093de7..952c0d9628 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -315,6 +315,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_READ_CHR:
+ read = ACL_READ;
+ break;
+ case ACL_WRITE_CHR:
+ read = ACL_WRITE;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -808,6 +814,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -903,6 +913,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'T':
objtype = OBJECT_TYPE;
break;
+ case 'V':
+ objtype = OBJECT_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
}
@@ -1627,6 +1640,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CONNECT;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
+ if (pg_strcasecmp(priv_type, "READ") == 0)
+ return ACL_READ;
+ if (pg_strcasecmp(priv_type, "WRITE") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1721,6 +1738,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 03e9a28a63..488cb26d3f 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7362,6 +7362,14 @@ get_parameter(Param *param, deparse_context *context)
return;
}
+ /* translate paramid to original schema variable name */
+ if (param->paramkind == PARAM_SCHEMA_VARIABLE)
+ {
+ appendStringInfo(context->buf, "%s",
+ schema_variable_get_name(param->paramid));
+ return;
+ }
+
/*
* Not PARAM_EXEC, or couldn't find referent: just print $N.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bba595ad1d..858a6dd4be 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1691,6 +1691,18 @@ get_relname_relid(const char *relname, Oid relnamespace)
ObjectIdGetDatum(relnamespace));
}
+/*
+ * get_varname_varid
+ * Given name and namespace of variable, look up the OID.
+ */
+Oid
+get_varname_varid(const char *varname, Oid varnamespace)
+{
+ return GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(varnamespace));
+}
+
#ifdef NOT_USED
/*
* get_relnatts
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b381782a3..35dc32f649 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -73,6 +73,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "utils/rel.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
@@ -968,6 +969,28 @@ static const struct cachedesc cacheinfo[] = {
0
},
2
+ },
+ {VariableRelationId, /* VARIABLENAMENSP */
+ VariableNameNspIndexId,
+ 2,
+ {
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ 0,
+ 0
+ },
+ 8
+ },
+ {VariableRelationId, /* VARIABLEOID */
+ VariableObjectIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
}
};
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 0d147cb08d..6d97931d85 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -296,6 +296,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading variables\n");
+ getVariables(fout);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 83c976eaf7..c9bc91ca68 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3471,6 +3471,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(type, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(type, "STATISTICS") == 0 ||
+ strcmp(type, "VARIABLE") == 0 ||
/* non-schema-specified objects */
strcmp(type, "DATABASE") == 0 ||
strcmp(type, "PROCEDURAL LANGUAGE") == 0 ||
@@ -3670,7 +3671,8 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
strcmp(te->desc, "SERVER") == 0 ||
strcmp(te->desc, "STATISTICS") == 0 ||
strcmp(te->desc, "PUBLICATION") == 0 ||
- strcmp(te->desc, "SUBSCRIPTION") == 0)
+ strcmp(te->desc, "SUBSCRIPTION") == 0 ||
+ strcmp(te->desc, "VARIABLE") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 9baf7b2fde..f825a00c9d 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -260,6 +260,7 @@ static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
+static void dumpVariable(Archive *fout, VariableInfo *varinfo);
static void dumpDatabase(Archive *AH);
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
const char *dbname, Oid dboid);
@@ -4221,6 +4222,208 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+/*
+ * getVariables
+ * get information about variables
+ */
+void
+getVariables(Archive *fout)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer init_acl_subquery = createPQExpBuffer();
+ PQExpBuffer init_racl_subquery = createPQExpBuffer();
+ PGresult *res;
+ VariableInfo *varinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_varname;
+ int i_varnamespace;
+ int i_vartype;
+ int i_vartypname;
+ int i_vardefexpr;
+ int i_rolname;
+ int i_varacl;
+ int i_rvaracl;
+ int i_initvaracl;
+ int i_initrvaracl;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion <= 110000)
+ return;
+
+ acl_subquery = createPQExpBuffer();
+ racl_subquery = createPQExpBuffer();
+ init_acl_subquery = createPQExpBuffer();
+ init_racl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
+ init_racl_subquery, "v.varacl", "v.varowner", "'V'",
+ dopt->binary_upgrade);
+
+ query = createPQExpBuffer();
+
+ resetPQExpBuffer(query);
+
+ /* Get the variables in current database. */
+ appendPQExpBuffer(query,
+ "SELECT v.tableoid, v.oid, v.varname, "
+ "v.varnamespace,"
+ "(%s varowner) AS rolname, "
+ "%s as varacl, "
+ "%s as rvaracl, "
+ "%s as initvaracl, "
+ "%s as initrvaracl, "
+ "v.vartype, "
+ "pg_catalog.format_type(v.vartype, v.vartypmod) as vartypname, "
+ "pg_catalog.pg_get_expr(v.vardefexpr,0) as vardefexpr "
+ "FROM pg_variable v "
+ "LEFT JOIN pg_init_privs pip "
+ "ON (v.oid = pip.objoid "
+ "AND pip.classoid = 'pg_variable'::regclass "
+ "AND pip.objsubid = 0)",
+ username_subquery,
+ acl_subquery->data,
+ racl_subquery->data,
+ init_acl_subquery->data,
+ init_racl_subquery->data);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(init_acl_subquery);
+ destroyPQExpBuffer(init_racl_subquery);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_varname = PQfnumber(res, "varname");
+ i_varnamespace = PQfnumber(res, "varnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_vartype = PQfnumber(res, "vartype");
+ i_vartypname = PQfnumber(res, "vartypname");
+ i_vardefexpr = PQfnumber(res, "vardefexpr");
+ i_varacl = PQfnumber(res, "varacl");
+ i_rvaracl = PQfnumber(res, "rvaracl");
+ i_initvaracl = PQfnumber(res, "initvaracl");
+ i_initrvaracl = PQfnumber(res, "initrvaracl");
+
+ varinfo = pg_malloc(ntups * sizeof(VariableInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ TypeInfo *vtype;
+
+ varinfo[i].dobj.objType = DO_VARIABLE;
+ varinfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ varinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&varinfo[i].dobj);
+ varinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_varname));
+ varinfo[i].dobj.namespace =
+ findNamespace(fout,
+ atooid(PQgetvalue(res, i, i_varnamespace)));
+
+ varinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ varinfo[i].vartype = atooid(PQgetvalue(res, i, i_vartype));
+ varinfo[i].vartypname = pg_strdup(PQgetvalue(res, i, i_vartypname));
+
+ varinfo[i].varacl = pg_strdup(PQgetvalue(res, i, i_varacl));
+ varinfo[i].rvaracl = pg_strdup(PQgetvalue(res, i, i_rvaracl));
+ varinfo[i].initvaracl = pg_strdup(PQgetvalue(res, i, i_initvaracl));
+ varinfo[i].initrvaracl = pg_strdup(PQgetvalue(res, i, i_initrvaracl));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ /* Do not try to dump ACL if no ACL exists. */
+ if (PQgetisnull(res, i, i_varacl) && PQgetisnull(res, i, i_rvaracl) &&
+ PQgetisnull(res, i, i_initvaracl) &&
+ PQgetisnull(res, i, i_initrvaracl))
+ varinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+
+ if (PQgetisnull(res, i, i_vardefexpr))
+ varinfo[i].vardefexpr = NULL;
+ else
+ varinfo[i].vardefexpr = pg_strdup(PQgetvalue(res, i, i_vardefexpr));
+
+ if (strlen(varinfo[i].rolname) == 0)
+ write_msg(NULL, "WARNING: owner of variable \"%s\" appears to be invalid\n",
+ varinfo[i].dobj.name);
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ vtype = findTypeByOid(varinfo[i].vartype);
+ addObjectDependency(&varinfo[i].dobj, vtype->dobj.dumpId);
+ }
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpVariable
+ * dump the definition of the given variables
+ */
+static void
+dumpVariable(Archive *fout, VariableInfo *varinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ const char *varname;
+ const char *vartypname;
+ const char *vardefexpr;
+
+ /* Skip if not to be dumped */
+ if (!varinfo->dobj.dump || dopt->dataOnly)
+ return;
+
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ varname = fmtQualifiedDumpable(varinfo);
+ vartypname = varinfo->vartypname;
+ vardefexpr = varinfo->vardefexpr;
+
+ appendPQExpBuffer(delq, "DROP VARIABLE %s;\n",
+ varname);
+
+ appendPQExpBuffer(query, "CREATE VARIABLE %s AS %s",
+ varname, vartypname);
+
+ if (vardefexpr)
+ appendPQExpBuffer(query, " DEFAULT %s",
+ vardefexpr);
+
+ appendPQExpBuffer(query, ";\n");
+
+ ArchiveEntry(fout, varinfo->dobj.catId, varinfo->dobj.dumpId,
+ varinfo->dobj.name,
+ NULL,
+ NULL,
+ varinfo->rolname, false,
+ "VARIABLE", SECTION_PRE_DATA,
+ query->data, delq->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ if (varinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, "VARIABLE", varname,
+ NULL, varinfo->rolname,
+ varinfo->dobj.catId, 0, varinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -9849,6 +10052,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_VARIABLE:
+ dumpVariable(fout, (VariableInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -17935,6 +18141,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_OPFAMILY:
case DO_COLLATION:
case DO_CONVERSION:
+ case DO_VARIABLE:
case DO_TABLE:
case DO_ATTRDEF:
case DO_PROCLANG:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 1448005f30..0d49bb7ed7 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -84,7 +84,8 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_VARIABLE
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -625,6 +626,22 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/*
+ * The VariableInfo struct is used to represent schema variables
+ */
+typedef struct _VariableInfo
+{
+ DumpableObject dobj;
+ Oid vartype;
+ char *vartypname;
+ char *rolname; /* name of owner, or empty string */
+ char *vardefexpr;
+ char *varacl;
+ char *rvaracl;
+ char *initvaracl;
+ char *initrvaracl;
+} VariableInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -725,5 +742,6 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern void getVariables(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index ec751a7c23..2a67766ed4 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2601,6 +2601,38 @@ my %tests = (
},
},
+ 'CREATE VARIABLE test_variable' => {
+ all_runs => 1,
+ catch_all => 'CREATE ... commands',
+ create_order => 61,
+ create_sql => 'CREATE VARIABLE dump_test.variable AS integer DEFAULT 0;',
+ regexp => qr/^
+ \QCREATE VARIABLE dump_test.variable AS integer DEFAULT 0;\E/xm,
+ like => {
+ binary_upgrade => 1,
+ clean => 1,
+ clean_if_exists => 1,
+ createdb => 1,
+ defaults => 1,
+ exclude_test_table => 1,
+ exclude_test_table_data => 1,
+ no_blobs => 1,
+ no_privs => 1,
+ no_owner => 1,
+ only_dump_test_schema => 1,
+ pg_dumpall_dbprivs => 1,
+ schema_only => 1,
+ section_pre_data => 1,
+ test_schema_plus_blobs => 1,
+ with_oids => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_test_table => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1,
+ role => 1,
+ section_post_data => 1, }, },
+
'CREATE VIEW test_view' => {
create_order => 61,
create_sql => 'CREATE VIEW dump_test.test_view
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..73a752fd7e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -853,6 +853,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
break;
}
break;
+ case 'V': /* Variables */
+ success = listVariables(pattern, show_verbose);
+ break;
case 'x': /* Extensions */
if (show_verbose)
success = listExtensionContents(pattern);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 80d8338b96..d645bba7af 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4178,6 +4178,80 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
return true;
}
+/*
+ * \dV
+ *
+ * listVariables()
+ */
+bool
+listVariables(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false, false, false, false, false};
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname as \"%s\",\n"
+ " v.varname as \"%s\",\n"
+ " pg_catalog.format_type(v.vartype, v.vartypmod) as \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(v.varowner) as \"%s\",\n"
+ " pg_catalog.pg_get_expr(v.vardefexpr, 0) as \"%s\"",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Type"),
+ gettext_noop("Owner"),
+ gettext_noop("Default"));
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_variable v"
+ "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = v.varnamespace");
+
+ appendPQExpBufferStr(&buf, "\nWHERE true\n");
+ if (!pattern)
+ appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
+ " AND n.nspname <> 'information_schema'\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "v.varname", NULL,
+ "pg_catalog.pg_variable_is_visible(v.oid)");
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ /*
+ * Most functions in this file are content to print an empty table when
+ * there are no matching objects. We intentionally deviate from that
+ * here, but only in !quiet mode, for historical reasons.
+ */
+ if (PQntuples(res) == 0 && !pset.quiet)
+ {
+ if (pattern)
+ psql_error("Did not find any schema variable named \"%s\".\n",
+ pattern);
+ else
+ psql_error("Did not find any schema variables.\n");
+ }
+ else
+ {
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of variables");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+ }
+
+ PQclear(res);
+ return true;
+}
/*
* \dFp
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..ecc4e3a531 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
/* \dt, \di, \ds, \dS, etc. */
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
+/* \dV */
+extern bool listVariables(const char *pattern, bool varbose);
+
/* \dD */
extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 316030d358..adcc36cb6e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
@@ -257,6 +257,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[S+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dV [PATTERN] list variables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index bb696f8ee9..a7583810e8 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -805,6 +805,22 @@ static const SchemaQuery Query_for_list_of_statistics = {
NULL
};
+static const SchemaQuery Query_for_list_of_variables = {
+ /* min_server_version */
+ 0,
+ /* catname */
+ "pg_catalog.pg_variable v",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_variable_is_visible(v.oid)",
+ /* namespace */
+ "v.varnamespace",
+ /* result */
+ "pg_catalog.quote_ident(v.varname)",
+ /* qualresult */
+ NULL
+};
/*
* Queries to get lists of names of various kinds of things, possibly
@@ -1249,6 +1265,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
{"USER MAPPING FOR", NULL, NULL, NULL},
+ {"VARIABLE", NULL, NULL, &Query_for_list_of_variables},
{"VIEW", NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
@@ -1604,7 +1621,7 @@ psql_completion(const char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
- "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LET", "LISTEN", "LOAD", "LOCK",
"MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
"RESET", "REVOKE", "ROLLBACK",
@@ -1621,9 +1638,9 @@ psql_completion(const char *text, int start, int end)
"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
- "\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+ "\\dm", "\\dn", "\\do", "\\dO", "\\dp"
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+ "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", "\\dV",
"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
"\\endif", "\\errverbose", "\\ev",
"\\f",
@@ -1988,6 +2005,9 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
else if (Matches4("ALTER", "SYSTEM", "SET", MatchAny))
COMPLETE_WITH_CONST("TO");
+ /* ALTER VARIABLE <name> */
+ else if (Matches3("ALTER", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
/* ALTER VIEW <name> */
else if (Matches3("ALTER", "VIEW", MatchAny))
COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
@@ -2837,6 +2857,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
COMPLETE_WITH_LIST2("GROUP", "ROLE");
+/* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
+ /* Complete CREATE VARIABLE <name> with AS */
+ else if (TailMatches3("CREATE", "VARIABLE", MatchAny))
+ COMPLETE_WITH_CONST("AS");
+ /* Complete CREATE VARIABLE <name> with AS types*/
+ else if (TailMatches4("CREATE", "VARIABLE", MatchAny, "AS"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE VIEW <name> with AS */
else if (TailMatches3("CREATE", "VIEW", MatchAny))
@@ -2890,7 +2918,7 @@ psql_completion(const char *text, int start, int end)
/* DISCARD */
else if (Matches1("DISCARD"))
- COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP");
+ COMPLETE_WITH_LIST5("ALL", "PLANS", "SEQUENCES", "TEMP", "VARIABLES");
/* DO */
else if (Matches1("DO"))
@@ -2992,6 +3020,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP VARIABLE */
+ else if (Matches2("DROP", "VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ else if (Matches3("DROP", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3002,14 +3036,14 @@ psql_completion(const char *text, int start, int end)
* Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "ANALYZE", "VERBOSE");
+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "ANALYZE", "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "ANALYZE"))
- COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "VERBOSE");
+ COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "VERBOSE") ||
Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
- COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
+ COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "LET");
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
@@ -3118,6 +3152,7 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'ALL ROUTINES IN SCHEMA'"
" UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
" UNION SELECT 'ALL TABLES IN SCHEMA'"
+ " UNION SELECT 'ALL VARIABLES IN SCHEMA'"
" UNION SELECT 'DATABASE'"
" UNION SELECT 'DOMAIN'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
@@ -3131,14 +3166,16 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'SEQUENCE'"
" UNION SELECT 'TABLE'"
" UNION SELECT 'TABLESPACE'"
- " UNION SELECT 'TYPE'");
+ " UNION SELECT 'TYPE'"
+ " UNION SELECT 'VARIABLE'");
}
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
- COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA",
+ COMPLETE_WITH_LIST6("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
- "TABLES IN SCHEMA");
+ "TABLES IN SCHEMA",
+ "VARIABLES IN SCHEMA");
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
@@ -3172,6 +3209,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches1("TYPE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+ else if (TailMatches1("VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
COMPLETE_WITH_CONST("TO");
else
@@ -3324,7 +3363,7 @@ psql_completion(const char *text, int start, int end)
/* PREPARE xx AS */
else if (Matches3("PREPARE", MatchAny, "AS"))
- COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
+ COMPLETE_WITH_LIST5("SELECT", "UPDATE", "INSERT", "DELETE FROM", "LET");
/*
* PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -3547,6 +3586,14 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
COMPLETE_WITH_CONST("=");
+/* LET --- can be inside EXPLAIN, PREPARE etc */
+ /* If prev. word is LET suggest a list of variables */
+ else if (TailMatches1("LET"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ /* Complete LET <variable> with "=" */
+ else if (TailMatches2("LET", MatchAny))
+ COMPLETE_WITH_CONST("=");
+
/* USER MAPPING */
else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 46c271a46c..3e38a05e55 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -180,7 +180,8 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_VARIABLE /* pg_variable */
} ObjectClass;
#define LAST_OCLASS OCLASS_TRANSFORM
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 24915824ca..dae80c20a8 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -360,4 +360,10 @@ DECLARE_UNIQUE_INDEX(pg_subscription_subname_index, 6115, on pg_subscription usi
DECLARE_UNIQUE_INDEX(pg_subscription_rel_srrelid_srsubid_index, 6117, on pg_subscription_rel using btree(srrelid oid_ops, srsubid oid_ops));
#define SubscriptionRelSrrelidSrsubidIndexId 6117
+DECLARE_UNIQUE_INDEX(pg_variable_oid_index, 4288, on pg_variable using btree(oid oid_ops));
+#define VariableObjectIndexId 4288
+
+DECLARE_UNIQUE_INDEX(pg_variable_varname_nsp_index, 4289, on pg_variable using btree(varname name_ops, varnamespace oid_ops));
+#define VariableNameNspIndexId 4289
+
#endif /* INDEXING_H */
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 7991de5e21..75068d7e92 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -75,10 +75,13 @@ extern Oid RangeVarGetAndCheckCreationNamespace(RangeVar *newRelation,
extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
+extern bool VariableIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
+extern bool VariableIsVisible(Oid varid);
+
extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
bool expand_variadic,
@@ -145,6 +148,10 @@ extern void SetTempNamespaceState(Oid tempNamespaceId,
Oid tempToastNamespaceId);
extern void ResetTempTableNamespace(void);
+extern List *NamesFromList(List *names);
+extern Oid lookup_variable(const char *nspname, const char *varname, bool missing_ok);
+extern Oid identify_variable(List *names, char **attrname, bool *not_uniq);
+
extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
extern OverrideSearchPath *CopyOverrideSearchPath(OverrideSearchPath *path);
extern bool OverrideSearchPathMatchesCurrent(OverrideSearchPath *path);
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index d0410f5586..56deef1a45 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -57,6 +57,7 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a14651010f..61cbe65805 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5961,6 +5961,9 @@
proname => 'pg_collation_is_visible', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid',
prosrc => 'pg_collation_is_visible' },
+{ oid => '4187', descr => 'is schema variable visible in search path?',
+ proname => 'pg_variable_is_visible', procost => '10', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid', prosrc => 'pg_variable_is_visible' },
{ oid => '2854', descr => 'get OID of current session\'s temp schema, if any',
proname => 'pg_my_temp_schema', provolatile => 's', proparallel => 'r',
diff --git a/src/include/catalog/pg_variable.h b/src/include/catalog/pg_variable.h
new file mode 100644
index 0000000000..34f4c34202
--- /dev/null
+++ b/src/include/catalog/pg_variable.h
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.h
+ * definition of schema variables system catalog (pg_variables)
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_variable.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VARIABLE_H
+#define PG_VARIABLE_H
+
+#include "catalog/genbki.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable_d.h"
+#include "utils/acl.h"
+
+/* ----------------
+ * pg_variable definition. cpp turns this into
+ * typedef struct FormData_pg_variable
+ * ----------------
+ */
+CATALOG(pg_variable,4287,VariableRelationId)
+{
+ NameData varname; /* variable name */
+ Oid varnamespace; /* OID of namespace containing variable class */
+ Oid vartype; /* OID of entry in pg_type for variable's type */
+ int32 vartypmod; /* typmode for variable's type */
+ Oid varowner; /* class owner */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+
+ /* list of expression trees for variable default (NULL if none) */
+ pg_node_tree vardefexpr BKI_DEFAULT(_null_);
+
+ aclitem varacl[1] BKI_DEFAULT(_null_); /* access permissions */
+
+#endif
+} FormData_pg_variable;
+
+/* ----------------
+ * Form_pg_variable corresponds to a pointer to a tuple with
+ * the format of pg_variable relation.
+ * ----------------
+ */
+typedef FormData_pg_variable *Form_pg_variable;
+
+typedef struct Variable
+{
+ Oid oid;
+ char *name;
+ Oid namespace;
+ Oid typid;
+ int32 typmod;
+ Oid owner;
+ Node *defexpr;
+ Acl *acl;
+} Variable;
+
+/* returns fields from pg_variable table */
+extern char *get_schema_variable_name(Oid varid);
+extern void get_schema_variable_type_typmod(Oid varid, Oid *typid, int32 *typmod);
+
+/* returns name of variable based on current search path */
+extern char *schema_variable_get_name(Oid varid);
+
+extern Variable *GetVariable(Oid varid, bool missing_ok);
+extern ObjectAddress VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Node *varDefexpr,
+ bool if_not_exists);
+
+
+#endif /* PG_VARIABLE_H */
diff --git a/src/include/commands/schemavariable.h b/src/include/commands/schemavariable.h
new file mode 100644
index 0000000000..4ea1dc1209
--- /dev/null
+++ b/src/include/commands/schemavariable.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavariable.h
+ * prototypes for schemavariable.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/schemavariable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SCHEMAVARIABLE_H
+#define SCHEMAVARIABLE_H
+
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable.h"
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "utils/queryenvironment.h"
+
+extern char *VariableGetName(Variable *var);
+
+extern void ResetSchemaVariableCache(void);
+
+extern void RemoveVariableById(Oid varid);
+extern ObjectAddress DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt);
+
+extern Datum GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid);
+extern Datum GetSchemaVariableCopy(Oid varid, bool *isNull, Oid expected_typid);
+
+extern void SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod);
+
+extern void doLetStmt(PlannedStmt *pstmt, ParamListInfo params, QueryEnvironment *queryEnv, const char *queryString);
+
+#endif
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77616..9c03580541 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -344,13 +345,22 @@ typedef struct ExprEvalStep
TupleDesc argdesc;
} nulltest_row;
- /* for EEOP_PARAM_EXEC/EXTERN */
+ /* for EEOP_PARAM_EXEC/EXTERN/VARIABLE */
struct
{
- int paramid; /* numeric ID for parameter */
- Oid paramtype; /* OID of parameter's datatype */
+ int paramid; /* numeric ID for parameter */
+ Oid paramtype; /* OID of parameter's datatype */
} param;
+ /* for EEOP_PARAM_VARIABLE */
+ struct
+ {
+ int paramid; /* numeric ID for parameter */
+ Oid varoid; /* OID of assigned variable */
+ Oid paramtype; /* OID of parameter's datatype */
+ } vparam;
+
+
/* for EEOP_PARAM_CALLBACK */
struct
{
diff --git a/src/include/executor/svariableReceiver.h b/src/include/executor/svariableReceiver.h
new file mode 100644
index 0000000000..8c8117701f
--- /dev/null
+++ b/src/include/executor/svariableReceiver.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.h
+ * prototypes for svariableReceiver.c
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/svariableReceiver.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SVARIABLE_RECEIVER_H
+#define SVARIABLE_RECEIVER_H
+
+#include "tcop/dest.h"
+
+
+extern DestReceiver *CreateVariableDestReceiver(void);
+
+extern void SetVariableDestReceiverParams(DestReceiver *self, Oid varid);
+
+#endif /* SVARIABLE_RECEIVER_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 018f50bbb7..33cc8be55a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -100,6 +100,8 @@ typedef struct ExprState
int steps_len; /* number of steps currently */
int steps_alloc; /* allocated length of steps array */
+ int nvariables; /* number of used variables */
+
struct PlanState *parent; /* parent PlanState node, if any */
ParamListInfo ext_params; /* for compiling PARAM_EXTERN nodes */
@@ -472,6 +474,7 @@ typedef struct ResultRelInfo
typedef struct EState
{
NodeTag type;
+ bool es_shared; /* plpgsql uses share estate */
/* Basic state for all query types: */
ScanDirection es_direction; /* current scan direction */
@@ -564,6 +567,14 @@ typedef struct EState
/* The per-query shared memory area to use for parallel execution. */
struct dsa_area *es_query_dsa;
+ int es_result_variable; /* Oid of target variable */
+
+ /* query schema variable cache */
+ int es_nvariables;
+ bool *es_varnulls;
+ Oid *es_vartypes;
+ Datum *es_varvalues;
+
/*
* JIT information. es_jit_flags indicates whether JIT should be performed
* and with which options. es_jit is created on-demand when JITing is
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 697d3d7a5f..dd7fd8ed42 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -348,6 +348,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSchemaVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
@@ -419,6 +420,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
+ T_LetStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -663,6 +665,7 @@ typedef enum CmdType
CMD_DELETE,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
+ CMD_PLAN_UTILITY, /* only let stmt now, requires planning */
CMD_NOTHING /* dummy command for instead nothing rules
* with qual */
} CmdType;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07ab1a3dde..2d4a3cb1b6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -84,7 +84,9 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
-#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_READ (1<<12) /* for variables */
+#define ACL_WRITE (1<<13) /* for variables */
+#define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -121,6 +123,7 @@ typedef struct Query
int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */
+ int resultVariable; /* Oid of target variable or 0 */
bool hasAggs; /* has aggregates in tlist or havingQual */
bool hasWindowFuncs; /* has window functions in tlist */
@@ -1505,6 +1508,18 @@ typedef struct UpdateStmt
WithClause *withClause; /* WITH clause */
} UpdateStmt;
+/* ----------------------
+ * Let Statement
+ * ----------------------
+ */
+typedef struct LetStmt
+{
+ NodeTag type;
+ List *target; /* target variable */
+ Node *selectStmt; /* source expression */
+ int location;
+} LetStmt;
+
/* ----------------------
* Select Statement
*
@@ -1682,6 +1697,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -2497,6 +2513,19 @@ typedef struct AlterSeqStmt
bool missing_ok; /* skip error if a role is missing? */
} AlterSeqStmt;
+/* ----------------------
+ * {Create|Alter} VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSchemaVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the type of variable */
+ Node *defexpr; /* default expression */
+ bool if_not_exists; /* do nothing if it already exists */
+} CreateSchemaVarStmt;
+
/* ----------------------
* Create {Aggregate|Operator|Type} Statement
* ----------------------
@@ -3238,7 +3267,8 @@ typedef enum DiscardMode
DISCARD_ALL,
DISCARD_PLANS,
DISCARD_SEQUENCES,
- DISCARD_TEMP
+ DISCARD_TEMP,
+ DISCARD_VARIABLES
} DiscardMode;
typedef struct DiscardStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd03a..2588f1455f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -43,7 +43,7 @@ typedef struct PlannedStmt
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete|utility */
+ CmdType commandType; /* select|let|insert|update|delete|utility */
uint64 queryId; /* query identifier (copied from Query) */
@@ -81,6 +81,9 @@ typedef struct PlannedStmt
*/
List *rootResultRelations;
+ /* Oid of target variable for LET command */
+ Oid resultVariable;
+
List *subplans; /* Plan trees for SubPlan expressions; note
* that some could be NULL */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..b366471940 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -229,13 +229,17 @@ typedef struct Const
* of the `paramid' field contain the SubLink's subLinkId, and
* the low-order 16 bits contain the column number. (This type
* of Param is also converted to PARAM_EXEC during planning.)
+ *
+ * PARAM_SCHEMA_VARIABLE: The parameter is a access to schema variable
+ * paramid holds varid.
*/
typedef enum ParamKind
{
PARAM_EXTERN,
PARAM_EXEC,
PARAM_SUBLINK,
- PARAM_MULTIEXPR
+ PARAM_MULTIEXPR,
+ PARAM_SCHEMA_VARIABLE
} ParamKind;
typedef struct Param
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db40147b..d3ed3f4d0f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -231,6 +231,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("let", LET, UNRESERVED_KEYWORD)
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -434,6 +435,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 0230543810..f7c2e67f33 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -69,7 +69,9 @@ typedef enum ParseExprKind
EXPR_KIND_TRIGGER_WHEN, /* WHEN condition in CREATE TRIGGER */
EXPR_KIND_POLICY, /* USING or WITH CHECK expr in policy */
EXPR_KIND_PARTITION_EXPRESSION, /* PARTITION BY expression */
- EXPR_KIND_CALL_ARGUMENT /* procedure argument in CALL */
+ EXPR_KIND_CALL_ARGUMENT, /* procedure argument in CALL */
+ EXPR_KIND_VARIABLE_DEFAULT, /* default value for schema variable */
+ EXPR_KIND_LET /* LET assignment (should be same like UPDATE) */
} ParseExprKind;
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index ec6e0c102f..1ee199ed8f 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -32,6 +32,16 @@ extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
int attrno,
List *indirection,
int location);
+extern Node *transformAssignmentIndirection(ParseState *pstate,
+ Node *basenode,
+ const char *targetName,
+ bool targetIsArray,
+ Oid targetTypeId,
+ int32 targetTypMod,
+ Oid targetCollation,
+ ListCell *indirection,
+ Node *rhs,
+ int location);
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
char *colname, int attrno,
List *indirection,
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 82f0f2e741..c49b653555 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -96,7 +96,8 @@ typedef enum
DestCopyOut, /* results sent to COPY TO code */
DestSQLFunction, /* results sent to SQL-language func mgr */
DestTransientRel, /* results sent to transient relation */
- DestTupleQueue /* results sent to tuple queue */
+ DestTupleQueue, /* results sent to tuple queue */
+ DestVariable /* results sents to schema variable */
} CommandDest;
/* ----------------
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f4d4be8d0d..c624d8dd0b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -147,9 +147,11 @@ typedef ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
+#define ACL_READ_CHR 'S' /* 'R' is occupated by old RULE priv */
+#define ACL_WRITE_CHR 'W'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcSW"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -166,6 +168,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_READ|ACL_WRITE)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -253,6 +256,8 @@ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_variable_aclmask(Oid var_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode);
@@ -269,6 +274,7 @@ extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
+extern AclResult pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, ObjectType objtype,
const char *objectname);
@@ -305,6 +311,7 @@ extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool pg_publication_ownercheck(Oid pub_oid, Oid roleid);
extern bool pg_subscription_ownercheck(Oid sub_oid, Oid roleid);
extern bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid);
+extern bool pg_variable_ownercheck(Oid stat_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index e55ea4035b..cb3f4aaca9 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -122,6 +122,7 @@ extern bool get_func_leakproof(Oid funcid);
extern float4 get_func_cost(Oid funcid);
extern float4 get_func_rows(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
+extern Oid get_varname_varid(const char *varname, Oid varnamespace);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4f333586ee..453699be3c 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -107,9 +107,11 @@ enum SysCacheIdentifier
TYPENAMENSP,
TYPEOID,
USERMAPPINGOID,
- USERMAPPINGUSERSERVER
+ USERMAPPINGUSERSERVER,
+ VARIABLENAMENSP,
+ VARIABLEOID
-#define SysCacheSize (USERMAPPINGUSERSERVER + 1)
+#define SysCacheSize (VARIABLEOID + 1)
};
extern void InitCatalogCache(void);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 380d1de8f4..ac71dd7d7a 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8049,6 +8049,7 @@ plpgsql_create_econtext(PLpgSQL_execstate *estate)
{
oldcontext = MemoryContextSwitchTo(TopTransactionContext);
shared_simple_eval_estate = CreateExecutorState();
+ shared_simple_eval_estate->es_shared = true;
MemoryContextSwitchTo(oldcontext);
}
estate->simple_eval_estate = shared_simple_eval_estate;
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 7d3647a12d..7f183d4f1b 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -332,6 +332,7 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
/* Create a private EState for simple-expression execution */
simple_eval_estate = CreateExecutorState();
+ simple_eval_estate->es_shared = true;
/* And run the function */
PG_TRY();
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b500..48286f8e1a 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -105,5 +105,7 @@ ORDER BY 1, 2;
pg_index | indpred | pg_node_tree
pg_largeobject | data | bytea
pg_largeobject_metadata | lomacl | aclitem[]
-(11 rows)
+ pg_variable | varacl | aclitem[]
+ pg_variable | vardefexpr | pg_node_tree
+(13 rows)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 0aa5357917..848b041a4b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -163,6 +163,7 @@ pg_ts_parser|t
pg_ts_template|t
pg_type|t
pg_user_mapping|t
+pg_variable|t
point_tbl|t
polygon_tbl|t
quad_box_tbl|t
diff --git a/src/test/regress/expected/schema_variables.out b/src/test/regress/expected/schema_variables.out
new file mode 100644
index 0000000000..84fe30a2c0
--- /dev/null
+++ b/src/test/regress/expected/schema_variables.out
@@ -0,0 +1,351 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+DROP VARIABLE var1, var2;
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT var1;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT READ ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT var1;
+ var1
+------
+
+(1 row)
+
+SET ROLE TO DEFAULT;
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to work
+LET var1 = 333;
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var1;
+ERROR: permission denied for schema variable var1
+-- should to work;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: ((g.v)::numeric = var1)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var1;
+SELECT * FROM schema_var_view;
+ var1
+------
+ 333
+(1 row)
+
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ var1
+------
+
+(1 row)
+
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+----------------------------
+ Result
+ Output: 3.14159265358979
+(2 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+SELECT inc(1);
+ inc
+-----
+ 1
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 2
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 3
+(1 row)
+
+SELECT inc(1) FROM generate_series(1,10);
+ inc
+-----
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+(10 rows)
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+-- composite variables
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+\d v1
+\d v2
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+SELECT v1;
+ v1
+------------
+ (1,2,3.14)
+(1 row)
+
+SELECT v2;
+ v2
+---------------
+ (10,20,31.40)
+(1 row)
+
+SELECT (v1).*;
+ x | y | z
+---+---+------
+ 1 | 2 | 3.14
+(1 row)
+
+SELECT (v2).*;
+ x | y | z
+----+----+-------
+ 10 | 20 | 31.40
+(1 row)
+
+SELECT v1.x + v1.z;
+ ?column?
+----------
+ 4.14
+(1 row)
+
+SELECT v2.x + v2.z;
+ ?column?
+----------
+ 41.40
+(1 row)
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+SELECT v2.x;
+ERROR: permission denied for schema variable v2
+SET ROLE TO DEFAULT;
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+DROP ROLE var_test_role;
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+ relname
+----------
+ pg_class
+(1 row)
+
+-- should to fail
+SELECT varx.xxx;
+ERROR: type text is not composite
+-- variables can be updated under RO transaction
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+SELECT varx;
+ varx
+-------
+ hello
+(1 row)
+
+DROP VARIABLE varx;
+CREATE TYPE t1 AS (a int, b numeric, c text);
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+ v1
+----------------------------
+ (1,3.14159265358979,hello)
+(1 row)
+
+LET v1.b = 10.2222;
+SELECT v1;
+ v1
+-------------------
+ (1,10.2222,hello)
+(1 row)
+
+-- should to fail
+LET v1.x = 10;
+ERROR: cannot assign to field "x" of column "x" because there is no such column in data type t1
+LINE 1: LET v1.x = 10;
+ ^
+DROP VARIABLE v1;
+DROP TYPE t1;
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+ va1
+------------
+ {10.1,2.1}
+(1 row)
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+ va2
+--------------------
+ (10.2,"{0.0,0.0}")
+(1 row)
+
+LET va2.b[1] = 10.3;
+SELECT va2;
+ va2
+---------------------
+ (10.2,"{10.3,0.0}")
+(1 row)
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+ v1
+------------------
+ 6.28318530717958
+(1 row)
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+ v2
+--------------------------
+ (3.14159265358979,Hello)
+(1 row)
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+ERROR: cannot drop type t2 because other objects depend on it
+DETAIL: schema variable v2 depends on type t2
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+ var1
+------
+ 1000
+(1 row)
+
+CREATE ROLE var_test_role;
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+DROP VARIABLE var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+SET ROLE TO DEFAULT;
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+SET ROLE TO var_test_role;
+SELECT public.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+SELECT public.var1_renamed;
+ var1_renamed
+--------------
+ 1000
+(1 row)
+
+DROP VARIABLE public.var1_renamed;
+SET ROLE TO DEFAULt;
+DROP ROLE var_test_role;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 16f979c8d9..9bf379b87b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml schema_variables
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 42632be675..42bf4ecb3f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -191,3 +191,4 @@ test: partition_aggregate
test: event_trigger
test: fast_default
test: stats
+test: schema_variables
diff --git a/src/test/regress/sql/schema_variables.sql b/src/test/regress/sql/schema_variables.sql
new file mode 100644
index 0000000000..91b2bbb28b
--- /dev/null
+++ b/src/test/regress/sql/schema_variables.sql
@@ -0,0 +1,247 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+
+DROP VARIABLE var1, var2;
+
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+
+CREATE ROLE var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT READ ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+-- should to work
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to work
+LET var1 = 333;
+
+SET ROLE TO DEFAULT;
+
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+
+SELECT secure_var();
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT public.var1;
+
+-- should to work;
+SELECT secure_var();
+
+SET ROLE TO DEFAULT;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+
+CREATE VIEW schema_var_view AS SELECT var1;
+
+SELECT * FROM schema_var_view;
+
+\c -
+
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+
+LET var1 = pi();
+
+SELECT var1;
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+
+EXECUTE var_pp(100, 1.23456);
+
+SELECT var1;
+
+CREATE VARIABLE var3 AS int;
+
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT inc(1);
+SELECT inc(1);
+SELECT inc(1);
+
+SELECT inc(1) FROM generate_series(1,10);
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+LET var3 = 0;
+
+SET ROLE TO DEFAULT;
+
+DROP VIEW schema_var_view;
+
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+
+-- composite variables
+
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+
+\d v1
+\d v2
+
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+
+SELECT v1;
+SELECT v2;
+SELECT (v1).*;
+SELECT (v2).*;
+
+SELECT v1.x + v1.z;
+SELECT v2.x + v2.z;
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+
+SELECT v2.x;
+
+SET ROLE TO DEFAULT;
+
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+DROP ROLE var_test_role;
+
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+
+-- should to fail
+SELECT varx.xxx;
+
+-- variables can be updated under RO transaction
+
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+
+SELECT varx;
+
+DROP VARIABLE varx;
+
+CREATE TYPE t1 AS (a int, b numeric, c text);
+
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+LET v1.b = 10.2222;
+SELECT v1;
+
+-- should to fail
+LET v1.x = 10;
+
+DROP VARIABLE v1;
+DROP TYPE t1;
+
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+LET va2.b[1] = 10.3;
+SELECT va2;
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+
+CREATE ROLE var_test_role;
+
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+DROP VARIABLE var_schema2.var1;
+
+SET ROLE TO DEFAULT;
+
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+
+SET ROLE TO var_test_role;
+SELECT public.var1;
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+
+SELECT public.var1_renamed;
+
+DROP VARIABLE public.var1_renamed;
+
+SET ROLE TO DEFAULt;
+
+DROP ROLE var_test_role;
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-08-12 05:35 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-14 14:38 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
0 siblings, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-08-12 05:35 UTC (permalink / raw)
To: Gilles Darold <gilles.darold@dalibo.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
2018-08-11 20:46 GMT+02:00 Pavel Stehule <pavel.stehule@gmail.com>:
>
>
> 2018-08-11 7:39 GMT+02:00 Pavel Stehule <pavel.stehule@gmail.com>:
>
>> Hi
>>
>> I am sending updated patch. It should to solve almost all Giles's and
>> Peter's objections.
>>
>> I am not happy so executor access values of variables directly. It is
>> most simple implementation - and I hope so it is good enough, but now the
>> access to variables is too volatile. But it is works good enough for
>> usability testing.
>>
>> I am thinking about some cache of used variables in ExprContext, so the
>> variable in one ExprContext will look like stable - more like PLpgSQL
>> variables.
>>
>
> I wrote EState based schema variable values cache, so now the variables in
> queries are stable (like PARAM_EXTERN) and can be used for optimization.
>
new update - after cleaning
Regards
Pavel
> Regards
>
> Pavel
>
>
>>
>> Regards
>>
>> Pavel
>>
>
>
Attachments:
[text/x-patch] schema-variables-180812-01.patch (194.9K, ../../CAFj8pRA_jZYuTRHEMsv8CnZLBqmnS5xRjcZh-uf0nBWA7WrzMA@mail.gmail.com/3-schema-variables-180812-01.patch)
download | inline diff:
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3bb48d4ccf..b863823160 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -359,6 +359,11 @@
<entry><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link></entry>
<entry>mappings of users to foreign servers</entry>
</row>
+
+ <row>
+ <entry><link linkend="catalog-pg-variable"><structname>pg_variable</structname></link></entry>
+ <entry>schema variables</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -11311,4 +11316,104 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
+ <sect1 id="catalog-pg-variable">
+ <title><structname>pg_variable</structname></title>
+
+ <indexterm zone="catalog-pg-variable">
+ <primary>pg_variable</primary>
+ </indexterm>
+
+ <para>
+ The table <structname>pg_variable</structname> holds metadata
+ of schema variables.
+ </para>
+
+ <table>
+ <title><structname>pg_views</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><structfield>oid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry></entry>
+ <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Name of the schema variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the namespace that contains this variable
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartype</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the data type of this variable.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartypmod</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ <structfield>vartypmod</structfield> records type-specific data
+ supplied at table creation time (for example, the maximum
+ length of a <type>varchar</type> column). It is passed to
+ type-specific input functions and length coercion functions.
+ The value will generally be -1 for types that do not need <structfield>vartypmod</structfield>.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>varowner</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>Owner of the variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>vardefexpr</structfield></entry>
+ <entry><type>pg_node_tree</type></entry>
+ <entry></entry>
+ <entry>The internal representation of the variable default value</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>
+ Access privileges; see
+ <xref linkend="sql-grant"/> and
+ <xref linkend="sql-revoke"/>
+ for details
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..0631c9ed56 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -47,6 +47,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterType SYSTEM "alter_type.sgml">
<!ENTITY alterUser SYSTEM "alter_user.sgml">
<!ENTITY alterUserMapping SYSTEM "alter_user_mapping.sgml">
+<!ENTITY alterVariable SYSTEM "alter_variable.sgml">
<!ENTITY alterView SYSTEM "alter_view.sgml">
<!ENTITY analyze SYSTEM "analyze.sgml">
<!ENTITY begin SYSTEM "begin.sgml">
@@ -99,6 +100,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createType SYSTEM "create_type.sgml">
<!ENTITY createUser SYSTEM "create_user.sgml">
<!ENTITY createUserMapping SYSTEM "create_user_mapping.sgml">
+<!ENTITY createVariable SYSTEM "create_variable.sgml">
<!ENTITY createView SYSTEM "create_view.sgml">
<!ENTITY deallocate SYSTEM "deallocate.sgml">
<!ENTITY declare SYSTEM "declare.sgml">
@@ -148,6 +150,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropUser SYSTEM "drop_user.sgml">
<!ENTITY dropUserMapping SYSTEM "drop_user_mapping.sgml">
<!ENTITY dropView SYSTEM "drop_view.sgml">
+<!ENTITY dropVariable SYSTEM "drop_variable.sgml">
<!ENTITY end SYSTEM "end.sgml">
<!ENTITY execute SYSTEM "execute.sgml">
<!ENTITY explain SYSTEM "explain.sgml">
@@ -155,6 +158,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY grant SYSTEM "grant.sgml">
<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
<!ENTITY insert SYSTEM "insert.sgml">
+<!ENTITY let SYSTEM "let.sgml">
<!ENTITY listen SYSTEM "listen.sgml">
<!ENTITY load SYSTEM "load.sgml">
<!ENTITY lock SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/alter_variable.sgml b/doc/src/sgml/ref/alter_variable.sgml
new file mode 100644
index 0000000000..6376ac716b
--- /dev/null
+++ b/doc/src/sgml/ref/alter_variable.sgml
@@ -0,0 +1,170 @@
+<!--
+doc/src/sgml/ref/alter_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-altervariable">
+ <indexterm zone="sql-altervariable">
+ <primary>ALTER VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>ALTER VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ALTER VARIABLE</refname>
+ <refpurpose>
+ change the definition of a variable
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="parameter">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>ALTER VARIABLE</command> changes the definition of an existing variable.
+ There are several subforms:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>OWNER</literal></term>
+ <listitem>
+ <para>
+ This form changes the owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RENAME</literal></term>
+ <listitem>
+ <para>
+ This form changes the name of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET SCHEMA</literal></term>
+ <listitem>
+ <para>
+ This form moves the variable into another schema.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </para>
+
+ <para>
+ You must own the variable to use <command>ALTER VARIABLE</command>.
+ To change the schema of a variable, you must also have
+ <literal>CREATE</literal> privilege on the new schema.
+ To alter the owner, you must also be a direct or indirect member of the new
+ owning role, and that role must have <literal>CREATE</literal> privilege on
+ the variable's schema. (These restrictions enforce that altering the owner
+ doesn't do anything you couldn't do by dropping and recreating the variable.
+ However, a superuser can alter ownership of any type anyway.)
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (possibly schema-qualified) of an existing variable to
+ alter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_name</replaceable></term>
+ <listitem>
+ <para>
+ The new name for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_owner</replaceable></term>
+ <listitem>
+ <para>
+ The user name of the new owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_schema</replaceable></term>
+ <listitem>
+ <para>
+ The new schema for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To rename a variable:
+<programlisting>
+ALTER VARIABLE foo RENAME TO boo;
+</programlisting>
+ </para>
+
+ <para>
+ To change the owner of the variable <literal>boo</literal>
+ to <literal>joe</literal>:
+<programlisting>
+ALTER VARIABLE boo OWNER TO joe;
+</programlisting>
+ </para>
+
+ <para>
+ To change the schema of the variable <literal>boo</literal>
+ to <literal>private</literal>:
+<programlisting>
+ALTER VARIABLE boo SET SCHEMA private;
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ This comman is a PostgreSQL extension.
+ </para>
+ </refsect1>
+
+ <refsect1 id="sql-altervariable-see-also">
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
new file mode 100644
index 0000000000..6099538813
--- /dev/null
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -0,0 +1,134 @@
+<!--
+doc/src/sgml/ref/create_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-createvariable">
+ <indexterm zone="sql-createvariable">
+ <primary>CREATE VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE VARIABLE</refname>
+ <refpurpose>define a new permissioned typed schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ]
+</synopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> creates a new schema variable.
+ These variables are scalar typed, non-transactional, and, like relations,
+ exist within a schema with access controlled via
+ <command>GRANT</command> and <command>REVOKE</command>.
+ </para>
+
+ <para>
+ The value of a schema variable is session-local. Retrieving
+ a variable's value will return NULL unless its value has been set
+ to something else in the current session.
+ </para>
+
+ <para>
+ Retrieval is done via the <function>get_schema_variable</function>dunxrion or the SQL
+ command <command>SELECT</command>. Setting of values is done via the
+ <function>set_schema_variable</function> function or the SQL command
+ <command>LET</command>.
+ Notably, while schema variables are in many ways a kind of table you cannot use
+ <command>UPDATE</command> on them.
+ </para>
+
+ <para>
+ For purposes of name uniqueness relation-like objects (e.g., tables, indexes)
+ within the same schema are considered. i.e., you cannot give a table and a
+ schema variable the same name. This is a consequence of them being treated
+ like relations for purposes of <command>SELECT</command>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF NOT EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the name already exists. A notice is issued in this case.
+ Note that type of the variable is not considered, nor could it be since the namespace
+ searched contains non-variable objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the data type of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Use <command>DROP VARIABLE</command> to remove a variable.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Create an integer variable <literal>var1</literal>:
+<programlisting>
+CREATE VARIABLE var1 AS integer;
+SELECT var1;
+</programlisting>
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> is a PostgreSQL feature.
+ <!-- The choice of wording here seems to be left to personal preference... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index 6b909b7232..d83ad811fd 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
+DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES }
</synopsis>
</refsynopsisdiv>
@@ -75,6 +75,17 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>VARIABLES</literal></term>
+ <listitem>
+ <para>
+ Resets the value of all schema variables. When variables
+ will be used later, then will be initialized again to
+ NULL or default value.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
new file mode 100644
index 0000000000..c1c1a2bd67
--- /dev/null
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -0,0 +1,93 @@
+<!--
+doc/src/sgml/ref/drop_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropvariable">
+ <indexterm zone="sql-dropvariable">
+ <primary>DROP VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP VARIABLE</refname>
+ <refpurpose>remove a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP VARIABLE</command> removes a schema variable.
+ A variable can only be dropped by its owner or a superuser.
+ <!-- this would suggest that we need an alter variable owner to command -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the variable does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To remove the schema variable <literal>var1</literal>:
+
+<programlisting>
+DROP VARIABLE var1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ <!-- create variable is a "PostgreSQL feature",
+ this is a "proprietary PostgreSQL command" ... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-createvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index ff64c7a3ba..a83920a7a1 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -79,6 +79,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE <replaceable>type_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
[ GROUP ] <replaceable class="parameter">role_name</replaceable>
@@ -167,6 +171,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
foreign servers,
large objects,
schemas,
+ schema variable
or tablespaces.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
@@ -385,6 +390,24 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>READ</literal></term>
+ <listitem>
+ <para>
+ Allows to read a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WRITE</literal></term>
+ <listitem>
+ <para>
+ Allows to set a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL PRIVILEGES</literal></term>
<listitem>
@@ -550,6 +573,8 @@ rolename=xxxx -- privileges granted to a role
C -- CREATE
c -- CONNECT
T -- TEMPORARY
+ S -- READ
+ w -- WRITE
arwdDxt -- ALL PRIVILEGES (for tables, varies for other objects)
* -- grant option for preceding privilege
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
new file mode 100644
index 0000000000..e8bf3f6dd4
--- /dev/null
+++ b/doc/src/sgml/ref/let.sgml
@@ -0,0 +1,90 @@
+<!--
+doc/src/sgml/ref/let.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-let">
+ <indexterm zone="sql-let">
+ <primary>LET</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>LET</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>LET</refname>
+ <refpurpose>change a schema variable's value</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable class="parameter">sql_expression</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ The <command>LET</command> command updates the specified schema variable' value.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>schema_variable</literal></term>
+ <listitem>
+ <para>
+ The name of schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sql expression</literal></term>
+ <listitem>
+ <para>
+ An SQL expression, the result is cast to the schema variable's type.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Example:
+<programlisting>
+CREATE VARIABLE myvar AS integer;
+LET myvar = 10;
+LET myvar = (SELECT sum(val) FROM tab);
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <!-- this feels like it needs to be more specific,
+ but I don't know enough to make it so -->
+ <literal>LET</literal> extends syntax defined in the SQL
+ standard. The standard knows <literal>SET</literal> command,
+ that is used for different purpouse in PostgreSQL.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 5317f8ccba..8435e05957 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -108,6 +108,12 @@ REVOKE [ GRANT OPTION FOR ]
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_name</replaceable> [, ...]
[ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { READ | WRITE } [, ...] | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..5fb82df51e 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -75,6 +75,7 @@
&alterType;
&alterUser;
&alterUserMapping;
+ &alterVariable;
&alterView;
&analyze;
&begin;
@@ -127,6 +128,7 @@
&createType;
&createUser;
&createUserMapping;
+ &createVariable;
&createView;
&deallocate;
&declare;
@@ -175,6 +177,7 @@
&dropType;
&dropUser;
&dropUserMapping;
+ &dropVariable;
&dropView;
&end;
&execute;
@@ -183,6 +186,7 @@
&grant;
&importForeignSchema;
&insert;
+ &let;
&listen;
&load;
&lock;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 0865240f11..1f7c4d1223 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -19,7 +19,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_publication.o pg_range.o \
pg_db_role_setting.o pg_shdepend.o pg_subscription.o pg_type.o \
- storage.o toasting.o
+ pg_variable.o storage.o toasting.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
@@ -46,7 +46,7 @@ CATALOG_HEADERS := \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
- pg_subscription_rel.h
+ pg_subscription_rel.h pg_variable.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 578e4c6592..86917e15a8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -57,6 +57,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_transform.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
@@ -112,6 +113,7 @@ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
+static void ExecGrant_Variable(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
@@ -284,6 +286,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -507,6 +512,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -609,6 +618,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_VARIABLE:
+ ExecGrant_Variable(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -768,6 +780,16 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_VARIABLE:
+ foreach(cell, objnames)
+ {
+ RangeVar *varvar = (RangeVar *) lfirst(cell);
+ Oid relOid;
+
+ relOid = lookup_variable(varvar->schemaname, varvar->relname, false);
+ objects = lappend_oid(objects, relOid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -855,6 +877,31 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
heap_close(rel, AccessShareLock);
}
break;
+ case OBJECT_VARIABLE:
+ {
+ ScanKeyData key;
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&key,
+ Anum_pg_variable_varnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(namespaceId));
+
+ rel = heap_open(VariableRelationId, AccessShareLock);
+ scan = heap_beginscan_catalog(rel, 1, &key);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ }
+ break;
+
default:
/* should not happen */
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
@@ -1018,6 +1065,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1215,6 +1266,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1441,6 +1498,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -3266,6 +3326,129 @@ ExecGrant_Type(InternalGrant *istmt)
heap_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Variable(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_VARIABLE;
+
+ relation = heap_open(VariableRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid varId = lfirst_oid(cell);
+ Form_pg_variable pg_variable_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_variable];
+ bool nulls[Natts_pg_variable];
+ bool replaces[Natts_pg_variable];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for schema variables %u", varId);
+
+ pg_variable_tuple = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_variable_tuple->varowner;
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple, Anum_pg_variable_varacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_VARIABLE, ownerId);
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ oldmembers = NULL;
+ }
+ else
+ {
+ old_acl = DatumGetAclPCopy(aclDatum);
+ /* Get the roles mentioned in the existing ACL */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+ }
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ varId, grantorId, OBJECT_VARIABLE,
+ NameStr(pg_variable_tuple->varname),
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ */
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_variable_varacl - 1] = true;
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(varId, VariableRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(VariableRelationId, varId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3298,6 +3481,10 @@ string_to_privilege(const char *privname)
return ACL_CONNECT;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
+ if (strcmp(privname, "read") == 0)
+ return ACL_READ;
+ if (strcmp(privname, "write") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized privilege type \"%s\"", privname)));
@@ -3333,6 +3520,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3456,6 +3647,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3566,6 +3760,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3710,6 +3907,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
+ case OBJECT_VARIABLE:
+ return pg_variable_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objtype: %d",
(int) objtype);
@@ -4499,6 +4698,67 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a variable.
+ */
+AclMode
+pg_variable_aclmask(Oid var_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_variable varForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the type's tuple from pg_type
+ */
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable with OID %u does not exist",
+ var_oid)));
+ varForm = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Now get the type's owner and ACL from the tuple
+ */
+ ownerId = varForm->varowner;
+
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple,
+ Anum_pg_variable_varacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_VARIABLE, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+
/*
* Exported routine for checking a user's access privileges to a column
*
@@ -4744,6 +5004,18 @@ pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a variable
+ */
+AclResult
+pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
+{
+ if (pg_variable_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Ownership check for a relation (specified by OID).
*/
@@ -5361,6 +5633,33 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a schema variables (specified by OID).
+ */
+bool
+pg_variable_ownercheck(Oid db_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(db_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("variable with OID %u does not exist", db_oid)));
+
+ ownerId = ((Form_pg_variable) GETSTRUCT(tuple))->varowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+
/*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
@@ -5486,6 +5785,10 @@ get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
+ case OBJECT_VARIABLE:
+ defaclobjtype = DEFACLOBJ_VARIABLE;
+ break;
+
default:
return NULL;
}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 4f1d365357..782ddb1655 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -59,6 +59,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -67,6 +68,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
@@ -1280,6 +1282,10 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_VARIABLE:
+ RemoveVariableById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2537,6 +2543,9 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case VariableRelationId:
+ return OCLASS_VARIABLE;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 0f67a122ed..81aaf454a8 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -39,6 +39,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
@@ -755,6 +756,71 @@ RelationIsVisible(Oid relid)
return visible;
}
+/*
+ * VariableIsVisible
+ * Determine whether a variable (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified variable name".
+ */
+bool
+VariableIsVisible(Oid varid)
+{
+ HeapTuple vartup;
+ Form_pg_variable varform;
+ Oid varnamespace;
+ bool visible;
+
+ vartup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(vartup))
+ elog(ERROR, "cache lookup failed for schema variable %u", varid);
+ varform = (Form_pg_variable) GETSTRUCT(vartup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ varnamespace = varform->varnamespace;
+ if (varnamespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, varnamespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another relation of the same name earlier in the path. So
+ * we must do a slow check for conflicting relations.
+ */
+ char *varname = NameStr(varform->varname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == varnamespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (OidIsValid(get_varname_varid(varname, namespaceId)))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(vartup);
+
+ return visible;
+}
+
+
/*
* TypenameGetTypid
@@ -2776,6 +2842,202 @@ TSConfigIsVisible(Oid cfgid)
return visible;
}
+/*
+ * When we know a variable name, then we can find variable simply
+ */
+Oid
+lookup_variable(const char *nspname, const char *varname, bool missing_ok)
+{
+ Oid namespaceId;
+ Oid varoid = InvalidOid;
+ ListCell *l;
+
+ if (nspname)
+ {
+ namespaceId = LookupExplicitNamespace(nspname, missing_ok);
+ if (!OidIsValid(namespaceId))
+ return InvalidOid;
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+
+ if (OidIsValid(varoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(varoid) && !missing_ok)
+ {
+ if (nspname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\".\"%s\" does not exist",
+ nspname, varname)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\" does not exist",
+ varname)));
+ }
+
+ return varoid;
+}
+
+List *
+NamesFromList(List *names)
+{
+ ListCell *l;
+ List *result = NIL;
+
+ foreach(l, names)
+ {
+ Node *n = lfirst(l);
+
+ if (IsA(n, String))
+ {
+ result = lappend(result, n);
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * identify_variable
+ *
+ * Returns oid of not ambigonuous variable specified by qualified path
+ * or InvalidOid. When the path is ambigonuous, then not_uniq flag is
+ * is true.
+ */
+Oid
+identify_variable(List *names, char **attrname, bool *not_uniq)
+{
+ char *a = NULL;
+ char *b = NULL;
+ char *c = NULL;
+ char *d = NULL;
+ Oid varoid_without_attr;
+ Oid varoid_with_attr;
+
+ *not_uniq = false;
+
+ switch (list_length(names))
+ {
+ case 1:
+ a = strVal(linitial(names));
+ return lookup_variable(NULL, a, true);
+
+ case 2:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+
+ /*
+ * a.b can mean "schema"."variable" or "variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(a, b, true);
+ varoid_with_attr = lookup_variable(NULL, a, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = b;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 3:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+
+ /*
+ * a.b.c can mean "catalog"."schema"."variable" or "schema"."variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(b, c, true);
+ varoid_with_attr = lookup_variable(a, b, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = c;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 4:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+ d = strVal(lfourth(names));
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ *attrname = d;
+ return lookup_variable(b, c, true);
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(names))));
+ break;
+ }
+}
/*
* DeconstructQualifiedName
@@ -4416,3 +4678,14 @@ pg_is_other_temp_schema(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(isOtherTempNamespace(oid));
}
+
+Datum
+pg_variable_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ if (!SearchSysCacheExists1(VARIABLEOID, ObjectIdGetDatum(oid)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_BOOL(VariableIsVisible(oid));
+}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7db942dcba..cc3d415e61 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -58,6 +58,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -489,6 +490,18 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
OBJECT_STATISTIC_EXT,
true
+ },
+ {
+ VariableRelationId,
+ VariableObjectIndexId,
+ VARIABLEOID,
+ VARIABLENAMENSP,
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ Anum_pg_variable_varowner,
+ Anum_pg_variable_varacl,
+ OBJECT_VARIABLE,
+ true
}
};
@@ -714,6 +727,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_VARIABLE */
+ {
+ "schema variable", OBJECT_VARIABLE
}
};
@@ -739,6 +756,7 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
bool missing_ok);
static ObjectAddress get_object_address_type(ObjectType objtype,
TypeName *typename, bool missing_ok);
+static ObjectAddress get_object_address_variable(List *object, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object,
bool missing_ok);
static ObjectAddress get_object_address_opf_member(ObjectType objtype,
@@ -996,6 +1014,10 @@ get_object_address(ObjectType objtype, Node *object,
missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_VARIABLE:
+ address = get_object_address_variable(castNode(List, object), missing_ok);
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
@@ -1848,16 +1870,20 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type \"%c\"", objtype),
- errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
+ errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
DEFACLOBJ_RELATION,
DEFACLOBJ_SEQUENCE,
DEFACLOBJ_FUNCTION,
DEFACLOBJ_TYPE,
- DEFACLOBJ_NAMESPACE)));
+ DEFACLOBJ_NAMESPACE,
+ DEFACLOBJ_VARIABLE)));
}
/*
@@ -1942,6 +1968,24 @@ textarray_to_strvaluelist(ArrayType *arr)
return list;
}
+/*
+ * Find the ObjectAddress for a type or domain
+ */
+static ObjectAddress
+get_object_address_variable(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ char *nspname = NULL;
+ char *varname = NULL;
+
+ ObjectAddressSet(address, VariableRelationId, InvalidOid);
+
+ DeconstructQualifiedName(object, &nspname, &varname);
+ address.objectId = lookup_variable(nspname, varname, missing_ok);
+
+ return address;
+}
+
/*
* SQL-callable version of get_object_address
*/
@@ -2131,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_TABCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
+ case OBJECT_VARIABLE:
objnode = (Node *) name;
break;
case OBJECT_ACCESS_METHOD:
@@ -2415,6 +2460,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
break;
+ case OBJECT_VARIABLE:
+ if (!pg_variable_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
default:
elog(ERROR, "unrecognized object type: %d",
(int) objtype);
@@ -3157,6 +3207,32 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_VARIABLE:
+ {
+ char *nspname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ if (VariableIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ appendStringInfo(&buffer, _("schema variable %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(varform->varname)));
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_TSPARSER:
{
HeapTuple tup;
@@ -3422,6 +3498,16 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new schemas belonging to role %s"),
rolename);
break;
+ case DEFACLOBJ_VARIABLE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s"),
+ rolename);
+ break;
default:
/* shouldn't get here */
if (nspname)
@@ -4070,6 +4156,10 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_VARIABLE:
+ appendStringInfoString(&buffer, "schema variable");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4962,6 +5052,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on variables");
+ break;
}
if (objname)
@@ -5121,6 +5215,33 @@ getObjectIdentityParts(const ObjectAddress *object,
}
break;
+ case OCLASS_VARIABLE:
+ {
+ char *schema;
+ char *varname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ schema = get_namespace_name_or_temp(varform->varnamespace);
+ varname = NameStr(varform->varname);
+
+ appendStringInfo(&buffer, "%s",
+ quote_qualified_identifier(schema, varname));
+
+ if (objname)
+ *objname = list_make2(schema, varname);
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
diff --git a/src/backend/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c
new file mode 100644
index 0000000000..ff71f8bf6a
--- /dev/null
+++ b/src/backend/catalog/pg_variable.c
@@ -0,0 +1,305 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.c
+ * schema variables
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_variable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+
+#include "nodes/makefuncs.h"
+
+#include "storage/lmgr.h"
+
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * Returns name of schema variable. When variable is not on path,
+ * then the name is qualified.
+ */
+char *
+schema_variable_get_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+ char *nspname;
+ char *result;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ if (VariableIsVisible(varid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ result = quote_qualified_identifier(nspname, varname);
+
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/*
+ * Returns varname field of pg_variable
+ */
+char *
+get_schema_variable_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ ReleaseSysCache(tup);
+
+ return varname;
+}
+
+/*
+ * Returns type, typmod of schema variable
+ */
+void
+get_schema_variable_type_typmod(Oid varid, Oid *typid, int32 *typmod)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ *typid = varform->vartype;
+ *typmod = varform->vartypmod;
+
+ ReleaseSysCache(tup);
+
+ return;
+}
+
+/*
+ * Fetch all fields of schema variable from the syscache.
+ */
+Variable *
+GetVariable(Oid varid, bool missing_ok)
+{
+ HeapTuple tup;
+ Variable *var;
+ Form_pg_variable varform;
+ Datum aclDatum;
+ Datum defexprDatum;
+ bool isnull;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (missing_ok)
+ return NULL;
+
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+ }
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ var = (Variable *) palloc(sizeof(Variable));
+ var->oid = varid;
+ var->name = pstrdup(NameStr(varform->varname));
+ var->namespace = varform->varnamespace;
+ var->typid = varform->vartype;
+ var->typmod = varform->vartypmod;
+ var->owner = varform->varowner;
+
+ /* Get defexpr */
+ defexprDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_vardefexpr,
+ &isnull);
+
+ if (!isnull)
+ var->defexpr = stringToNode(TextDatumGetCString(defexprDatum));
+ else
+ var->defexpr = NULL;
+
+ /* Get varacl */
+ aclDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_varacl,
+ &isnull);
+ if (!isnull)
+ var->acl = DatumGetAclPCopy(aclDatum);
+ else
+ var->acl = NULL;
+
+ ReleaseSysCache(tup);
+
+ return var;
+}
+
+ObjectAddress
+VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Node *varDefexpr,
+ bool if_not_exists)
+{
+ Acl *varacl;
+ NameData varname;
+ bool nulls[Natts_pg_variable];
+ Datum values[Natts_pg_variable];
+ Relation rel;
+ HeapTuple tup,
+ oldtup;
+ TupleDesc tupdesc;
+ ObjectAddress myself,
+ referenced;
+ Oid retval;
+ int i;
+
+ for (i = 0; i < Natts_pg_variable; i++)
+ {
+ nulls[i] = false;
+ values[i] = (Datum) 0;
+ }
+
+ namestrcpy(&varname, varName);
+ values[Anum_pg_variable_varname - 1] = NameGetDatum(&varname);
+ values[Anum_pg_variable_varnamespace - 1] = ObjectIdGetDatum(varNamespace);
+ values[Anum_pg_variable_vartype - 1] = ObjectIdGetDatum(varType);
+ values[Anum_pg_variable_vartypmod - 1] = Int32GetDatum(varTypmod);
+ values[Anum_pg_variable_varowner - 1] = ObjectIdGetDatum(varOwner);
+ /* proacl will be determined later */
+
+ if (varDefexpr)
+ values[Anum_pg_variable_vardefexpr - 1] = CStringGetTextDatum(nodeToString(varDefexpr));
+ else
+ nulls[Anum_pg_variable_vardefexpr - 1] = true;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+ tupdesc = RelationGetDescr(rel);
+
+ oldtup = SearchSysCache2(VARIABLENAMENSP,
+ PointerGetDatum(varName),
+ ObjectIdGetDatum(varNamespace));
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ if (if_not_exists)
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists, skipping",
+ varName)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists",
+ varName)));
+
+ heap_freetuple(oldtup);
+ heap_close(rel, RowExclusiveLock);
+
+ return InvalidObjectAddress;
+ }
+
+ varacl = get_user_default_acl(OBJECT_VARIABLE, varOwner,
+ varNamespace);
+
+ if (varacl != NULL)
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(varacl);
+ else
+ nulls[Anum_pg_variable_varacl - 1] = true;
+
+ tup = heap_form_tuple(tupdesc, values, nulls);
+ CatalogTupleInsert(rel, tup);
+
+ retval = HeapTupleGetOid(tup);
+
+ myself.classId = VariableRelationId;
+ myself.objectId = retval;
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = varNamespace;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on used type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = varType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on any roles mentioned in ACL */
+ if (varacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(varacl, &newmembers);
+ updateAclDependencies(VariableRelationId, retval, 0,
+ varOwner,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_freetuple(tup);
+
+ /* Post creation hook for new function */
+ InvokeObjectPostCreateHook(VariableRelationId, retval, 0);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..2cb5b1172d 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,7 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
- schemacmds.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
+ schemacmds.o seclabel.o sequence.o schemavariable.o statscmds.o subscriptioncmds.o \
tablecmds.o tablespace.o trigger.o tsearchcmds.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index eff325cc7d..a9d5e5e0ad 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -387,6 +387,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_VARIABLE:
{
ObjectAddress address;
Relation catalog;
@@ -504,6 +505,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
@@ -594,6 +596,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_TSDICT:
case OCLASS_TSTEMPLATE:
case OCLASS_TSCONFIG:
+ case OCLASS_VARIABLE:
{
Relation catalog;
@@ -852,6 +855,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_TABLESPACE:
case OBJECT_TSDICTIONARY:
case OBJECT_TSCONFIGURATION:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 01a999c2ac..fec2495e93 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -19,6 +19,7 @@
#include "commands/discard.h"
#include "commands/prepare.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "utils/guc.h"
#include "utils/portal.h"
@@ -48,6 +49,10 @@ DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
ResetTempTableNamespace();
break;
+ case DISCARD_VARIABLES:
+ ResetSchemaVariableCache();
+ break;
+
default:
elog(ERROR, "unrecognized DISCARD target: %d", stmt->target);
}
@@ -75,4 +80,5 @@ DiscardAll(bool isTopLevel)
ResetPlanCache();
ResetTempTableNamespace();
ResetSequenceCaches();
+ ResetSchemaVariableCache();
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e..426df246b3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -126,6 +126,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"TEXT SEARCH TEMPLATE", true},
{"TYPE", true},
{"USER MAPPING", true},
+ {"VARIABLE", true},
{"VIEW", true},
{NULL, false}
};
@@ -297,7 +298,8 @@ check_ddl_tag(const char *tag)
pg_strcasecmp(tag, "REVOKE") == 0 ||
pg_strcasecmp(tag, "DROP OWNED") == 0 ||
pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
+ pg_strcasecmp(tag, "SECURITY LABEL") == 0 ||
+ pg_strcasecmp(tag, "CREATE VARIABLE") == 0)
return EVENT_TRIGGER_COMMAND_TAG_OK;
/*
@@ -1146,6 +1148,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -1209,6 +1212,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
return true;
/*
@@ -2244,6 +2248,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2326,6 +2332,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b1556a..eb8c08baf3 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_PLAN_UTILITY:
/* OK */
break;
default:
diff --git a/src/backend/commands/schemavariable.c b/src/backend/commands/schemavariable.c
new file mode 100644
index 0000000000..3d39f5fd41
--- /dev/null
+++ b/src/backend/commands/schemavariable.c
@@ -0,0 +1,467 @@
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
+#include "executor/executor.h"
+#include "executor/svariableReceiver.h"
+#include "nodes/execnodes.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/inval.h"
+#include "utils/memutils.h"
+#include "utils/lsyscache.h"
+#include "utils/snapmgr.h"
+#include "utils/syscache.h"
+
+/*
+ * The content of variables is not transactional. Due this fact the
+ * implementation of DROP can be simple, because although DROP VARIABLE
+ * can be reverted, the content of variable can be lost. In this example,
+ * DROP VARIABLE is same like reset variable.
+ */
+
+typedef struct SchemaVariableData
+{
+ Oid varid; /* pg_variable OID of this sequence (hash key) */
+ Oid typid; /* OID of the data type */
+ int32 typmod;
+ int16 typlen;
+ bool typbyval;
+ bool isnull;
+ bool freeval;
+ Datum value;
+ bool is_rowtype; /* true when variable is composite */
+ bool is_valid; /* true when variable was successfuly initialized */
+} SchemaVariableData;
+
+typedef SchemaVariableData *SchemaVariable;
+
+static HTAB *schemavarhashtab = NULL; /* hash table for session variables */
+static MemoryContext SchemaVariableMemoryContext = NULL;
+
+static bool first_time = true;
+static void create_schemavar_hashtable(void);
+static bool clean_cache_req = false;
+
+static void clean_cache(void);
+static void force_clean_cache(XactEvent event, void *arg);
+
+
+/*
+ * Save info about ncessity to clean hash table, because some
+ * schema variable was dropped. Don't do here more, recheck
+ * needs to be in transaction state.
+ */
+static void
+InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ if (cacheid != VARIABLEOID)
+ return;
+
+ clean_cache_req = true;
+}
+
+static void
+force_clean_cache(XactEvent event, void *arg)
+{
+ /*
+ * should continue only in transaction time, when
+ * syscache is available.
+ */
+ if (clean_cache_req && IsTransactionState())
+ {
+ clean_cache();
+ clean_cache_req = false;
+ }
+}
+
+static void
+clean_cache(void)
+{
+ HASH_SEQ_STATUS status;
+ SchemaVariable var;
+
+ if (!schemavarhashtab)
+ return;
+
+ hash_seq_init(&status, schemavarhashtab);
+
+ /*
+ * Every valid variable have to have entry in system
+ * catalog. Removed if there is nothing.
+ */
+ while ((var = (SchemaVariable) hash_seq_search(&status)) != NULL)
+ {
+ HeapTuple tp = InvalidOid;
+
+ tp = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var->varid));
+ if (!HeapTupleIsValid(tp))
+ {
+ elog(DEBUG1, "variable %d is removed from cache", var->varid);
+
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (hash_search(schemavarhashtab,
+ (void *) &var->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(DEBUG1, "hash table corrupted");
+ }
+ else
+ ReleaseSysCache(tp);
+ }
+}
+
+/*
+ * Create the hash table for storing schema variables
+ */
+static void
+create_schemavar_hashtable(void)
+{
+ HASHCTL ctl;
+
+ /* set callbacks */
+ if (first_time)
+ {
+ CacheRegisterSyscacheCallback(VARIABLEOID,
+ InvalidateSchemaVarCacheCallback,
+ (Datum) 0);
+
+ RegisterXactCallback(force_clean_cache, NULL);
+
+ first_time = false;
+ }
+
+ /* needs own long life memory context */
+ if (SchemaVariableMemoryContext == NULL)
+ {
+ SchemaVariableMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "schema variables",
+ ALLOCSET_START_SMALL_SIZES);
+ }
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(SchemaVariableData);
+ ctl.hcxt = SchemaVariableMemoryContext;
+
+ schemavarhashtab = hash_create("Schema variables", 64, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/*
+ * Fast drop complete content of schema variables
+ */
+void
+ResetSchemaVariableCache(void)
+{
+ if (schemavarhashtab)
+ {
+ hash_destroy(schemavarhashtab);
+ schemavarhashtab = NULL;
+ }
+
+ if (SchemaVariableMemoryContext != NULL)
+ {
+ MemoryContextReset(SchemaVariableMemoryContext);
+ }
+}
+
+/*
+ * Drop variable by OID
+ */
+void
+RemoveVariableById(Oid varid)
+{
+ Relation rel;
+ HeapTuple tup;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ CatalogTupleDelete(rel, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Creates new variable - entry in pg_catalog.pg_variable table
+ */
+ObjectAddress
+DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt)
+{
+ Oid namespaceid;
+ AclResult aclresult;
+ Oid typid;
+ int32 typmod;
+ Oid varowner = GetUserId();
+
+ Node *cooked_default = NULL;
+
+ namespaceid =
+ RangeVarGetAndCheckCreationNamespace(stmt->variable, NoLock, NULL);
+
+ typenameTypeIdAndMod(pstate, stmt->typeName, &typid, &typmod);
+
+ aclresult = pg_type_aclcheck(typid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error_type(aclresult, typid);
+
+ if (stmt->defexpr)
+ {
+ cooked_default = transformExpr(pstate, stmt->defexpr,
+ EXPR_KIND_VARIABLE_DEFAULT);
+
+ cooked_default = coerce_to_specific_type(pstate,
+ cooked_default, typid, "DEFAULT");
+ }
+
+ return VariableCreate(stmt->variable->relname,
+ namespaceid,
+ typid,
+ typmod,
+ varowner,
+ cooked_default,
+ stmt->if_not_exists);
+}
+
+/*
+ * Try to search value in hash table. If doesn't
+ * exists insert it (and calculate defexpr if exists.
+ */
+static SchemaVariable
+PrepareSchemaVariableForReading(Oid varid)
+{
+ SchemaVariable svar;
+ Variable *var;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+ if (!found)
+ {
+ var = GetVariable(varid, false);
+ get_typlenbyval(var->typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = var->typid;
+ svar->typmod = var->typmod;
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+ svar->is_rowtype = type_is_rowtype(var->typid);
+
+ /* when we don't need calculate defexpr, value is valid already */
+ svar->is_valid = var->defexpr ? false : true;
+ }
+ else if (!svar->is_valid)
+ {
+ /* we need var to recalculate defexpr */
+ var = GetVariable(varid, false);
+ }
+ else
+ /* we don't need to go to sys cache */
+ var = NULL;
+
+ /*
+ * Initialize variable when it is necessary. It is fresh
+ * or last initialization was not successfull.
+ */
+ if (var != NULL && var->defexpr && !svar->is_valid)
+ {
+ MemoryContext oldcontext = NULL;
+
+ Datum value = (Datum) 0;
+ bool null;
+ EState *estate = NULL;
+ Expr *defexpr;
+ ExprState *defexprs;
+
+ /* Prepare default expr */
+ estate = CreateExecutorState();
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ defexpr = expression_planner((Expr *) var->defexpr);
+ defexprs = ExecInitExpr(defexpr, NULL);
+ value = ExecEvalExprSwitchContext(defexprs, GetPerTupleExprContext(estate), &null);
+
+ MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!null)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ FreeExecutorState(estate);
+ }
+
+ if (!svar->is_valid)
+ elog(ERROR, "the content of variable is not valid");
+
+ return svar;
+}
+
+/*
+ * Returns content of variable. We expext secured access now.
+ * Secure check should be done before.
+ */
+Datum
+GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid, bool copy)
+{
+ SchemaVariable svar;
+ Datum value;
+ bool isnull;
+
+ svar = PrepareSchemaVariableForReading(varid);
+ Assert(svar != NULL);
+
+ if (expected_typid != svar->typid)
+ elog(ERROR, "type of variable \"%s\" is different than expected",
+ schema_variable_get_name(varid));
+
+ value = svar->value;
+ isnull = svar->isnull;
+
+ *isNull = isnull;
+
+ if (!isnull && copy)
+ return datumCopy(value, svar->typbyval, svar->typlen);
+
+ return value;
+}
+
+/*
+ * Write value to variable. We expect secured access in this moment.
+ * In this time, we recheck syschache about used type.
+ */
+void
+SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod)
+{
+ MemoryContext oldcontext = NULL;
+
+ SchemaVariable svar;
+ Oid var_typid;
+ int32 var_typmod;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+
+ get_schema_variable_type_typmod(varid, &var_typid, &var_typmod);
+
+ /* check types first */
+ if (var_typid != typid)
+ elog(ERROR, "type of expression is different than schema variable type");
+
+ if (found)
+ {
+ /* release current content first */
+ if (svar->freeval)
+ {
+ pfree(DatumGetPointer(svar->value));
+ svar->value = (Datum) 0;
+ svar->isnull = true;
+ svar->freeval = false;
+ }
+ }
+
+ get_typlenbyval(typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = typid;
+ svar->typmod = typmod;
+
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+
+ svar->is_rowtype = type_is_rowtype(typid);
+ svar->is_valid = false;
+
+ oldcontext = MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!isNull)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+}
+
+void
+doLetStmt(PlannedStmt *pstmt,
+ ParamListInfo params,
+ QueryEnvironment *queryEnv,
+ const char *queryString)
+{
+ QueryDesc *queryDesc;
+ DestReceiver *dest;
+
+ PushCopiedSnapshot(GetActiveSnapshot());
+ UpdateActiveSnapshotCommandId();
+
+ /* Create dest receiver for LET */
+ dest = CreateDestReceiver(DestVariable);
+
+ SetVariableDestReceiverParams(dest, pstmt->resultVariable);
+
+ /* Create a QueryDesc requesting no output */
+ queryDesc = CreateQueryDesc(pstmt, queryString,
+ GetActiveSnapshot(),
+ InvalidSnapshot,
+ dest, params, queryEnv, 0);
+
+ ExecutorStart(queryDesc, 0);
+ ExecutorRun(queryDesc, ForwardScanDirection, 2L, true);
+ ExecutorFinish(queryDesc);
+ ExecutorEnd(queryDesc);
+
+ FreeQueryDesc(queryDesc);
+
+ PopActiveSnapshot();
+}
+
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cef6632840..30e6c1290b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9656,6 +9656,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
/*
* We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index cc09895fa5..ee8ff7da9e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -29,6 +29,6 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
nodeCtescan.o nodeNamedtuplestorescan.o nodeWorktablescan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o \
- nodeTableFuncscan.o
+ nodeTableFuncscan.o svariableReceiver.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d7..bb9bf53e1c 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -33,6 +33,7 @@
#include "access/nbtree.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -727,6 +728,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
Param *param = (Param *) node;
ParamListInfo params;
+ AclResult aclresult;
switch (param->paramkind)
{
@@ -736,6 +738,28 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
break;
+
+ case PARAM_VARIABLE:
+
+ /* Check permission to read schema variable */
+ aclresult = pg_variable_aclcheck(param->paramid, GetUserId(), ACL_READ);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE,
+ schema_variable_get_name(param->paramid));
+
+ /*
+ * Using varoid as paramid is not practical. Better to recount
+ * used schema variables from zero, and later to use paramid like
+ * offset.
+ */
+ scratch.opcode = EEOP_PARAM_VARIABLE;
+ scratch.d.vparam.paramid = state->nvariables++;
+ scratch.d.vparam.varoid = param->paramid;
+ scratch.d.vparam.paramtype = param->paramtype;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+
case PARAM_EXTERN:
/*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25aae5..4462dcc952 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -351,6 +352,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1007,6 +1009,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_VARIABLE)
+ {
+ /* iut of line implementation; too large */
+ ExecEvalParamVariable(state, op, econtext);
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_CASE_TESTVAL)
{
/*
@@ -2323,6 +2332,79 @@ ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
errmsg("no value found for parameter %d", paramId)));
}
+/*
+ * Evaluate a PARAM_VARIABLE parameter
+ */
+void
+ExecEvalParamVariable(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ EState *estate = econtext->ecxt_estate;
+
+ /*
+ * We should to ensure stable behave of schema variables in queries. It is
+ * important, because optimizer uses these values as stable, like extern
+ * parameters, what is nice, because queries are optimized well. So, don't
+ * try to access variables directly, use this query variable cache.
+ * This cache cannot be used when EState is shared - PLpgSQL did it for
+ * simple expressions.
+ */
+ if (estate && !estate->es_shared)
+ {
+ int paramid = op->d.vparam.paramid;
+
+ if (estate->es_nvariables == 0)
+ {
+ MemoryContext old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* initialize estate schema variable cache */
+
+ estate->es_nvariables = state->nvariables;
+ estate->es_varnulls = palloc(sizeof(bool) * state->nvariables);
+ estate->es_vartypes = palloc0(sizeof(Oid) * state->nvariables);
+ estate->es_varvalues = palloc(sizeof(Datum) * state->nvariables);
+
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ Assert(estate->es_nvariables == state->nvariables);
+ Assert(estate->es_nvariables > paramid);
+
+ if (!OidIsValid(estate->es_vartypes[paramid]))
+ {
+ MemoryContext old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* copy variable to estate schema variable cache */
+ estate->es_varvalues[paramid] =
+ GetSchemaVariable(op->d.vparam.varoid,
+ &estate->es_varnulls[paramid],
+ op->d.vparam.paramtype,
+ true);
+ estate->es_vartypes[paramid] = op->d.vparam.paramtype;
+
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ Assert(OidIsValid(estate->es_vartypes[paramid]));
+
+ *op->resvalue = estate->es_varvalues[paramid];
+ *op->resnull = estate->es_varnulls[paramid];
+ }
+ else
+ {
+ Datum d;
+ bool isnull;
+
+ /* read content of variable directly */
+ d = GetSchemaVariable(op->d.vparam.varoid,
+ &isnull,
+ op->d.vparam.paramtype,
+ false);
+
+ *op->resvalue = d;
+ *op->resnull = isnull;
+ }
+}
+
/*
* Evaluate a SQLValueFunction expression.
*/
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index b797d064b7..a49deb810c 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,9 +43,11 @@
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_publication.h"
+#include "catalog/pg_variable.h"
#include "commands/matview.h"
#include "commands/trigger.h"
#include "executor/execdebug.h"
+#include "executor/svariableReceiver.h"
#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -204,12 +206,18 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
*/
estate->es_queryEnv = queryDesc->queryEnv;
+ /*
+ * Result can be stored in schema variable.
+ */
+ estate->es_result_variable = queryDesc->plannedstmt->resultVariable;
+
/*
* If non-read-only query, set the command ID to mark output tuples with
*/
switch (queryDesc->operation)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
@@ -345,6 +353,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate->es_lastoid = InvalidOid;
sendTuples = (operation == CMD_SELECT ||
+ OidIsValid(estate->es_result_variable) ||
queryDesc->plannedstmt->hasReturning);
if (sendTuples)
@@ -924,6 +933,17 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_num_root_result_relations = 0;
}
+ if (OidIsValid(estate->es_result_variable))
+ {
+ AclResult aclresult;
+ Oid varid = estate->es_result_variable;
+
+ /* Ensure this variable is writeable */
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, schema_variable_get_name(varid));
+ }
+
/*
* Similarly, we have to lock relations selected FOR [KEY] UPDATE/SHARE
* before we initialize the plan tree, else we'd be risking lock upgrades.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5b3eaec80b..eca7805517 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -102,6 +102,7 @@ CreateExecutorState(void)
/*
* Initialize all fields of the Executor State structure
*/
+ estate->es_shared = false;
estate->es_direction = ForwardScanDirection;
estate->es_snapshot = InvalidSnapshot; /* caller must initialize this */
estate->es_crosscheck_snapshot = InvalidSnapshot; /* no crosscheck */
diff --git a/src/backend/executor/svariableReceiver.c b/src/backend/executor/svariableReceiver.c
new file mode 100644
index 0000000000..0eac4b5d0c
--- /dev/null
+++ b/src/backend/executor/svariableReceiver.c
@@ -0,0 +1,145 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.c
+ * An implementation of DestReceiver that stores the result value in
+ * a schema variable.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/svariableReceiver.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/tuptoaster.h"
+#include "executor/svariableReceiver.h"
+#include "commands/schemavariable.h"
+
+typedef struct
+{
+ DestReceiver pub;
+ Oid varid;
+ Oid typid;
+ int32 typmod;
+ int typlen;
+ int slot_offset;
+ int rows;
+} svariableState;
+
+
+/*
+ * Prepare to receive tuples from executor.
+ */
+static void
+svariableStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+ svariableState *myState = (svariableState *) self;
+ int natts = typeinfo->natts;
+ int outcols = 0;
+ int i;
+
+ for (i = 0; i < natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+
+ if (attr->attisdropped)
+ continue;
+
+ if (++outcols > 1)
+ elog(ERROR, "svariable DestReceiver can take only one attribute");
+
+ myState->typid = attr->atttypid;
+ myState->typmod = attr->atttypmod;
+ myState->typlen = attr->attlen;
+ myState->slot_offset = i;
+ }
+
+ myState->rows = 0;
+}
+
+/*
+ * Receive a tuple from the executor and store it in schema variable.
+ */
+static bool
+svariableReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
+{
+ svariableState *myState = (svariableState *) self;
+ Datum value;
+ bool isnull;
+ bool freeval = false;
+
+ /* Make sure the tuple is fully deconstructed */
+ slot_getallattrs(slot);
+
+ value = slot->tts_values[myState->slot_offset];
+ isnull = slot->tts_isnull[myState->slot_offset];
+
+ if (myState->typlen == -1 && !isnull && VARATT_IS_EXTERNAL(DatumGetPointer(value)))
+ {
+ value = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+ DatumGetPointer(value)));
+ freeval = true;
+ }
+
+ SetSchemaVariable(myState->varid, value, isnull, myState->typid, myState->typmod);
+
+ if (freeval)
+ pfree(DatumGetPointer(value));
+
+ return true;
+}
+
+/*
+ * Clean up at end of an executor run
+ */
+static void
+svariableShutdownReceiver(DestReceiver *self)
+{
+ /* Do nothing */
+}
+
+/*
+ * Destroy receiver when done with it
+ */
+static void
+svariableDestroyReceiver(DestReceiver *self)
+{
+ pfree(self);
+}
+
+/*
+ * Initially create a DestReceiver object.
+ */
+DestReceiver *
+CreateVariableDestReceiver(void)
+{
+ svariableState *self = (svariableState *) palloc0(sizeof(svariableState));
+
+ self->pub.receiveSlot = svariableReceiveSlot;
+ self->pub.rStartup = svariableStartupReceiver;
+ self->pub.rShutdown = svariableShutdownReceiver;
+ self->pub.rDestroy = svariableDestroyReceiver;
+ self->pub.mydest = DestVariable;
+
+ /* private fields will be set by SetVariableDestReceiverParams */
+
+ return (DestReceiver *) self;
+}
+
+/*
+ * Set parameters for a VariableDestReceiver
+ */
+void
+SetVariableDestReceiverParams(DestReceiver *self, Oid varid)
+{
+ svariableState *myState = (svariableState *) self;
+
+ Assert(myState->pub.mydest == DestVariable);
+ Assert(OidIsValid(varid));
+
+ myState->varid = varid;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c8220cf65..fcaa2db51a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(nonleafResultRelations);
COPY_NODE_FIELD(rootResultRelations);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks);
@@ -3000,6 +3001,7 @@ _copyQuery(const Query *from)
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_SCALAR_FIELD(hasAggs);
COPY_SCALAR_FIELD(hasWindowFuncs);
COPY_SCALAR_FIELD(hasTargetSRFs);
@@ -3118,6 +3120,18 @@ _copySelectStmt(const SelectStmt *from)
return newnode;
}
+static LetStmt *
+_copyLetStmt(const LetStmt *from)
+{
+ LetStmt *newnode = makeNode(LetStmt);
+
+ COPY_NODE_FIELD(target);
+ COPY_NODE_FIELD(selectStmt);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
static SetOperationStmt *
_copySetOperationStmt(const SetOperationStmt *from)
{
@@ -5166,6 +5180,9 @@ copyObjectImpl(const void *from)
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
+ case T_LetStmt:
+ retval = _copyLetStmt(from);
+ break;
case T_SetOperationStmt:
retval = _copySetOperationStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 378f2facb8..3ec472e19b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -949,6 +949,7 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_SCALAR_FIELD(canSetTag);
COMPARE_NODE_FIELD(utilityStmt);
COMPARE_SCALAR_FIELD(resultRelation);
+ COMPARE_SCALAR_FIELD(resultVariable);
COMPARE_SCALAR_FIELD(hasAggs);
COMPARE_SCALAR_FIELD(hasWindowFuncs);
COMPARE_SCALAR_FIELD(hasTargetSRFs);
@@ -1057,6 +1058,16 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
return true;
}
+static bool
+_equalLetStmt(const LetStmt *a, const LetStmt *b)
+{
+ COMPARE_NODE_FIELD(target);
+ COMPARE_NODE_FIELD(selectStmt);
+
+ return true;
+}
+
+
static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
@@ -3225,6 +3236,9 @@ equal(const void *a, const void *b)
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
break;
+ case T_LetStmt:
+ retval = _equalLetStmt(a, b);
+ break;
case T_SetOperationStmt:
retval = _equalSetOperationStmt(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6269f474d2..46404ff9ac 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -278,6 +278,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(nonleafResultRelations);
WRITE_NODE_FIELD(rootResultRelations);
+ WRITE_OID_FIELD(resultVariable);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks);
@@ -2793,6 +2794,16 @@ _outSelectStmt(StringInfo str, const SelectStmt *node)
WRITE_NODE_FIELD(rarg);
}
+static void
+_outLetStmt(StringInfo str, const LetStmt *node)
+{
+ WRITE_NODE_TYPE("LET");
+
+ WRITE_NODE_FIELD(target);
+ WRITE_NODE_FIELD(selectStmt);
+ WRITE_LOCATION_FIELD(location);
+}
+
static void
_outFuncCall(StringInfo str, const FuncCall *node)
{
@@ -2971,6 +2982,7 @@ _outQuery(StringInfo str, const Query *node)
appendStringInfoString(str, " :utilityStmt <>");
WRITE_INT_FIELD(resultRelation);
+ WRITE_INT_FIELD(resultVariable);
WRITE_BOOL_FIELD(hasAggs);
WRITE_BOOL_FIELD(hasWindowFuncs);
WRITE_BOOL_FIELD(hasTargetSRFs);
@@ -4191,6 +4203,9 @@ outNode(StringInfo str, const void *obj)
case T_SelectStmt:
_outSelectStmt(str, obj);
break;
+ case T_LetStmt:
+ _outLetStmt(str, obj);
+ break;
case T_ColumnDef:
_outColumnDef(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3254524223..4454327549 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -242,6 +242,7 @@ _readQuery(void)
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
+ READ_INT_FIELD(resultVariable);
READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasWindowFuncs);
READ_BOOL_FIELD(hasTargetSRFs);
@@ -1485,6 +1486,7 @@ _readPlannedStmt(void)
READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(nonleafResultRelations);
READ_NODE_FIELD(rootResultRelations);
+ READ_OID_FIELD(resultVariable);
READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index fd06da98b9..01f97f2d86 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -335,7 +335,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
*/
if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
IsUnderPostmaster &&
- parse->commandType == CMD_SELECT &&
+ (parse->commandType == CMD_SELECT ||
+ parse->commandType == CMD_PLAN_UTILITY) &&
!parse->hasModifyingCTE &&
max_parallel_workers_per_gather > 0 &&
!IsParallelWorker() &&
@@ -352,6 +353,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->parallelModeOK = false;
}
+
+
/*
* glob->parallelModeNeeded is normally set to false here and changed to
* true during plan creation if a Gather or Gather Merge plan is actually
@@ -521,6 +524,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->resultRelations = glob->resultRelations;
result->nonleafResultRelations = glob->nonleafResultRelations;
result->rootResultRelations = glob->rootResultRelations;
+ result->resultVariable = parse->resultVariable;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks;
@@ -2167,7 +2171,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
* inheritance_planner, add the ModifyTable node.
*/
- if (parse->commandType != CMD_SELECT && !inheritance_update)
+ if (parse->commandType != CMD_SELECT && parse->commandType != CMD_PLAN_UTILITY && !inheritance_update)
{
List *withCheckOptionLists;
List *returningLists;
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 8603feef2b..2923e3fcc7 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -71,6 +71,7 @@ preprocess_targetlist(PlannerInfo *root)
{
Query *parse = root->parse;
int result_relation = parse->resultRelation;
+ int result_variable = parse->resultVariable;
List *range_table = parse->rtable;
CmdType command_type = parse->commandType;
RangeTblEntry *target_rte = NULL;
@@ -96,6 +97,10 @@ preprocess_targetlist(PlannerInfo *root)
target_relation = heap_open(target_rte->relid, NoLock);
}
+ else if (result_variable)
+ {
+ Assert(command_type == CMD_PLAN_UTILITY);
+ }
else
Assert(command_type == CMD_SELECT);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a04ad6e99e..8f023225c6 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1254,7 +1254,8 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
{
Param *param = (Param *) node;
- if (param->paramkind == PARAM_EXTERN)
+ if (param->paramkind == PARAM_EXTERN ||
+ param->paramkind == PARAM_VARIABLE)
return false;
if (param->paramkind != PARAM_EXEC ||
@@ -4799,7 +4800,7 @@ substitute_actual_parameters_mutator(Node *node,
{
if (node == NULL)
return NULL;
- if (IsA(node, Param))
+ if (IsA(node, Param) && ((Param *) node)->paramkind != PARAM_VARIABLE)
{
Param *param = (Param *) node;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8369e3ad62..fc0cf34c7d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1272,7 +1272,7 @@ get_relation_constraints(PlannerInfo *root,
* descriptor, instead of constraint exclusion which is driven by the
* individual partition's partition constraint.
*/
- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
+ if (enable_partition_pruning && root->parse->commandType != CMD_SELECT && root->parse->commandType != CMD_PLAN_UTILITY)
{
List *pcqual = RelationGetPartitionQual(relation);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c601b6d40d..53ce2435d6 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,7 +25,10 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/namespace.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -44,6 +47,8 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -78,6 +83,8 @@ static Query *transformCreateTableAsStmt(ParseState *pstate,
CreateTableAsStmt *stmt);
static Query *transformCallStmt(ParseState *pstate,
CallStmt *stmt);
+static Query *transformLetStmt(ParseState *pstate,
+ LetStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown);
#ifdef RAW_EXPRESSION_COVERAGE_TEST
@@ -267,6 +274,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
+ case T_LetStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
@@ -327,6 +335,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
(CallStmt *) parseTree);
break;
+ case T_LetStmt:
+ result = transformLetStmt(pstate,
+ (LetStmt *) parseTree);
+ break;
+
default:
/*
@@ -367,6 +380,7 @@ analyze_requires_snapshot(RawStmt *parseTree)
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
+ case T_LetStmt:
result = true;
break;
@@ -1567,6 +1581,203 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
return qry;
}
+/*
+ * transformLetStmt -
+ * transform an Let Statement
+ */
+static Query *
+transformLetStmt(ParseState *pstate, LetStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ List *exprList = NIL;
+ List *exprListCoer = NIL;
+ List *indirection = NIL;
+ ListCell *lc;
+ Query *selectQuery;
+ int i = 0;
+
+ Oid varid;
+
+ ParseExprKind sv_expr_kind;
+ char *attrname = NULL;
+ bool not_unique;
+ bool is_rowtype;
+ Oid typid;
+ int32 typmod;
+
+ AclResult aclresult;
+ List *names = NULL;
+ int indirection_start;
+
+ sv_expr_kind = pstate->p_expr_kind;
+ pstate->p_expr_kind = EXPR_KIND_LET;
+
+ /* There can't be any outer WITH to worry about */
+ Assert(pstate->p_ctenamespace == NIL);
+
+ /* Exec this command as utility */
+ qry->commandType = CMD_PLAN_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ names = NamesFromList(stmt->target);
+
+ varid = identify_variable(names, &attrname, ¬_unique);
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("target \"%s\" of LET command is ambiguous",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ if (!OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("schema variable \"%s\" doesn't exists",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ qry->resultVariable = varid;
+
+ get_schema_variable_type_typmod(varid, &typid, &typmod);
+
+ is_rowtype = type_is_rowtype(typid);
+
+ if (attrname && !is_rowtype)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("target variable \"%s\" is not row type",
+ schema_variable_get_name(varid)),
+ parser_errposition(pstate, stmt->location)));
+
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, NameListToString(names));
+
+ selectQuery = transformStmt(pstate, stmt->selectStmt);
+
+ /* The grammar should have produced a SELECT */
+ if (!IsA(selectQuery, Query) ||
+ selectQuery->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in LET ... SELECT");
+
+ /*----------
+ * Generate an expression list for the LET that selects all the
+ * non-resjunk columns from the subquery.
+ *----------
+ */
+ exprList = NIL;
+ foreach(lc, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->resjunk)
+ continue;
+
+ exprList = lappend(exprList, tle->expr);
+ }
+
+ /*
+ * Because doesn't support pattern matching, don't allow multicolumn result
+ */
+ if (list_length(exprList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expression is not scalar value"),
+ parser_errposition(pstate,
+ exprLocation((Node *) exprList))));
+
+ indirection_start = list_length(names) - (attrname ? 1 : 0);
+ indirection = list_copy_tail(stmt->target, indirection_start);
+
+ exprListCoer = NIL;
+ foreach(lc, exprList)
+ {
+ Node *orig_expr = (Node*) lfirst(lc);
+ Oid exprtypid = exprType((Node *) orig_expr);
+ Param *param = makeNode(Param);
+ Expr *expr = NULL;
+
+ param->paramkind = PARAM_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (indirection != NULL)
+ {
+ bool targetIsArray;
+ char *targetName;
+
+ targetName = attrname != NULL ? attrname : get_schema_variable_name(varid);
+ targetIsArray = OidIsValid(get_element_type(typid));
+
+ expr = (Expr *)
+ transformAssignmentIndirection(pstate,
+ (Node *) param,
+ targetName,
+ targetIsArray,
+ typid,
+ typmod,
+ InvalidOid,
+ list_head(indirection),
+ (Node *) orig_expr,
+ stmt->location);
+ }
+ else
+ expr = (Expr *)
+ coerce_to_target_type(pstate,
+ (Node *) orig_expr,
+ exprtypid,
+ typid, typmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ stmt->location);
+
+ if (expr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable \"%s\" is of type %s,"
+ " but expression is of type %s",
+ schema_variable_get_name(varid),
+ format_type_be(typid),
+ format_type_be(exprtypid)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *) orig_expr))));
+
+ exprListCoer = lappend(exprListCoer, expr);
+ }
+
+ /*
+ * Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
+ */
+ qry->targetList = NIL;
+ foreach(lc, exprListCoer)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ tle = makeTargetEntry(expr,
+ i + 1,
+ FigureColname((Node *)expr),
+ false);
+ qry->targetList = lappend(qry->targetList, tle);
+ }
+
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+ qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+
+ assign_query_collations(pstate, qry);
+
+ pstate->p_expr_kind = sv_expr_kind;
+
+ return qry;
+}
+
+
/*
* transformSetOperationStmt -
* transforms a set-operations tree
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 87f5e95827..25036669c1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -257,8 +257,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSchemaVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -268,7 +268,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DropTransformStmt
DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
- ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
+ LetStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -400,6 +400,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
TriggerTransitions TriggerReferencing
publication_name_list
vacuum_relation_list opt_vacuum_relation_list
+ let_target
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -584,6 +585,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> optSchemaVarDefExpr
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -649,7 +651,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+ LEADING LEAKPROOF LEAST LEFT LET LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -687,8 +689,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -878,6 +880,7 @@ stmt :
| CreatePolicyStmt
| CreatePLangStmt
| CreateSchemaStmt
+ | CreateSchemaVarStmt
| CreateSeqStmt
| CreateStmt
| CreateSubscriptionStmt
@@ -917,6 +920,7 @@ stmt :
| ImportForeignSchemaStmt
| IndexStmt
| InsertStmt
+ | LetStmt
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
@@ -1808,7 +1812,12 @@ DiscardStmt:
n->target = DISCARD_SEQUENCES;
$$ = (Node *) n;
}
-
+ | DISCARD VARIABLES
+ {
+ DiscardStmt *n = makeNode(DiscardStmt);
+ n->target = DISCARD_VARIABLES;
+ $$ = (Node *) n;
+ }
;
@@ -4479,6 +4488,42 @@ create_extension_opt_item:
}
;
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE VARIABLE varname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSchemaVarStmt:
+ CREATE OptTemp VARIABLE qualified_name opt_as Typename optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $4->relpersistence = $2;
+ n->variable = $4;
+ n->typeName = $6;
+ n->defexpr = $7;
+ n->if_not_exists = false;
+ $$ = (Node *) n;
+ }
+ | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $7->relpersistence = $2;
+ n->variable = $7;
+ n->typeName = $9;
+ n->defexpr = $10;
+ n->if_not_exists = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+optSchemaVarDefExpr: DEFAULT b_expr { $$ = $2; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+
+
/*****************************************************************************
*
* ALTER EXTENSION name UPDATE [ TO version ]
@@ -6335,6 +6380,7 @@ drop_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name_list */
@@ -6604,6 +6650,7 @@ comment_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -6742,6 +6789,7 @@ security_label_type_any_name:
| TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -7163,6 +7211,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| ALL TABLES IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7203,6 +7259,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
;
@@ -7363,6 +7427,7 @@ defacl_privilege_target:
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
;
@@ -8959,6 +9024,25 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newname = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
opt_column: COLUMN { $$ = COLUMN; }
@@ -9277,6 +9361,25 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newschema = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
/*****************************************************************************
@@ -9512,6 +9615,14 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
@@ -10693,6 +10804,7 @@ ExplainableStmt:
| CreateMatViewStmt
| RefreshMatViewStmt
| ExecuteStmt /* by default all are $$=$1 */
+ | LetStmt
;
explain_option_list:
@@ -10750,6 +10862,7 @@ PreparableStmt:
| InsertStmt
| UpdateStmt
| DeleteStmt /* by default all are $$=$1 */
+ | LetStmt
;
/*****************************************************************************
@@ -11148,6 +11261,44 @@ opt_hold: /* EMPTY */ { $$ = 0; }
| WITHOUT HOLD { $$ = 0; }
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * LET STATEMENTS
+ *
+ *****************************************************************************/
+LetStmt: LET let_target '=' a_expr
+ {
+ LetStmt *n = makeNode(LetStmt);
+ SelectStmt *select = makeNode(SelectStmt);
+ ResTarget *res = makeNode(ResTarget);
+
+ n->target = $2;
+
+ /* Create target list for implicit query */
+ res->name = NULL;
+ res->indirection = NIL;
+ res->val = (Node *) $4;
+ res->location = @4;
+
+ select->targetList = list_make1(res);
+ n->selectStmt = (Node *) select;
+
+ n->location = @2;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+let_target:
+ ColId opt_indirection
+ {
+ $$ = list_make1(makeString($1));
+ if ($2)
+ $$ = list_concat($$,
+ check_indirection($2, yyscanner));
+ }
+
/*****************************************************************************
*
* QUERY:
@@ -15127,6 +15278,7 @@ unreserved_keyword:
| LARGE_P
| LAST_P
| LEAKPROOF
+ | LET
| LEVEL
| LISTEN
| LOAD
@@ -15275,6 +15427,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 61727e1d71..6823612fba 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -349,6 +349,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
Assert(false); /* can't happen */
break;
case EXPR_KIND_OTHER:
+ case EXPR_KIND_LET:
/*
* Accept aggregate/grouping here; caller must throw error if
@@ -465,6 +466,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
if (isAgg)
err = _("aggregate functions are not allowed in DEFAULT expressions");
@@ -879,6 +881,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("window functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -902,6 +905,8 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_CALL_ARGUMENT:
err = _("window functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("window functions are not allowed in LET statement");
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a9b6..6ea194b563 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -37,6 +38,7 @@
#include "utils/date.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
+#include "utils/typcache.h"
#include "utils/xml.h"
@@ -116,6 +118,9 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
+static Node *makeParamSchemaVariable(ParseState *pstate,
+ Oid varid, Oid typid, int32 typmod,
+ char *attrname, int location);
static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
int location);
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
@@ -512,6 +517,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
+ Oid varid = InvalidOid;
+ char *attrname = NULL;
+ bool not_unique;
+
RangeTblEntry *rte;
int levels_up;
enum
@@ -749,6 +758,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break;
}
+ varid = identify_variable(cref->fields, &attrname, ¬_unique);
+
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("schema variable reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+
/*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
@@ -773,6 +791,71 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
parser_errposition(pstate, cref->location)));
}
+ if (OidIsValid(varid))
+ {
+ Oid typid;
+ int32 typmod;
+
+ get_schema_variable_type_typmod(varid, &typid, &typmod);
+
+ if (node != NULL)
+ {
+ /*
+ * some collision can be solved simply here to reduce errors
+ * based on simply existence of some variables. Often error
+ * can be using alias same like variable name. In this case,
+ * when we found column reference, and we found reference to
+ * possible composite variable, but the variable is not composite,
+ * then we can ignore the variable as simply improper, and we
+ * use column reference only.
+ */
+ if (attrname)
+ {
+ if (type_is_rowtype(typid))
+ {
+ TupleDesc tupdesc;
+ bool found = false;
+ int i;
+
+ /* slow part, I hope it will not be to often */
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ if (namestrcmp(&(TupleDescAttr(tupdesc, i)->attname), attrname) == 0 &&
+ !TupleDescAttr(tupdesc, i)->attisdropped)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ FreeTupleDesc(tupdesc);
+
+ /* there are not composite variable with this field */
+ if (!found)
+ varid = InvalidOid;
+ }
+ else
+ /* there are not composite variable with this name */
+ varid = InvalidOid;
+ }
+
+ /* Raise error if varid is still valid. It should be really amigonuous */
+ if (OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ errdetail("The qualified identifier can be column reference or schema variable reference"),
+ parser_errposition(pstate, cref->location)));
+ }
+
+ if (OidIsValid(varid))
+ node = makeParamSchemaVariable(pstate,
+ varid, typid, typmod,
+ attrname, cref->location);
+ }
+
/*
* Throw error if no translation found.
*/
@@ -807,6 +890,59 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return node;
}
+/*
+ * Generate param variable for reference to schema variable
+ */
+static Node *
+makeParamSchemaVariable(ParseState *pstate, Oid varid, Oid typid, int32 typmod, char *attrname, int location)
+{
+ Param *param;
+
+ param = makeNode(Param);
+
+ param->paramkind = PARAM_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (attrname != NULL)
+ {
+ TupleDesc tupdesc;
+ int i;
+
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+
+ if (strcmp(attrname, NameStr(att->attname)) == 0 &&
+ !att->attisdropped)
+ {
+ /* Success, so generate a FieldSelect expression */
+ FieldSelect *fselect = makeNode(FieldSelect);
+
+ fselect->arg = (Expr *) param;
+ fselect->fieldnum = i + 1;
+ fselect->resulttype = att->atttypid;
+ fselect->resulttypmod = att->atttypmod;
+ /* save attribute's collation for parse_collate.c */
+ fselect->resultcollid = att->attcollation;
+
+ ReleaseTupleDesc(tupdesc);
+ return (Node *) fselect;
+ }
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("could not identify column \"%s\" in variable", attrname),
+ parser_errposition(pstate, location)));
+ }
+
+ return (Node *) param;
+}
+
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
@@ -1818,6 +1954,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
case EXPR_KIND_VALUES_SINGLE:
+ case EXPR_KIND_LET:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
@@ -1826,6 +1963,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -3460,6 +3598,7 @@ ParseExprKindName(ParseExprKind exprKind)
return "CHECK";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
return "DEFAULT";
case EXPR_KIND_INDEX_EXPRESSION:
return "index expression";
@@ -3475,6 +3614,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "PARTITION BY";
case EXPR_KIND_CALL_ARGUMENT:
return "CALL";
+ case EXPR_KIND_LET:
+ return "LET";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 44257154b8..b2c9900e00 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2347,6 +2347,7 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("set-returning functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -2370,6 +2371,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_CALL_ARGUMENT:
err = _("set-returning functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("set-returning functions are not allowed in CALL arguments");
+ break;
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4932e58022..c60fe011f7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -35,16 +35,6 @@
static void markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
Var *var, int levelsup);
-static Node *transformAssignmentIndirection(ParseState *pstate,
- Node *basenode,
- const char *targetName,
- bool targetIsArray,
- Oid targetTypeId,
- int32 targetTypMod,
- Oid targetCollation,
- ListCell *indirection,
- Node *rhs,
- int location);
static Node *transformAssignmentSubscripts(ParseState *pstate,
Node *basenode,
const char *targetName,
@@ -672,7 +662,7 @@ updateTargetListEntry(ParseState *pstate,
* might want to decorate indirection cells with their own location info,
* in which case the location argument could probably be dropped.)
*/
-static Node *
+Node *
transformAssignmentIndirection(ParseState *pstate,
Node *basenode,
const char *targetName,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 3123ee274d..10737d422d 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3350,7 +3350,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
* get executed. Also, utilities aren't rewritten at all (do we still
* need that check?)
*/
- if (event != CMD_SELECT && event != CMD_UTILITY)
+ if (event != CMD_SELECT && event != CMD_UTILITY && event != CMD_PLAN_UTILITY)
{
int result_relation;
RangeTblEntry *rt_entry;
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 61ef396d8a..6a068af799 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -212,7 +212,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
}
/*
- * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
+ * For SELECT, LET, UPDATE and DELETE, add security quals to enforce the USING
* policies. These security quals control access to existing table rows.
* Restrictive policies are combined together using AND, and permissive
* policies are combined together using OR.
@@ -222,6 +222,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
&restrictive_policies);
if (commandType == CMD_SELECT ||
+ commandType == CMD_PLAN_UTILITY ||
commandType == CMD_UPDATE ||
commandType == CMD_DELETE)
add_security_quals(rt_index,
@@ -423,6 +424,7 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
switch (cmd)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
if (policy->polcmd == ACL_SELECT_CHR)
cmd_matches = true;
break;
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index c95a4d519d..47fb0f38b1 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -37,6 +37,7 @@
#include "executor/functions.h"
#include "executor/tqueue.h"
#include "executor/tstoreReceiver.h"
+#include "executor/svariableReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/portal.h"
@@ -143,6 +144,9 @@ CreateDestReceiver(CommandDest dest)
case DestTupleQueue:
return CreateTupleQueueDestReceiver(NULL);
+
+ case DestVariable:
+ return CreateVariableDestReceiver();
}
/* should never get here */
@@ -178,6 +182,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -222,6 +227,7 @@ NullCommand(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -268,6 +274,7 @@ ReadyForQuery(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b5804f64ad..35199fd0dc 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
@@ -344,7 +345,7 @@ ProcessUtility(PlannedStmt *pstmt,
char *completionTag)
{
Assert(IsA(pstmt, PlannedStmt));
- Assert(pstmt->commandType == CMD_UTILITY);
+ Assert(pstmt->commandType == CMD_UTILITY || pstmt->commandType == CMD_PLAN_UTILITY);
Assert(queryString != NULL); /* required as of 8.4 */
/*
@@ -915,6 +916,14 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
}
+ case T_LetStmt:
+ {
+ doLetStmt(pstmt, params, queryEnv, queryString);
+ if (completionTag)
+ strcpy(completionTag, "LET");
+ }
+ break;
+
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
@@ -1221,6 +1230,10 @@ ProcessUtilitySlow(ParseState *pstate,
}
break;
+ case T_CreateSchemaVarStmt:
+ address = DefineSchemaVariable(pstate, (CreateSchemaVarStmt *) parsetree);
+ break;
+
/*
* ************* object creation / destruction **************
*/
@@ -2055,6 +2068,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_STATISTIC_EXT:
tag = "ALTER STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "ALTER VARIABLE";
+ break;
default:
tag = "???";
break;
@@ -2104,6 +2120,10 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ case T_LetStmt:
+ tag = "LET";
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
@@ -2358,6 +2378,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "DROP VARIABLE";
+ break;
default:
tag = "???";
}
@@ -2639,6 +2662,9 @@ CreateCommandTag(Node *parsetree)
case DISCARD_SEQUENCES:
tag = "DISCARD SEQUENCES";
break;
+ case DISCARD_VARIABLES:
+ tag = "DISCARD VARIABLES";
+ break;
default:
tag = "???";
}
@@ -2844,6 +2870,7 @@ CreateCommandTag(Node *parsetree)
tag = "DELETE";
break;
case CMD_UTILITY:
+ case CMD_PLAN_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
default:
@@ -2915,6 +2942,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_CreateSchemaVarStmt:
+ tag = "CREATE VARIABLE";
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -2961,6 +2992,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ case T_LetStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a45e093de7..952c0d9628 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -315,6 +315,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_READ_CHR:
+ read = ACL_READ;
+ break;
+ case ACL_WRITE_CHR:
+ read = ACL_WRITE;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -808,6 +814,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -903,6 +913,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'T':
objtype = OBJECT_TYPE;
break;
+ case 'V':
+ objtype = OBJECT_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
}
@@ -1627,6 +1640,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CONNECT;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
+ if (pg_strcasecmp(priv_type, "READ") == 0)
+ return ACL_READ;
+ if (pg_strcasecmp(priv_type, "WRITE") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1721,6 +1738,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 03e9a28a63..cc8f3326ac 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -38,6 +38,7 @@
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
#include "common/keywords.h"
@@ -7362,6 +7363,14 @@ get_parameter(Param *param, deparse_context *context)
return;
}
+ /* translate paramid to original schema variable name */
+ if (param->paramkind == PARAM_VARIABLE)
+ {
+ appendStringInfo(context->buf, "%s",
+ schema_variable_get_name(param->paramid));
+ return;
+ }
+
/*
* Not PARAM_EXEC, or couldn't find referent: just print $N.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bba595ad1d..858a6dd4be 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1691,6 +1691,18 @@ get_relname_relid(const char *relname, Oid relnamespace)
ObjectIdGetDatum(relnamespace));
}
+/*
+ * get_varname_varid
+ * Given name and namespace of variable, look up the OID.
+ */
+Oid
+get_varname_varid(const char *varname, Oid varnamespace)
+{
+ return GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(varnamespace));
+}
+
#ifdef NOT_USED
/*
* get_relnatts
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b381782a3..35dc32f649 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -73,6 +73,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "utils/rel.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
@@ -968,6 +969,28 @@ static const struct cachedesc cacheinfo[] = {
0
},
2
+ },
+ {VariableRelationId, /* VARIABLENAMENSP */
+ VariableNameNspIndexId,
+ 2,
+ {
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ 0,
+ 0
+ },
+ 8
+ },
+ {VariableRelationId, /* VARIABLEOID */
+ VariableObjectIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
}
};
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 0d147cb08d..6d97931d85 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -296,6 +296,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading variables\n");
+ getVariables(fout);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 83c976eaf7..c9bc91ca68 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3471,6 +3471,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(type, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(type, "STATISTICS") == 0 ||
+ strcmp(type, "VARIABLE") == 0 ||
/* non-schema-specified objects */
strcmp(type, "DATABASE") == 0 ||
strcmp(type, "PROCEDURAL LANGUAGE") == 0 ||
@@ -3670,7 +3671,8 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
strcmp(te->desc, "SERVER") == 0 ||
strcmp(te->desc, "STATISTICS") == 0 ||
strcmp(te->desc, "PUBLICATION") == 0 ||
- strcmp(te->desc, "SUBSCRIPTION") == 0)
+ strcmp(te->desc, "SUBSCRIPTION") == 0 ||
+ strcmp(te->desc, "VARIABLE") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 9baf7b2fde..f825a00c9d 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -260,6 +260,7 @@ static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
+static void dumpVariable(Archive *fout, VariableInfo *varinfo);
static void dumpDatabase(Archive *AH);
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
const char *dbname, Oid dboid);
@@ -4221,6 +4222,208 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+/*
+ * getVariables
+ * get information about variables
+ */
+void
+getVariables(Archive *fout)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer init_acl_subquery = createPQExpBuffer();
+ PQExpBuffer init_racl_subquery = createPQExpBuffer();
+ PGresult *res;
+ VariableInfo *varinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_varname;
+ int i_varnamespace;
+ int i_vartype;
+ int i_vartypname;
+ int i_vardefexpr;
+ int i_rolname;
+ int i_varacl;
+ int i_rvaracl;
+ int i_initvaracl;
+ int i_initrvaracl;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion <= 110000)
+ return;
+
+ acl_subquery = createPQExpBuffer();
+ racl_subquery = createPQExpBuffer();
+ init_acl_subquery = createPQExpBuffer();
+ init_racl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
+ init_racl_subquery, "v.varacl", "v.varowner", "'V'",
+ dopt->binary_upgrade);
+
+ query = createPQExpBuffer();
+
+ resetPQExpBuffer(query);
+
+ /* Get the variables in current database. */
+ appendPQExpBuffer(query,
+ "SELECT v.tableoid, v.oid, v.varname, "
+ "v.varnamespace,"
+ "(%s varowner) AS rolname, "
+ "%s as varacl, "
+ "%s as rvaracl, "
+ "%s as initvaracl, "
+ "%s as initrvaracl, "
+ "v.vartype, "
+ "pg_catalog.format_type(v.vartype, v.vartypmod) as vartypname, "
+ "pg_catalog.pg_get_expr(v.vardefexpr,0) as vardefexpr "
+ "FROM pg_variable v "
+ "LEFT JOIN pg_init_privs pip "
+ "ON (v.oid = pip.objoid "
+ "AND pip.classoid = 'pg_variable'::regclass "
+ "AND pip.objsubid = 0)",
+ username_subquery,
+ acl_subquery->data,
+ racl_subquery->data,
+ init_acl_subquery->data,
+ init_racl_subquery->data);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(init_acl_subquery);
+ destroyPQExpBuffer(init_racl_subquery);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_varname = PQfnumber(res, "varname");
+ i_varnamespace = PQfnumber(res, "varnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_vartype = PQfnumber(res, "vartype");
+ i_vartypname = PQfnumber(res, "vartypname");
+ i_vardefexpr = PQfnumber(res, "vardefexpr");
+ i_varacl = PQfnumber(res, "varacl");
+ i_rvaracl = PQfnumber(res, "rvaracl");
+ i_initvaracl = PQfnumber(res, "initvaracl");
+ i_initrvaracl = PQfnumber(res, "initrvaracl");
+
+ varinfo = pg_malloc(ntups * sizeof(VariableInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ TypeInfo *vtype;
+
+ varinfo[i].dobj.objType = DO_VARIABLE;
+ varinfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ varinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&varinfo[i].dobj);
+ varinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_varname));
+ varinfo[i].dobj.namespace =
+ findNamespace(fout,
+ atooid(PQgetvalue(res, i, i_varnamespace)));
+
+ varinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ varinfo[i].vartype = atooid(PQgetvalue(res, i, i_vartype));
+ varinfo[i].vartypname = pg_strdup(PQgetvalue(res, i, i_vartypname));
+
+ varinfo[i].varacl = pg_strdup(PQgetvalue(res, i, i_varacl));
+ varinfo[i].rvaracl = pg_strdup(PQgetvalue(res, i, i_rvaracl));
+ varinfo[i].initvaracl = pg_strdup(PQgetvalue(res, i, i_initvaracl));
+ varinfo[i].initrvaracl = pg_strdup(PQgetvalue(res, i, i_initrvaracl));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ /* Do not try to dump ACL if no ACL exists. */
+ if (PQgetisnull(res, i, i_varacl) && PQgetisnull(res, i, i_rvaracl) &&
+ PQgetisnull(res, i, i_initvaracl) &&
+ PQgetisnull(res, i, i_initrvaracl))
+ varinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+
+ if (PQgetisnull(res, i, i_vardefexpr))
+ varinfo[i].vardefexpr = NULL;
+ else
+ varinfo[i].vardefexpr = pg_strdup(PQgetvalue(res, i, i_vardefexpr));
+
+ if (strlen(varinfo[i].rolname) == 0)
+ write_msg(NULL, "WARNING: owner of variable \"%s\" appears to be invalid\n",
+ varinfo[i].dobj.name);
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ vtype = findTypeByOid(varinfo[i].vartype);
+ addObjectDependency(&varinfo[i].dobj, vtype->dobj.dumpId);
+ }
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpVariable
+ * dump the definition of the given variables
+ */
+static void
+dumpVariable(Archive *fout, VariableInfo *varinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ const char *varname;
+ const char *vartypname;
+ const char *vardefexpr;
+
+ /* Skip if not to be dumped */
+ if (!varinfo->dobj.dump || dopt->dataOnly)
+ return;
+
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ varname = fmtQualifiedDumpable(varinfo);
+ vartypname = varinfo->vartypname;
+ vardefexpr = varinfo->vardefexpr;
+
+ appendPQExpBuffer(delq, "DROP VARIABLE %s;\n",
+ varname);
+
+ appendPQExpBuffer(query, "CREATE VARIABLE %s AS %s",
+ varname, vartypname);
+
+ if (vardefexpr)
+ appendPQExpBuffer(query, " DEFAULT %s",
+ vardefexpr);
+
+ appendPQExpBuffer(query, ";\n");
+
+ ArchiveEntry(fout, varinfo->dobj.catId, varinfo->dobj.dumpId,
+ varinfo->dobj.name,
+ NULL,
+ NULL,
+ varinfo->rolname, false,
+ "VARIABLE", SECTION_PRE_DATA,
+ query->data, delq->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ if (varinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, "VARIABLE", varname,
+ NULL, varinfo->rolname,
+ varinfo->dobj.catId, 0, varinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -9849,6 +10052,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_VARIABLE:
+ dumpVariable(fout, (VariableInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -17935,6 +18141,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_OPFAMILY:
case DO_COLLATION:
case DO_CONVERSION:
+ case DO_VARIABLE:
case DO_TABLE:
case DO_ATTRDEF:
case DO_PROCLANG:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 1448005f30..0d49bb7ed7 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -84,7 +84,8 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_VARIABLE
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -625,6 +626,22 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/*
+ * The VariableInfo struct is used to represent schema variables
+ */
+typedef struct _VariableInfo
+{
+ DumpableObject dobj;
+ Oid vartype;
+ char *vartypname;
+ char *rolname; /* name of owner, or empty string */
+ char *vardefexpr;
+ char *varacl;
+ char *rvaracl;
+ char *initvaracl;
+ char *initrvaracl;
+} VariableInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -725,5 +742,6 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern void getVariables(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 6227a8fd26..969a021771 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -1477,6 +1477,10 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POST-DATA BOUNDARY (ID %d)",
obj->dumpId);
return;
+ case DO_VARIABLE:
+ snprintf(buf, bufsize,
+ "VARIABLE %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
}
/* shouldn't get here */
snprintf(buf, bufsize,
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index ec751a7c23..2a67766ed4 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2601,6 +2601,38 @@ my %tests = (
},
},
+ 'CREATE VARIABLE test_variable' => {
+ all_runs => 1,
+ catch_all => 'CREATE ... commands',
+ create_order => 61,
+ create_sql => 'CREATE VARIABLE dump_test.variable AS integer DEFAULT 0;',
+ regexp => qr/^
+ \QCREATE VARIABLE dump_test.variable AS integer DEFAULT 0;\E/xm,
+ like => {
+ binary_upgrade => 1,
+ clean => 1,
+ clean_if_exists => 1,
+ createdb => 1,
+ defaults => 1,
+ exclude_test_table => 1,
+ exclude_test_table_data => 1,
+ no_blobs => 1,
+ no_privs => 1,
+ no_owner => 1,
+ only_dump_test_schema => 1,
+ pg_dumpall_dbprivs => 1,
+ schema_only => 1,
+ section_pre_data => 1,
+ test_schema_plus_blobs => 1,
+ with_oids => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_test_table => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1,
+ role => 1,
+ section_post_data => 1, }, },
+
'CREATE VIEW test_view' => {
create_order => 61,
create_sql => 'CREATE VIEW dump_test.test_view
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..73a752fd7e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -853,6 +853,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
break;
}
break;
+ case 'V': /* Variables */
+ success = listVariables(pattern, show_verbose);
+ break;
case 'x': /* Extensions */
if (show_verbose)
success = listExtensionContents(pattern);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 80d8338b96..d645bba7af 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4178,6 +4178,80 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
return true;
}
+/*
+ * \dV
+ *
+ * listVariables()
+ */
+bool
+listVariables(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false, false, false, false, false};
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname as \"%s\",\n"
+ " v.varname as \"%s\",\n"
+ " pg_catalog.format_type(v.vartype, v.vartypmod) as \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(v.varowner) as \"%s\",\n"
+ " pg_catalog.pg_get_expr(v.vardefexpr, 0) as \"%s\"",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Type"),
+ gettext_noop("Owner"),
+ gettext_noop("Default"));
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_variable v"
+ "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = v.varnamespace");
+
+ appendPQExpBufferStr(&buf, "\nWHERE true\n");
+ if (!pattern)
+ appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
+ " AND n.nspname <> 'information_schema'\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "v.varname", NULL,
+ "pg_catalog.pg_variable_is_visible(v.oid)");
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ /*
+ * Most functions in this file are content to print an empty table when
+ * there are no matching objects. We intentionally deviate from that
+ * here, but only in !quiet mode, for historical reasons.
+ */
+ if (PQntuples(res) == 0 && !pset.quiet)
+ {
+ if (pattern)
+ psql_error("Did not find any schema variable named \"%s\".\n",
+ pattern);
+ else
+ psql_error("Did not find any schema variables.\n");
+ }
+ else
+ {
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of variables");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+ }
+
+ PQclear(res);
+ return true;
+}
/*
* \dFp
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..ecc4e3a531 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
/* \dt, \di, \ds, \dS, etc. */
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
+/* \dV */
+extern bool listVariables(const char *pattern, bool varbose);
+
/* \dD */
extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 316030d358..adcc36cb6e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
@@ -257,6 +257,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[S+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dV [PATTERN] list variables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index bb696f8ee9..a7583810e8 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -805,6 +805,22 @@ static const SchemaQuery Query_for_list_of_statistics = {
NULL
};
+static const SchemaQuery Query_for_list_of_variables = {
+ /* min_server_version */
+ 0,
+ /* catname */
+ "pg_catalog.pg_variable v",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_variable_is_visible(v.oid)",
+ /* namespace */
+ "v.varnamespace",
+ /* result */
+ "pg_catalog.quote_ident(v.varname)",
+ /* qualresult */
+ NULL
+};
/*
* Queries to get lists of names of various kinds of things, possibly
@@ -1249,6 +1265,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
{"USER MAPPING FOR", NULL, NULL, NULL},
+ {"VARIABLE", NULL, NULL, &Query_for_list_of_variables},
{"VIEW", NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
@@ -1604,7 +1621,7 @@ psql_completion(const char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
- "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LET", "LISTEN", "LOAD", "LOCK",
"MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
"RESET", "REVOKE", "ROLLBACK",
@@ -1621,9 +1638,9 @@ psql_completion(const char *text, int start, int end)
"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
- "\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+ "\\dm", "\\dn", "\\do", "\\dO", "\\dp"
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+ "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", "\\dV",
"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
"\\endif", "\\errverbose", "\\ev",
"\\f",
@@ -1988,6 +2005,9 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
else if (Matches4("ALTER", "SYSTEM", "SET", MatchAny))
COMPLETE_WITH_CONST("TO");
+ /* ALTER VARIABLE <name> */
+ else if (Matches3("ALTER", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
/* ALTER VIEW <name> */
else if (Matches3("ALTER", "VIEW", MatchAny))
COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
@@ -2837,6 +2857,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
COMPLETE_WITH_LIST2("GROUP", "ROLE");
+/* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
+ /* Complete CREATE VARIABLE <name> with AS */
+ else if (TailMatches3("CREATE", "VARIABLE", MatchAny))
+ COMPLETE_WITH_CONST("AS");
+ /* Complete CREATE VARIABLE <name> with AS types*/
+ else if (TailMatches4("CREATE", "VARIABLE", MatchAny, "AS"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE VIEW <name> with AS */
else if (TailMatches3("CREATE", "VIEW", MatchAny))
@@ -2890,7 +2918,7 @@ psql_completion(const char *text, int start, int end)
/* DISCARD */
else if (Matches1("DISCARD"))
- COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP");
+ COMPLETE_WITH_LIST5("ALL", "PLANS", "SEQUENCES", "TEMP", "VARIABLES");
/* DO */
else if (Matches1("DO"))
@@ -2992,6 +3020,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP VARIABLE */
+ else if (Matches2("DROP", "VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ else if (Matches3("DROP", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3002,14 +3036,14 @@ psql_completion(const char *text, int start, int end)
* Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "ANALYZE", "VERBOSE");
+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "ANALYZE", "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "ANALYZE"))
- COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "VERBOSE");
+ COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "VERBOSE") ||
Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
- COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
+ COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "LET");
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
@@ -3118,6 +3152,7 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'ALL ROUTINES IN SCHEMA'"
" UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
" UNION SELECT 'ALL TABLES IN SCHEMA'"
+ " UNION SELECT 'ALL VARIABLES IN SCHEMA'"
" UNION SELECT 'DATABASE'"
" UNION SELECT 'DOMAIN'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
@@ -3131,14 +3166,16 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'SEQUENCE'"
" UNION SELECT 'TABLE'"
" UNION SELECT 'TABLESPACE'"
- " UNION SELECT 'TYPE'");
+ " UNION SELECT 'TYPE'"
+ " UNION SELECT 'VARIABLE'");
}
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
- COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA",
+ COMPLETE_WITH_LIST6("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
- "TABLES IN SCHEMA");
+ "TABLES IN SCHEMA",
+ "VARIABLES IN SCHEMA");
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
@@ -3172,6 +3209,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches1("TYPE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+ else if (TailMatches1("VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
COMPLETE_WITH_CONST("TO");
else
@@ -3324,7 +3363,7 @@ psql_completion(const char *text, int start, int end)
/* PREPARE xx AS */
else if (Matches3("PREPARE", MatchAny, "AS"))
- COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
+ COMPLETE_WITH_LIST5("SELECT", "UPDATE", "INSERT", "DELETE FROM", "LET");
/*
* PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -3547,6 +3586,14 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
COMPLETE_WITH_CONST("=");
+/* LET --- can be inside EXPLAIN, PREPARE etc */
+ /* If prev. word is LET suggest a list of variables */
+ else if (TailMatches1("LET"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ /* Complete LET <variable> with "=" */
+ else if (TailMatches2("LET", MatchAny))
+ COMPLETE_WITH_CONST("=");
+
/* USER MAPPING */
else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 46c271a46c..3e38a05e55 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -180,7 +180,8 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_VARIABLE /* pg_variable */
} ObjectClass;
#define LAST_OCLASS OCLASS_TRANSFORM
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 24915824ca..dae80c20a8 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -360,4 +360,10 @@ DECLARE_UNIQUE_INDEX(pg_subscription_subname_index, 6115, on pg_subscription usi
DECLARE_UNIQUE_INDEX(pg_subscription_rel_srrelid_srsubid_index, 6117, on pg_subscription_rel using btree(srrelid oid_ops, srsubid oid_ops));
#define SubscriptionRelSrrelidSrsubidIndexId 6117
+DECLARE_UNIQUE_INDEX(pg_variable_oid_index, 4288, on pg_variable using btree(oid oid_ops));
+#define VariableObjectIndexId 4288
+
+DECLARE_UNIQUE_INDEX(pg_variable_varname_nsp_index, 4289, on pg_variable using btree(varname name_ops, varnamespace oid_ops));
+#define VariableNameNspIndexId 4289
+
#endif /* INDEXING_H */
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 7991de5e21..75068d7e92 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -75,10 +75,13 @@ extern Oid RangeVarGetAndCheckCreationNamespace(RangeVar *newRelation,
extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
+extern bool VariableIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
+extern bool VariableIsVisible(Oid varid);
+
extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
bool expand_variadic,
@@ -145,6 +148,10 @@ extern void SetTempNamespaceState(Oid tempNamespaceId,
Oid tempToastNamespaceId);
extern void ResetTempTableNamespace(void);
+extern List *NamesFromList(List *names);
+extern Oid lookup_variable(const char *nspname, const char *varname, bool missing_ok);
+extern Oid identify_variable(List *names, char **attrname, bool *not_uniq);
+
extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
extern OverrideSearchPath *CopyOverrideSearchPath(OverrideSearchPath *path);
extern bool OverrideSearchPathMatchesCurrent(OverrideSearchPath *path);
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index d0410f5586..56deef1a45 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -57,6 +57,7 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a14651010f..61cbe65805 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5961,6 +5961,9 @@
proname => 'pg_collation_is_visible', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid',
prosrc => 'pg_collation_is_visible' },
+{ oid => '4187', descr => 'is schema variable visible in search path?',
+ proname => 'pg_variable_is_visible', procost => '10', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid', prosrc => 'pg_variable_is_visible' },
{ oid => '2854', descr => 'get OID of current session\'s temp schema, if any',
proname => 'pg_my_temp_schema', provolatile => 's', proparallel => 'r',
diff --git a/src/include/catalog/pg_variable.h b/src/include/catalog/pg_variable.h
new file mode 100644
index 0000000000..34f4c34202
--- /dev/null
+++ b/src/include/catalog/pg_variable.h
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.h
+ * definition of schema variables system catalog (pg_variables)
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_variable.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VARIABLE_H
+#define PG_VARIABLE_H
+
+#include "catalog/genbki.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable_d.h"
+#include "utils/acl.h"
+
+/* ----------------
+ * pg_variable definition. cpp turns this into
+ * typedef struct FormData_pg_variable
+ * ----------------
+ */
+CATALOG(pg_variable,4287,VariableRelationId)
+{
+ NameData varname; /* variable name */
+ Oid varnamespace; /* OID of namespace containing variable class */
+ Oid vartype; /* OID of entry in pg_type for variable's type */
+ int32 vartypmod; /* typmode for variable's type */
+ Oid varowner; /* class owner */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+
+ /* list of expression trees for variable default (NULL if none) */
+ pg_node_tree vardefexpr BKI_DEFAULT(_null_);
+
+ aclitem varacl[1] BKI_DEFAULT(_null_); /* access permissions */
+
+#endif
+} FormData_pg_variable;
+
+/* ----------------
+ * Form_pg_variable corresponds to a pointer to a tuple with
+ * the format of pg_variable relation.
+ * ----------------
+ */
+typedef FormData_pg_variable *Form_pg_variable;
+
+typedef struct Variable
+{
+ Oid oid;
+ char *name;
+ Oid namespace;
+ Oid typid;
+ int32 typmod;
+ Oid owner;
+ Node *defexpr;
+ Acl *acl;
+} Variable;
+
+/* returns fields from pg_variable table */
+extern char *get_schema_variable_name(Oid varid);
+extern void get_schema_variable_type_typmod(Oid varid, Oid *typid, int32 *typmod);
+
+/* returns name of variable based on current search path */
+extern char *schema_variable_get_name(Oid varid);
+
+extern Variable *GetVariable(Oid varid, bool missing_ok);
+extern ObjectAddress VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Node *varDefexpr,
+ bool if_not_exists);
+
+
+#endif /* PG_VARIABLE_H */
diff --git a/src/include/commands/schemavariable.h b/src/include/commands/schemavariable.h
new file mode 100644
index 0000000000..2823d35b7c
--- /dev/null
+++ b/src/include/commands/schemavariable.h
@@ -0,0 +1,35 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavariable.h
+ * prototypes for schemavariable.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/schemavariable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SCHEMAVARIABLE_H
+#define SCHEMAVARIABLE_H
+
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable.h"
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "utils/queryenvironment.h"
+
+extern void ResetSchemaVariableCache(void);
+
+extern void RemoveVariableById(Oid varid);
+extern ObjectAddress DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt);
+
+extern Datum GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid, bool copy);
+extern void SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod);
+
+extern void doLetStmt(PlannedStmt *pstmt, ParamListInfo params, QueryEnvironment *queryEnv, const char *queryString);
+
+#endif
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77616..4fdceb6cee 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -344,13 +345,22 @@ typedef struct ExprEvalStep
TupleDesc argdesc;
} nulltest_row;
- /* for EEOP_PARAM_EXEC/EXTERN */
+ /* for EEOP_PARAM_EXEC/EXTERN/VARIABLE */
struct
{
- int paramid; /* numeric ID for parameter */
- Oid paramtype; /* OID of parameter's datatype */
+ int paramid; /* numeric ID for parameter */
+ Oid paramtype; /* OID of parameter's datatype */
} param;
+ /* for EEOP_PARAM_VARIABLE */
+ struct
+ {
+ int paramid; /* numeric ID for parameter */
+ Oid varoid; /* OID of assigned variable */
+ Oid paramtype; /* OID of parameter's datatype */
+ } vparam;
+
+
/* for EEOP_PARAM_CALLBACK */
struct
{
@@ -700,6 +710,8 @@ extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
extern void ExecEvalParamExecParams(Bitmapset *params, EState *estate);
extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern void ExecEvalParamVariable(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext);
extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op);
diff --git a/src/include/executor/svariableReceiver.h b/src/include/executor/svariableReceiver.h
new file mode 100644
index 0000000000..8c8117701f
--- /dev/null
+++ b/src/include/executor/svariableReceiver.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.h
+ * prototypes for svariableReceiver.c
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/svariableReceiver.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SVARIABLE_RECEIVER_H
+#define SVARIABLE_RECEIVER_H
+
+#include "tcop/dest.h"
+
+
+extern DestReceiver *CreateVariableDestReceiver(void);
+
+extern void SetVariableDestReceiverParams(DestReceiver *self, Oid varid);
+
+#endif /* SVARIABLE_RECEIVER_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 018f50bbb7..33cc8be55a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -100,6 +100,8 @@ typedef struct ExprState
int steps_len; /* number of steps currently */
int steps_alloc; /* allocated length of steps array */
+ int nvariables; /* number of used variables */
+
struct PlanState *parent; /* parent PlanState node, if any */
ParamListInfo ext_params; /* for compiling PARAM_EXTERN nodes */
@@ -472,6 +474,7 @@ typedef struct ResultRelInfo
typedef struct EState
{
NodeTag type;
+ bool es_shared; /* plpgsql uses share estate */
/* Basic state for all query types: */
ScanDirection es_direction; /* current scan direction */
@@ -564,6 +567,14 @@ typedef struct EState
/* The per-query shared memory area to use for parallel execution. */
struct dsa_area *es_query_dsa;
+ int es_result_variable; /* Oid of target variable */
+
+ /* query schema variable cache */
+ int es_nvariables;
+ bool *es_varnulls;
+ Oid *es_vartypes;
+ Datum *es_varvalues;
+
/*
* JIT information. es_jit_flags indicates whether JIT should be performed
* and with which options. es_jit is created on-demand when JITing is
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 697d3d7a5f..dd7fd8ed42 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -348,6 +348,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSchemaVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
@@ -419,6 +420,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
+ T_LetStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -663,6 +665,7 @@ typedef enum CmdType
CMD_DELETE,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
+ CMD_PLAN_UTILITY, /* only let stmt now, requires planning */
CMD_NOTHING /* dummy command for instead nothing rules
* with qual */
} CmdType;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07ab1a3dde..2d4a3cb1b6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -84,7 +84,9 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
-#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_READ (1<<12) /* for variables */
+#define ACL_WRITE (1<<13) /* for variables */
+#define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -121,6 +123,7 @@ typedef struct Query
int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */
+ int resultVariable; /* Oid of target variable or 0 */
bool hasAggs; /* has aggregates in tlist or havingQual */
bool hasWindowFuncs; /* has window functions in tlist */
@@ -1505,6 +1508,18 @@ typedef struct UpdateStmt
WithClause *withClause; /* WITH clause */
} UpdateStmt;
+/* ----------------------
+ * Let Statement
+ * ----------------------
+ */
+typedef struct LetStmt
+{
+ NodeTag type;
+ List *target; /* target variable */
+ Node *selectStmt; /* source expression */
+ int location;
+} LetStmt;
+
/* ----------------------
* Select Statement
*
@@ -1682,6 +1697,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -2497,6 +2513,19 @@ typedef struct AlterSeqStmt
bool missing_ok; /* skip error if a role is missing? */
} AlterSeqStmt;
+/* ----------------------
+ * {Create|Alter} VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSchemaVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the type of variable */
+ Node *defexpr; /* default expression */
+ bool if_not_exists; /* do nothing if it already exists */
+} CreateSchemaVarStmt;
+
/* ----------------------
* Create {Aggregate|Operator|Type} Statement
* ----------------------
@@ -3238,7 +3267,8 @@ typedef enum DiscardMode
DISCARD_ALL,
DISCARD_PLANS,
DISCARD_SEQUENCES,
- DISCARD_TEMP
+ DISCARD_TEMP,
+ DISCARD_VARIABLES
} DiscardMode;
typedef struct DiscardStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd03a..2588f1455f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -43,7 +43,7 @@ typedef struct PlannedStmt
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete|utility */
+ CmdType commandType; /* select|let|insert|update|delete|utility */
uint64 queryId; /* query identifier (copied from Query) */
@@ -81,6 +81,9 @@ typedef struct PlannedStmt
*/
List *rootResultRelations;
+ /* Oid of target variable for LET command */
+ Oid resultVariable;
+
List *subplans; /* Plan trees for SubPlan expressions; note
* that some could be NULL */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..97f838a54e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -229,13 +229,17 @@ typedef struct Const
* of the `paramid' field contain the SubLink's subLinkId, and
* the low-order 16 bits contain the column number. (This type
* of Param is also converted to PARAM_EXEC during planning.)
+ *
+ * PARAM_VARIABLE: The parameter is a access to schema variable
+ * paramid holds varid.
*/
typedef enum ParamKind
{
PARAM_EXTERN,
PARAM_EXEC,
PARAM_SUBLINK,
- PARAM_MULTIEXPR
+ PARAM_MULTIEXPR,
+ PARAM_VARIABLE
} ParamKind;
typedef struct Param
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db40147b..d3ed3f4d0f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -231,6 +231,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("let", LET, UNRESERVED_KEYWORD)
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -434,6 +435,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 0230543810..f7c2e67f33 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -69,7 +69,9 @@ typedef enum ParseExprKind
EXPR_KIND_TRIGGER_WHEN, /* WHEN condition in CREATE TRIGGER */
EXPR_KIND_POLICY, /* USING or WITH CHECK expr in policy */
EXPR_KIND_PARTITION_EXPRESSION, /* PARTITION BY expression */
- EXPR_KIND_CALL_ARGUMENT /* procedure argument in CALL */
+ EXPR_KIND_CALL_ARGUMENT, /* procedure argument in CALL */
+ EXPR_KIND_VARIABLE_DEFAULT, /* default value for schema variable */
+ EXPR_KIND_LET /* LET assignment (should be same like UPDATE) */
} ParseExprKind;
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index ec6e0c102f..1ee199ed8f 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -32,6 +32,16 @@ extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
int attrno,
List *indirection,
int location);
+extern Node *transformAssignmentIndirection(ParseState *pstate,
+ Node *basenode,
+ const char *targetName,
+ bool targetIsArray,
+ Oid targetTypeId,
+ int32 targetTypMod,
+ Oid targetCollation,
+ ListCell *indirection,
+ Node *rhs,
+ int location);
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
char *colname, int attrno,
List *indirection,
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 82f0f2e741..c49b653555 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -96,7 +96,8 @@ typedef enum
DestCopyOut, /* results sent to COPY TO code */
DestSQLFunction, /* results sent to SQL-language func mgr */
DestTransientRel, /* results sent to transient relation */
- DestTupleQueue /* results sent to tuple queue */
+ DestTupleQueue, /* results sent to tuple queue */
+ DestVariable /* results sents to schema variable */
} CommandDest;
/* ----------------
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f4d4be8d0d..c624d8dd0b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -147,9 +147,11 @@ typedef ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
+#define ACL_READ_CHR 'S' /* 'R' is occupated by old RULE priv */
+#define ACL_WRITE_CHR 'W'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcSW"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -166,6 +168,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_READ|ACL_WRITE)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -253,6 +256,8 @@ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_variable_aclmask(Oid var_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode);
@@ -269,6 +274,7 @@ extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
+extern AclResult pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, ObjectType objtype,
const char *objectname);
@@ -305,6 +311,7 @@ extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool pg_publication_ownercheck(Oid pub_oid, Oid roleid);
extern bool pg_subscription_ownercheck(Oid sub_oid, Oid roleid);
extern bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid);
+extern bool pg_variable_ownercheck(Oid stat_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index e55ea4035b..cb3f4aaca9 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -122,6 +122,7 @@ extern bool get_func_leakproof(Oid funcid);
extern float4 get_func_cost(Oid funcid);
extern float4 get_func_rows(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
+extern Oid get_varname_varid(const char *varname, Oid varnamespace);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4f333586ee..453699be3c 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -107,9 +107,11 @@ enum SysCacheIdentifier
TYPENAMENSP,
TYPEOID,
USERMAPPINGOID,
- USERMAPPINGUSERSERVER
+ USERMAPPINGUSERSERVER,
+ VARIABLENAMENSP,
+ VARIABLEOID
-#define SysCacheSize (USERMAPPINGUSERSERVER + 1)
+#define SysCacheSize (VARIABLEOID + 1)
};
extern void InitCatalogCache(void);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 380d1de8f4..ac71dd7d7a 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8049,6 +8049,7 @@ plpgsql_create_econtext(PLpgSQL_execstate *estate)
{
oldcontext = MemoryContextSwitchTo(TopTransactionContext);
shared_simple_eval_estate = CreateExecutorState();
+ shared_simple_eval_estate->es_shared = true;
MemoryContextSwitchTo(oldcontext);
}
estate->simple_eval_estate = shared_simple_eval_estate;
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 7d3647a12d..7f183d4f1b 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -332,6 +332,7 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
/* Create a private EState for simple-expression execution */
simple_eval_estate = CreateExecutorState();
+ simple_eval_estate->es_shared = true;
/* And run the function */
PG_TRY();
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b500..48286f8e1a 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -105,5 +105,7 @@ ORDER BY 1, 2;
pg_index | indpred | pg_node_tree
pg_largeobject | data | bytea
pg_largeobject_metadata | lomacl | aclitem[]
-(11 rows)
+ pg_variable | varacl | aclitem[]
+ pg_variable | vardefexpr | pg_node_tree
+(13 rows)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 0aa5357917..848b041a4b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -163,6 +163,7 @@ pg_ts_parser|t
pg_ts_template|t
pg_type|t
pg_user_mapping|t
+pg_variable|t
point_tbl|t
polygon_tbl|t
quad_box_tbl|t
diff --git a/src/test/regress/expected/schema_variables.out b/src/test/regress/expected/schema_variables.out
new file mode 100644
index 0000000000..84fe30a2c0
--- /dev/null
+++ b/src/test/regress/expected/schema_variables.out
@@ -0,0 +1,351 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+DROP VARIABLE var1, var2;
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT var1;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT READ ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT var1;
+ var1
+------
+
+(1 row)
+
+SET ROLE TO DEFAULT;
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to work
+LET var1 = 333;
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var1;
+ERROR: permission denied for schema variable var1
+-- should to work;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: ((g.v)::numeric = var1)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var1;
+SELECT * FROM schema_var_view;
+ var1
+------
+ 333
+(1 row)
+
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ var1
+------
+
+(1 row)
+
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+----------------------------
+ Result
+ Output: 3.14159265358979
+(2 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+SELECT inc(1);
+ inc
+-----
+ 1
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 2
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 3
+(1 row)
+
+SELECT inc(1) FROM generate_series(1,10);
+ inc
+-----
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+(10 rows)
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+-- composite variables
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+\d v1
+\d v2
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+SELECT v1;
+ v1
+------------
+ (1,2,3.14)
+(1 row)
+
+SELECT v2;
+ v2
+---------------
+ (10,20,31.40)
+(1 row)
+
+SELECT (v1).*;
+ x | y | z
+---+---+------
+ 1 | 2 | 3.14
+(1 row)
+
+SELECT (v2).*;
+ x | y | z
+----+----+-------
+ 10 | 20 | 31.40
+(1 row)
+
+SELECT v1.x + v1.z;
+ ?column?
+----------
+ 4.14
+(1 row)
+
+SELECT v2.x + v2.z;
+ ?column?
+----------
+ 41.40
+(1 row)
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+SELECT v2.x;
+ERROR: permission denied for schema variable v2
+SET ROLE TO DEFAULT;
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+DROP ROLE var_test_role;
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+ relname
+----------
+ pg_class
+(1 row)
+
+-- should to fail
+SELECT varx.xxx;
+ERROR: type text is not composite
+-- variables can be updated under RO transaction
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+SELECT varx;
+ varx
+-------
+ hello
+(1 row)
+
+DROP VARIABLE varx;
+CREATE TYPE t1 AS (a int, b numeric, c text);
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+ v1
+----------------------------
+ (1,3.14159265358979,hello)
+(1 row)
+
+LET v1.b = 10.2222;
+SELECT v1;
+ v1
+-------------------
+ (1,10.2222,hello)
+(1 row)
+
+-- should to fail
+LET v1.x = 10;
+ERROR: cannot assign to field "x" of column "x" because there is no such column in data type t1
+LINE 1: LET v1.x = 10;
+ ^
+DROP VARIABLE v1;
+DROP TYPE t1;
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+ va1
+------------
+ {10.1,2.1}
+(1 row)
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+ va2
+--------------------
+ (10.2,"{0.0,0.0}")
+(1 row)
+
+LET va2.b[1] = 10.3;
+SELECT va2;
+ va2
+---------------------
+ (10.2,"{10.3,0.0}")
+(1 row)
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+ v1
+------------------
+ 6.28318530717958
+(1 row)
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+ v2
+--------------------------
+ (3.14159265358979,Hello)
+(1 row)
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+ERROR: cannot drop type t2 because other objects depend on it
+DETAIL: schema variable v2 depends on type t2
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+ var1
+------
+ 1000
+(1 row)
+
+CREATE ROLE var_test_role;
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+DROP VARIABLE var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+SET ROLE TO DEFAULT;
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+SET ROLE TO var_test_role;
+SELECT public.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+SELECT public.var1_renamed;
+ var1_renamed
+--------------
+ 1000
+(1 row)
+
+DROP VARIABLE public.var1_renamed;
+SET ROLE TO DEFAULt;
+DROP ROLE var_test_role;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 16f979c8d9..9bf379b87b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml schema_variables
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 42632be675..42bf4ecb3f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -191,3 +191,4 @@ test: partition_aggregate
test: event_trigger
test: fast_default
test: stats
+test: schema_variables
diff --git a/src/test/regress/sql/schema_variables.sql b/src/test/regress/sql/schema_variables.sql
new file mode 100644
index 0000000000..91b2bbb28b
--- /dev/null
+++ b/src/test/regress/sql/schema_variables.sql
@@ -0,0 +1,247 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+
+DROP VARIABLE var1, var2;
+
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+
+CREATE ROLE var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT READ ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+-- should to work
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to work
+LET var1 = 333;
+
+SET ROLE TO DEFAULT;
+
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+
+SELECT secure_var();
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT public.var1;
+
+-- should to work;
+SELECT secure_var();
+
+SET ROLE TO DEFAULT;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+
+CREATE VIEW schema_var_view AS SELECT var1;
+
+SELECT * FROM schema_var_view;
+
+\c -
+
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+
+LET var1 = pi();
+
+SELECT var1;
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+
+EXECUTE var_pp(100, 1.23456);
+
+SELECT var1;
+
+CREATE VARIABLE var3 AS int;
+
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT inc(1);
+SELECT inc(1);
+SELECT inc(1);
+
+SELECT inc(1) FROM generate_series(1,10);
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+LET var3 = 0;
+
+SET ROLE TO DEFAULT;
+
+DROP VIEW schema_var_view;
+
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+
+-- composite variables
+
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+
+\d v1
+\d v2
+
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+
+SELECT v1;
+SELECT v2;
+SELECT (v1).*;
+SELECT (v2).*;
+
+SELECT v1.x + v1.z;
+SELECT v2.x + v2.z;
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+
+SELECT v2.x;
+
+SET ROLE TO DEFAULT;
+
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+DROP ROLE var_test_role;
+
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+
+-- should to fail
+SELECT varx.xxx;
+
+-- variables can be updated under RO transaction
+
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+
+SELECT varx;
+
+DROP VARIABLE varx;
+
+CREATE TYPE t1 AS (a int, b numeric, c text);
+
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+LET v1.b = 10.2222;
+SELECT v1;
+
+-- should to fail
+LET v1.x = 10;
+
+DROP VARIABLE v1;
+DROP TYPE t1;
+
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+LET va2.b[1] = 10.3;
+SELECT va2;
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+
+CREATE ROLE var_test_role;
+
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+DROP VARIABLE var_schema2.var1;
+
+SET ROLE TO DEFAULT;
+
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+
+SET ROLE TO var_test_role;
+SELECT public.var1;
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+
+SELECT public.var1_renamed;
+
+DROP VARIABLE public.var1_renamed;
+
+SET ROLE TO DEFAULt;
+
+DROP ROLE var_test_role;
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-08-14 14:38 ` Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-08-14 14:38 UTC (permalink / raw)
To: Gilles Darold <gilles.darold@dalibo.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
I wrote missing collation support
Regards
Pavel
Attachments:
[text/x-patch] schema-variables-180814-01.patch (197.4K, ../../CAFj8pRD3J+92Va+T=tK0Q14Q4J-CVgKEoXGH8cS3YqRzQcbOUQ@mail.gmail.com/3-schema-variables-180814-01.patch)
download | inline diff:
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3bb48d4ccf..ffeea7df3f 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -359,6 +359,11 @@
<entry><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link></entry>
<entry>mappings of users to foreign servers</entry>
</row>
+
+ <row>
+ <entry><link linkend="catalog-pg-variable"><structname>pg_variable</structname></link></entry>
+ <entry>schema variables</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -11311,4 +11316,114 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
+ <sect1 id="catalog-pg-variable">
+ <title><structname>pg_variable</structname></title>
+
+ <indexterm zone="catalog-pg-variable">
+ <primary>pg_variable</primary>
+ </indexterm>
+
+ <para>
+ The table <structname>pg_variable</structname> holds metadata
+ of schema variables.
+ </para>
+
+ <table>
+ <title><structname>pg_views</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><structfield>oid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry></entry>
+ <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Name of the schema variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the namespace that contains this variable
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartype</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the data type of this variable.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartypmod</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ <structfield>vartypmod</structfield> records type-specific data
+ supplied at table creation time (for example, the maximum
+ length of a <type>varchar</type> column). It is passed to
+ type-specific input functions and length coercion functions.
+ The value will generally be -1 for types that do not need <structfield>vartypmod</structfield>.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>varowner</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>Owner of the variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varcollation</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+ <entry>
+ The defined collation of the variable, or zero if the variable is
+ not of a collatable data type.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vardefexpr</structfield></entry>
+ <entry><type>pg_node_tree</type></entry>
+ <entry></entry>
+ <entry>The internal representation of the variable default value</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>
+ Access privileges; see
+ <xref linkend="sql-grant"/> and
+ <xref linkend="sql-revoke"/>
+ for details
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..0631c9ed56 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -47,6 +47,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterType SYSTEM "alter_type.sgml">
<!ENTITY alterUser SYSTEM "alter_user.sgml">
<!ENTITY alterUserMapping SYSTEM "alter_user_mapping.sgml">
+<!ENTITY alterVariable SYSTEM "alter_variable.sgml">
<!ENTITY alterView SYSTEM "alter_view.sgml">
<!ENTITY analyze SYSTEM "analyze.sgml">
<!ENTITY begin SYSTEM "begin.sgml">
@@ -99,6 +100,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createType SYSTEM "create_type.sgml">
<!ENTITY createUser SYSTEM "create_user.sgml">
<!ENTITY createUserMapping SYSTEM "create_user_mapping.sgml">
+<!ENTITY createVariable SYSTEM "create_variable.sgml">
<!ENTITY createView SYSTEM "create_view.sgml">
<!ENTITY deallocate SYSTEM "deallocate.sgml">
<!ENTITY declare SYSTEM "declare.sgml">
@@ -148,6 +150,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropUser SYSTEM "drop_user.sgml">
<!ENTITY dropUserMapping SYSTEM "drop_user_mapping.sgml">
<!ENTITY dropView SYSTEM "drop_view.sgml">
+<!ENTITY dropVariable SYSTEM "drop_variable.sgml">
<!ENTITY end SYSTEM "end.sgml">
<!ENTITY execute SYSTEM "execute.sgml">
<!ENTITY explain SYSTEM "explain.sgml">
@@ -155,6 +158,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY grant SYSTEM "grant.sgml">
<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
<!ENTITY insert SYSTEM "insert.sgml">
+<!ENTITY let SYSTEM "let.sgml">
<!ENTITY listen SYSTEM "listen.sgml">
<!ENTITY load SYSTEM "load.sgml">
<!ENTITY lock SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/alter_variable.sgml b/doc/src/sgml/ref/alter_variable.sgml
new file mode 100644
index 0000000000..6376ac716b
--- /dev/null
+++ b/doc/src/sgml/ref/alter_variable.sgml
@@ -0,0 +1,170 @@
+<!--
+doc/src/sgml/ref/alter_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-altervariable">
+ <indexterm zone="sql-altervariable">
+ <primary>ALTER VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>ALTER VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ALTER VARIABLE</refname>
+ <refpurpose>
+ change the definition of a variable
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="parameter">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>ALTER VARIABLE</command> changes the definition of an existing variable.
+ There are several subforms:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>OWNER</literal></term>
+ <listitem>
+ <para>
+ This form changes the owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RENAME</literal></term>
+ <listitem>
+ <para>
+ This form changes the name of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET SCHEMA</literal></term>
+ <listitem>
+ <para>
+ This form moves the variable into another schema.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </para>
+
+ <para>
+ You must own the variable to use <command>ALTER VARIABLE</command>.
+ To change the schema of a variable, you must also have
+ <literal>CREATE</literal> privilege on the new schema.
+ To alter the owner, you must also be a direct or indirect member of the new
+ owning role, and that role must have <literal>CREATE</literal> privilege on
+ the variable's schema. (These restrictions enforce that altering the owner
+ doesn't do anything you couldn't do by dropping and recreating the variable.
+ However, a superuser can alter ownership of any type anyway.)
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (possibly schema-qualified) of an existing variable to
+ alter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_name</replaceable></term>
+ <listitem>
+ <para>
+ The new name for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_owner</replaceable></term>
+ <listitem>
+ <para>
+ The user name of the new owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_schema</replaceable></term>
+ <listitem>
+ <para>
+ The new schema for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To rename a variable:
+<programlisting>
+ALTER VARIABLE foo RENAME TO boo;
+</programlisting>
+ </para>
+
+ <para>
+ To change the owner of the variable <literal>boo</literal>
+ to <literal>joe</literal>:
+<programlisting>
+ALTER VARIABLE boo OWNER TO joe;
+</programlisting>
+ </para>
+
+ <para>
+ To change the schema of the variable <literal>boo</literal>
+ to <literal>private</literal>:
+<programlisting>
+ALTER VARIABLE boo SET SCHEMA private;
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ This comman is a PostgreSQL extension.
+ </para>
+ </refsect1>
+
+ <refsect1 id="sql-altervariable-see-also">
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
new file mode 100644
index 0000000000..1bf127eccd
--- /dev/null
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -0,0 +1,145 @@
+<!--
+doc/src/sgml/ref/create_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-createvariable">
+ <indexterm zone="sql-createvariable">
+ <primary>CREATE VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE VARIABLE</refname>
+ <refpurpose>define a new permissioned typed schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ] [ COLLATE <replaceable class="parameter">collation</replaceable> ]
+</synopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> creates a new schema variable.
+ These variables are scalar typed, non-transactional, and, like relations,
+ exist within a schema with access controlled via
+ <command>GRANT</command> and <command>REVOKE</command>.
+ </para>
+
+ <para>
+ The value of a schema variable is session-local. Retrieving
+ a variable's value will return NULL unless its value has been set
+ to something else in the current session.
+ </para>
+
+ <para>
+ Retrieval is done via the <function>get_schema_variable</function>dunxrion or the SQL
+ command <command>SELECT</command>. Setting of values is done via the
+ <function>set_schema_variable</function> function or the SQL command
+ <command>LET</command>.
+ Notably, while schema variables are in many ways a kind of table you cannot use
+ <command>UPDATE</command> on them.
+ </para>
+
+ <para>
+ For purposes of name uniqueness relation-like objects (e.g., tables, indexes)
+ within the same schema are considered. i.e., you cannot give a table and a
+ schema variable the same name. This is a consequence of them being treated
+ like relations for purposes of <command>SELECT</command>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF NOT EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the name already exists. A notice is issued in this case.
+ Note that type of the variable is not considered, nor could it be since the namespace
+ searched contains non-variable objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the data type of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>COLLATE <replaceable>collation</replaceable></literal></term>
+ <listitem>
+ <para>
+ The <literal>COLLATE</literal> clause assigns a collation to
+ the variable (which must be of a collatable data type).
+ If not specified, the variable data type's default collation is used.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Use <command>DROP VARIABLE</command> to remove a variable.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Create an integer variable <literal>var1</literal>:
+<programlisting>
+CREATE VARIABLE var1 AS integer;
+SELECT var1;
+</programlisting>
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> is a PostgreSQL feature.
+ <!-- The choice of wording here seems to be left to personal preference... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index 6b909b7232..d83ad811fd 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
+DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES }
</synopsis>
</refsynopsisdiv>
@@ -75,6 +75,17 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>VARIABLES</literal></term>
+ <listitem>
+ <para>
+ Resets the value of all schema variables. When variables
+ will be used later, then will be initialized again to
+ NULL or default value.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
new file mode 100644
index 0000000000..c1c1a2bd67
--- /dev/null
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -0,0 +1,93 @@
+<!--
+doc/src/sgml/ref/drop_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropvariable">
+ <indexterm zone="sql-dropvariable">
+ <primary>DROP VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP VARIABLE</refname>
+ <refpurpose>remove a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP VARIABLE</command> removes a schema variable.
+ A variable can only be dropped by its owner or a superuser.
+ <!-- this would suggest that we need an alter variable owner to command -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the variable does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To remove the schema variable <literal>var1</literal>:
+
+<programlisting>
+DROP VARIABLE var1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ <!-- create variable is a "PostgreSQL feature",
+ this is a "proprietary PostgreSQL command" ... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-createvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index ff64c7a3ba..a83920a7a1 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -79,6 +79,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE <replaceable>type_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
[ GROUP ] <replaceable class="parameter">role_name</replaceable>
@@ -167,6 +171,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
foreign servers,
large objects,
schemas,
+ schema variable
or tablespaces.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
@@ -385,6 +390,24 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>READ</literal></term>
+ <listitem>
+ <para>
+ Allows to read a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WRITE</literal></term>
+ <listitem>
+ <para>
+ Allows to set a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL PRIVILEGES</literal></term>
<listitem>
@@ -550,6 +573,8 @@ rolename=xxxx -- privileges granted to a role
C -- CREATE
c -- CONNECT
T -- TEMPORARY
+ S -- READ
+ w -- WRITE
arwdDxt -- ALL PRIVILEGES (for tables, varies for other objects)
* -- grant option for preceding privilege
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
new file mode 100644
index 0000000000..e8bf3f6dd4
--- /dev/null
+++ b/doc/src/sgml/ref/let.sgml
@@ -0,0 +1,90 @@
+<!--
+doc/src/sgml/ref/let.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-let">
+ <indexterm zone="sql-let">
+ <primary>LET</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>LET</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>LET</refname>
+ <refpurpose>change a schema variable's value</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable class="parameter">sql_expression</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ The <command>LET</command> command updates the specified schema variable' value.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>schema_variable</literal></term>
+ <listitem>
+ <para>
+ The name of schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sql expression</literal></term>
+ <listitem>
+ <para>
+ An SQL expression, the result is cast to the schema variable's type.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Example:
+<programlisting>
+CREATE VARIABLE myvar AS integer;
+LET myvar = 10;
+LET myvar = (SELECT sum(val) FROM tab);
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <!-- this feels like it needs to be more specific,
+ but I don't know enough to make it so -->
+ <literal>LET</literal> extends syntax defined in the SQL
+ standard. The standard knows <literal>SET</literal> command,
+ that is used for different purpouse in PostgreSQL.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 5317f8ccba..8435e05957 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -108,6 +108,12 @@ REVOKE [ GRANT OPTION FOR ]
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_name</replaceable> [, ...]
[ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { READ | WRITE } [, ...] | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..5fb82df51e 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -75,6 +75,7 @@
&alterType;
&alterUser;
&alterUserMapping;
+ &alterVariable;
&alterView;
&analyze;
&begin;
@@ -127,6 +128,7 @@
&createType;
&createUser;
&createUserMapping;
+ &createVariable;
&createView;
&deallocate;
&declare;
@@ -175,6 +177,7 @@
&dropType;
&dropUser;
&dropUserMapping;
+ &dropVariable;
&dropView;
&end;
&execute;
@@ -183,6 +186,7 @@
&grant;
&importForeignSchema;
&insert;
+ &let;
&listen;
&load;
&lock;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 0865240f11..1f7c4d1223 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -19,7 +19,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_publication.o pg_range.o \
pg_db_role_setting.o pg_shdepend.o pg_subscription.o pg_type.o \
- storage.o toasting.o
+ pg_variable.o storage.o toasting.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
@@ -46,7 +46,7 @@ CATALOG_HEADERS := \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
- pg_subscription_rel.h
+ pg_subscription_rel.h pg_variable.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 578e4c6592..86917e15a8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -57,6 +57,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_transform.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
@@ -112,6 +113,7 @@ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
+static void ExecGrant_Variable(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
@@ -284,6 +286,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -507,6 +512,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -609,6 +618,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_VARIABLE:
+ ExecGrant_Variable(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -768,6 +780,16 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_VARIABLE:
+ foreach(cell, objnames)
+ {
+ RangeVar *varvar = (RangeVar *) lfirst(cell);
+ Oid relOid;
+
+ relOid = lookup_variable(varvar->schemaname, varvar->relname, false);
+ objects = lappend_oid(objects, relOid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -855,6 +877,31 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
heap_close(rel, AccessShareLock);
}
break;
+ case OBJECT_VARIABLE:
+ {
+ ScanKeyData key;
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&key,
+ Anum_pg_variable_varnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(namespaceId));
+
+ rel = heap_open(VariableRelationId, AccessShareLock);
+ scan = heap_beginscan_catalog(rel, 1, &key);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ }
+ break;
+
default:
/* should not happen */
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
@@ -1018,6 +1065,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1215,6 +1266,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1441,6 +1498,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -3266,6 +3326,129 @@ ExecGrant_Type(InternalGrant *istmt)
heap_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Variable(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_VARIABLE;
+
+ relation = heap_open(VariableRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid varId = lfirst_oid(cell);
+ Form_pg_variable pg_variable_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_variable];
+ bool nulls[Natts_pg_variable];
+ bool replaces[Natts_pg_variable];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for schema variables %u", varId);
+
+ pg_variable_tuple = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_variable_tuple->varowner;
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple, Anum_pg_variable_varacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_VARIABLE, ownerId);
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ oldmembers = NULL;
+ }
+ else
+ {
+ old_acl = DatumGetAclPCopy(aclDatum);
+ /* Get the roles mentioned in the existing ACL */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+ }
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ varId, grantorId, OBJECT_VARIABLE,
+ NameStr(pg_variable_tuple->varname),
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ */
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_variable_varacl - 1] = true;
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(varId, VariableRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(VariableRelationId, varId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3298,6 +3481,10 @@ string_to_privilege(const char *privname)
return ACL_CONNECT;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
+ if (strcmp(privname, "read") == 0)
+ return ACL_READ;
+ if (strcmp(privname, "write") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized privilege type \"%s\"", privname)));
@@ -3333,6 +3520,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3456,6 +3647,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3566,6 +3760,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3710,6 +3907,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
+ case OBJECT_VARIABLE:
+ return pg_variable_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objtype: %d",
(int) objtype);
@@ -4499,6 +4698,67 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a variable.
+ */
+AclMode
+pg_variable_aclmask(Oid var_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_variable varForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the type's tuple from pg_type
+ */
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable with OID %u does not exist",
+ var_oid)));
+ varForm = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Now get the type's owner and ACL from the tuple
+ */
+ ownerId = varForm->varowner;
+
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple,
+ Anum_pg_variable_varacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_VARIABLE, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+
/*
* Exported routine for checking a user's access privileges to a column
*
@@ -4744,6 +5004,18 @@ pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a variable
+ */
+AclResult
+pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
+{
+ if (pg_variable_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Ownership check for a relation (specified by OID).
*/
@@ -5361,6 +5633,33 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a schema variables (specified by OID).
+ */
+bool
+pg_variable_ownercheck(Oid db_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(db_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("variable with OID %u does not exist", db_oid)));
+
+ ownerId = ((Form_pg_variable) GETSTRUCT(tuple))->varowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+
/*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
@@ -5486,6 +5785,10 @@ get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
+ case OBJECT_VARIABLE:
+ defaclobjtype = DEFACLOBJ_VARIABLE;
+ break;
+
default:
return NULL;
}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 4f1d365357..782ddb1655 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -59,6 +59,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -67,6 +68,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
@@ -1280,6 +1282,10 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_VARIABLE:
+ RemoveVariableById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2537,6 +2543,9 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case VariableRelationId:
+ return OCLASS_VARIABLE;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 3971346e73..90d16e263f 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -39,6 +39,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
@@ -755,6 +756,71 @@ RelationIsVisible(Oid relid)
return visible;
}
+/*
+ * VariableIsVisible
+ * Determine whether a variable (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified variable name".
+ */
+bool
+VariableIsVisible(Oid varid)
+{
+ HeapTuple vartup;
+ Form_pg_variable varform;
+ Oid varnamespace;
+ bool visible;
+
+ vartup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(vartup))
+ elog(ERROR, "cache lookup failed for schema variable %u", varid);
+ varform = (Form_pg_variable) GETSTRUCT(vartup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ varnamespace = varform->varnamespace;
+ if (varnamespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, varnamespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another relation of the same name earlier in the path. So
+ * we must do a slow check for conflicting relations.
+ */
+ char *varname = NameStr(varform->varname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == varnamespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (OidIsValid(get_varname_varid(varname, namespaceId)))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(vartup);
+
+ return visible;
+}
+
+
/*
* TypenameGetTypid
@@ -2776,6 +2842,202 @@ TSConfigIsVisible(Oid cfgid)
return visible;
}
+/*
+ * When we know a variable name, then we can find variable simply
+ */
+Oid
+lookup_variable(const char *nspname, const char *varname, bool missing_ok)
+{
+ Oid namespaceId;
+ Oid varoid = InvalidOid;
+ ListCell *l;
+
+ if (nspname)
+ {
+ namespaceId = LookupExplicitNamespace(nspname, missing_ok);
+ if (!OidIsValid(namespaceId))
+ return InvalidOid;
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+
+ if (OidIsValid(varoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(varoid) && !missing_ok)
+ {
+ if (nspname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\".\"%s\" does not exist",
+ nspname, varname)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\" does not exist",
+ varname)));
+ }
+
+ return varoid;
+}
+
+List *
+NamesFromList(List *names)
+{
+ ListCell *l;
+ List *result = NIL;
+
+ foreach(l, names)
+ {
+ Node *n = lfirst(l);
+
+ if (IsA(n, String))
+ {
+ result = lappend(result, n);
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * identify_variable
+ *
+ * Returns oid of not ambigonuous variable specified by qualified path
+ * or InvalidOid. When the path is ambigonuous, then not_uniq flag is
+ * is true.
+ */
+Oid
+identify_variable(List *names, char **attrname, bool *not_uniq)
+{
+ char *a = NULL;
+ char *b = NULL;
+ char *c = NULL;
+ char *d = NULL;
+ Oid varoid_without_attr;
+ Oid varoid_with_attr;
+
+ *not_uniq = false;
+
+ switch (list_length(names))
+ {
+ case 1:
+ a = strVal(linitial(names));
+ return lookup_variable(NULL, a, true);
+
+ case 2:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+
+ /*
+ * a.b can mean "schema"."variable" or "variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(a, b, true);
+ varoid_with_attr = lookup_variable(NULL, a, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = b;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 3:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+
+ /*
+ * a.b.c can mean "catalog"."schema"."variable" or "schema"."variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(b, c, true);
+ varoid_with_attr = lookup_variable(a, b, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = c;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 4:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+ d = strVal(lfourth(names));
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ *attrname = d;
+ return lookup_variable(b, c, true);
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(names))));
+ break;
+ }
+}
/*
* DeconstructQualifiedName
@@ -4484,3 +4746,14 @@ pg_is_other_temp_schema(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(isOtherTempNamespace(oid));
}
+
+Datum
+pg_variable_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ if (!SearchSysCacheExists1(VARIABLEOID, ObjectIdGetDatum(oid)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_BOOL(VariableIsVisible(oid));
+}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7db942dcba..cc3d415e61 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -58,6 +58,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -489,6 +490,18 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
OBJECT_STATISTIC_EXT,
true
+ },
+ {
+ VariableRelationId,
+ VariableObjectIndexId,
+ VARIABLEOID,
+ VARIABLENAMENSP,
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ Anum_pg_variable_varowner,
+ Anum_pg_variable_varacl,
+ OBJECT_VARIABLE,
+ true
}
};
@@ -714,6 +727,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_VARIABLE */
+ {
+ "schema variable", OBJECT_VARIABLE
}
};
@@ -739,6 +756,7 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
bool missing_ok);
static ObjectAddress get_object_address_type(ObjectType objtype,
TypeName *typename, bool missing_ok);
+static ObjectAddress get_object_address_variable(List *object, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object,
bool missing_ok);
static ObjectAddress get_object_address_opf_member(ObjectType objtype,
@@ -996,6 +1014,10 @@ get_object_address(ObjectType objtype, Node *object,
missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_VARIABLE:
+ address = get_object_address_variable(castNode(List, object), missing_ok);
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
@@ -1848,16 +1870,20 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type \"%c\"", objtype),
- errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
+ errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
DEFACLOBJ_RELATION,
DEFACLOBJ_SEQUENCE,
DEFACLOBJ_FUNCTION,
DEFACLOBJ_TYPE,
- DEFACLOBJ_NAMESPACE)));
+ DEFACLOBJ_NAMESPACE,
+ DEFACLOBJ_VARIABLE)));
}
/*
@@ -1942,6 +1968,24 @@ textarray_to_strvaluelist(ArrayType *arr)
return list;
}
+/*
+ * Find the ObjectAddress for a type or domain
+ */
+static ObjectAddress
+get_object_address_variable(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ char *nspname = NULL;
+ char *varname = NULL;
+
+ ObjectAddressSet(address, VariableRelationId, InvalidOid);
+
+ DeconstructQualifiedName(object, &nspname, &varname);
+ address.objectId = lookup_variable(nspname, varname, missing_ok);
+
+ return address;
+}
+
/*
* SQL-callable version of get_object_address
*/
@@ -2131,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_TABCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
+ case OBJECT_VARIABLE:
objnode = (Node *) name;
break;
case OBJECT_ACCESS_METHOD:
@@ -2415,6 +2460,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
break;
+ case OBJECT_VARIABLE:
+ if (!pg_variable_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
default:
elog(ERROR, "unrecognized object type: %d",
(int) objtype);
@@ -3157,6 +3207,32 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_VARIABLE:
+ {
+ char *nspname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ if (VariableIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ appendStringInfo(&buffer, _("schema variable %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(varform->varname)));
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_TSPARSER:
{
HeapTuple tup;
@@ -3422,6 +3498,16 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new schemas belonging to role %s"),
rolename);
break;
+ case DEFACLOBJ_VARIABLE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s"),
+ rolename);
+ break;
default:
/* shouldn't get here */
if (nspname)
@@ -4070,6 +4156,10 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_VARIABLE:
+ appendStringInfoString(&buffer, "schema variable");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4962,6 +5052,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on variables");
+ break;
}
if (objname)
@@ -5121,6 +5215,33 @@ getObjectIdentityParts(const ObjectAddress *object,
}
break;
+ case OCLASS_VARIABLE:
+ {
+ char *schema;
+ char *varname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ schema = get_namespace_name_or_temp(varform->varnamespace);
+ varname = NameStr(varform->varname);
+
+ appendStringInfo(&buffer, "%s",
+ quote_qualified_identifier(schema, varname));
+
+ if (objname)
+ *objname = list_make2(schema, varname);
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
diff --git a/src/backend/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c
new file mode 100644
index 0000000000..ea6b0960a3
--- /dev/null
+++ b/src/backend/catalog/pg_variable.c
@@ -0,0 +1,309 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.c
+ * schema variables
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_variable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+
+#include "nodes/makefuncs.h"
+
+#include "storage/lmgr.h"
+
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * Returns name of schema variable. When variable is not on path,
+ * then the name is qualified.
+ */
+char *
+schema_variable_get_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+ char *nspname;
+ char *result;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ if (VariableIsVisible(varid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ result = quote_qualified_identifier(nspname, varname);
+
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/*
+ * Returns varname field of pg_variable
+ */
+char *
+get_schema_variable_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ ReleaseSysCache(tup);
+
+ return varname;
+}
+
+/*
+ * Returns type, typmod of schema variable
+ */
+void
+get_schema_variable_type_typmod_collid(Oid varid, Oid *typid, int32 *typmod, Oid *collid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ *typid = varform->vartype;
+ *typmod = varform->vartypmod;
+ *collid = varform->varcollation;
+
+ ReleaseSysCache(tup);
+
+ return;
+}
+
+/*
+ * Fetch all fields of schema variable from the syscache.
+ */
+Variable *
+GetVariable(Oid varid, bool missing_ok)
+{
+ HeapTuple tup;
+ Variable *var;
+ Form_pg_variable varform;
+ Datum aclDatum;
+ Datum defexprDatum;
+ bool isnull;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (missing_ok)
+ return NULL;
+
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+ }
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ var = (Variable *) palloc(sizeof(Variable));
+ var->oid = varid;
+ var->name = pstrdup(NameStr(varform->varname));
+ var->namespace = varform->varnamespace;
+ var->typid = varform->vartype;
+ var->typmod = varform->vartypmod;
+ var->owner = varform->varowner;
+ var->collation = varform->varcollation;
+
+ /* Get defexpr */
+ defexprDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_vardefexpr,
+ &isnull);
+
+ if (!isnull)
+ var->defexpr = stringToNode(TextDatumGetCString(defexprDatum));
+ else
+ var->defexpr = NULL;
+
+ /* Get varacl */
+ aclDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_varacl,
+ &isnull);
+ if (!isnull)
+ var->acl = DatumGetAclPCopy(aclDatum);
+ else
+ var->acl = NULL;
+
+ ReleaseSysCache(tup);
+
+ return var;
+}
+
+ObjectAddress
+VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Oid varCollation,
+ Node *varDefexpr,
+ bool if_not_exists)
+{
+ Acl *varacl;
+ NameData varname;
+ bool nulls[Natts_pg_variable];
+ Datum values[Natts_pg_variable];
+ Relation rel;
+ HeapTuple tup,
+ oldtup;
+ TupleDesc tupdesc;
+ ObjectAddress myself,
+ referenced;
+ Oid retval;
+ int i;
+
+ for (i = 0; i < Natts_pg_variable; i++)
+ {
+ nulls[i] = false;
+ values[i] = (Datum) 0;
+ }
+
+ namestrcpy(&varname, varName);
+ values[Anum_pg_variable_varname - 1] = NameGetDatum(&varname);
+ values[Anum_pg_variable_varnamespace - 1] = ObjectIdGetDatum(varNamespace);
+ values[Anum_pg_variable_vartype - 1] = ObjectIdGetDatum(varType);
+ values[Anum_pg_variable_vartypmod - 1] = Int32GetDatum(varTypmod);
+ values[Anum_pg_variable_varowner - 1] = ObjectIdGetDatum(varOwner);
+ values[Anum_pg_variable_varcollation - 1] = ObjectIdGetDatum(varCollation);
+ /* proacl will be determined later */
+
+ if (varDefexpr)
+ values[Anum_pg_variable_vardefexpr - 1] = CStringGetTextDatum(nodeToString(varDefexpr));
+ else
+ nulls[Anum_pg_variable_vardefexpr - 1] = true;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+ tupdesc = RelationGetDescr(rel);
+
+ oldtup = SearchSysCache2(VARIABLENAMENSP,
+ PointerGetDatum(varName),
+ ObjectIdGetDatum(varNamespace));
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ if (if_not_exists)
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists, skipping",
+ varName)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists",
+ varName)));
+
+ heap_freetuple(oldtup);
+ heap_close(rel, RowExclusiveLock);
+
+ return InvalidObjectAddress;
+ }
+
+ varacl = get_user_default_acl(OBJECT_VARIABLE, varOwner,
+ varNamespace);
+
+ if (varacl != NULL)
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(varacl);
+ else
+ nulls[Anum_pg_variable_varacl - 1] = true;
+
+ tup = heap_form_tuple(tupdesc, values, nulls);
+ CatalogTupleInsert(rel, tup);
+
+ retval = HeapTupleGetOid(tup);
+
+ myself.classId = VariableRelationId;
+ myself.objectId = retval;
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = varNamespace;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on used type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = varType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on any roles mentioned in ACL */
+ if (varacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(varacl, &newmembers);
+ updateAclDependencies(VariableRelationId, retval, 0,
+ varOwner,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_freetuple(tup);
+
+ /* Post creation hook for new function */
+ InvokeObjectPostCreateHook(VariableRelationId, retval, 0);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..2cb5b1172d 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,7 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
- schemacmds.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
+ schemacmds.o seclabel.o sequence.o schemavariable.o statscmds.o subscriptioncmds.o \
tablecmds.o tablespace.o trigger.o tsearchcmds.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index eff325cc7d..a9d5e5e0ad 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -387,6 +387,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_VARIABLE:
{
ObjectAddress address;
Relation catalog;
@@ -504,6 +505,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
@@ -594,6 +596,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_TSDICT:
case OCLASS_TSTEMPLATE:
case OCLASS_TSCONFIG:
+ case OCLASS_VARIABLE:
{
Relation catalog;
@@ -852,6 +855,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_TABLESPACE:
case OBJECT_TSDICTIONARY:
case OBJECT_TSCONFIGURATION:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 01a999c2ac..fec2495e93 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -19,6 +19,7 @@
#include "commands/discard.h"
#include "commands/prepare.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "utils/guc.h"
#include "utils/portal.h"
@@ -48,6 +49,10 @@ DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
ResetTempTableNamespace();
break;
+ case DISCARD_VARIABLES:
+ ResetSchemaVariableCache();
+ break;
+
default:
elog(ERROR, "unrecognized DISCARD target: %d", stmt->target);
}
@@ -75,4 +80,5 @@ DiscardAll(bool isTopLevel)
ResetPlanCache();
ResetTempTableNamespace();
ResetSequenceCaches();
+ ResetSchemaVariableCache();
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e..426df246b3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -126,6 +126,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"TEXT SEARCH TEMPLATE", true},
{"TYPE", true},
{"USER MAPPING", true},
+ {"VARIABLE", true},
{"VIEW", true},
{NULL, false}
};
@@ -297,7 +298,8 @@ check_ddl_tag(const char *tag)
pg_strcasecmp(tag, "REVOKE") == 0 ||
pg_strcasecmp(tag, "DROP OWNED") == 0 ||
pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
+ pg_strcasecmp(tag, "SECURITY LABEL") == 0 ||
+ pg_strcasecmp(tag, "CREATE VARIABLE") == 0)
return EVENT_TRIGGER_COMMAND_TAG_OK;
/*
@@ -1146,6 +1148,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -1209,6 +1212,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
return true;
/*
@@ -2244,6 +2248,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2326,6 +2332,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b1556a..eb8c08baf3 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_PLAN_UTILITY:
/* OK */
break;
default:
diff --git a/src/backend/commands/schemavariable.c b/src/backend/commands/schemavariable.c
new file mode 100644
index 0000000000..6bdd200e40
--- /dev/null
+++ b/src/backend/commands/schemavariable.c
@@ -0,0 +1,492 @@
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
+#include "executor/executor.h"
+#include "executor/svariableReceiver.h"
+#include "nodes/execnodes.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/inval.h"
+#include "utils/memutils.h"
+#include "utils/lsyscache.h"
+#include "utils/snapmgr.h"
+#include "utils/syscache.h"
+
+/*
+ * The content of variables is not transactional. Due this fact the
+ * implementation of DROP can be simple, because although DROP VARIABLE
+ * can be reverted, the content of variable can be lost. In this example,
+ * DROP VARIABLE is same like reset variable.
+ */
+
+typedef struct SchemaVariableData
+{
+ Oid varid; /* pg_variable OID of this sequence (hash key) */
+ Oid typid; /* OID of the data type */
+ int32 typmod;
+ int16 typlen;
+ bool typbyval;
+ bool isnull;
+ bool freeval;
+ Datum value;
+ bool is_rowtype; /* true when variable is composite */
+ bool is_valid; /* true when variable was successfuly initialized */
+} SchemaVariableData;
+
+typedef SchemaVariableData *SchemaVariable;
+
+static HTAB *schemavarhashtab = NULL; /* hash table for session variables */
+static MemoryContext SchemaVariableMemoryContext = NULL;
+
+static bool first_time = true;
+static void create_schemavar_hashtable(void);
+static bool clean_cache_req = false;
+
+static void clean_cache(void);
+static void force_clean_cache(XactEvent event, void *arg);
+
+
+/*
+ * Save info about ncessity to clean hash table, because some
+ * schema variable was dropped. Don't do here more, recheck
+ * needs to be in transaction state.
+ */
+static void
+InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ if (cacheid != VARIABLEOID)
+ return;
+
+ clean_cache_req = true;
+}
+
+static void
+force_clean_cache(XactEvent event, void *arg)
+{
+ /*
+ * should continue only in transaction time, when
+ * syscache is available.
+ */
+ if (clean_cache_req && IsTransactionState())
+ {
+ clean_cache();
+ clean_cache_req = false;
+ }
+}
+
+static void
+clean_cache(void)
+{
+ HASH_SEQ_STATUS status;
+ SchemaVariable var;
+
+ if (!schemavarhashtab)
+ return;
+
+ hash_seq_init(&status, schemavarhashtab);
+
+ /*
+ * Every valid variable have to have entry in system
+ * catalog. Removed if there is nothing.
+ */
+ while ((var = (SchemaVariable) hash_seq_search(&status)) != NULL)
+ {
+ HeapTuple tp = InvalidOid;
+
+ tp = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var->varid));
+ if (!HeapTupleIsValid(tp))
+ {
+ elog(DEBUG1, "variable %d is removed from cache", var->varid);
+
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (hash_search(schemavarhashtab,
+ (void *) &var->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(DEBUG1, "hash table corrupted");
+ }
+ else
+ ReleaseSysCache(tp);
+ }
+}
+
+/*
+ * Create the hash table for storing schema variables
+ */
+static void
+create_schemavar_hashtable(void)
+{
+ HASHCTL ctl;
+
+ /* set callbacks */
+ if (first_time)
+ {
+ CacheRegisterSyscacheCallback(VARIABLEOID,
+ InvalidateSchemaVarCacheCallback,
+ (Datum) 0);
+
+ RegisterXactCallback(force_clean_cache, NULL);
+
+ first_time = false;
+ }
+
+ /* needs own long life memory context */
+ if (SchemaVariableMemoryContext == NULL)
+ {
+ SchemaVariableMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "schema variables",
+ ALLOCSET_START_SMALL_SIZES);
+ }
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(SchemaVariableData);
+ ctl.hcxt = SchemaVariableMemoryContext;
+
+ schemavarhashtab = hash_create("Schema variables", 64, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/*
+ * Fast drop complete content of schema variables
+ */
+void
+ResetSchemaVariableCache(void)
+{
+ if (schemavarhashtab)
+ {
+ hash_destroy(schemavarhashtab);
+ schemavarhashtab = NULL;
+ }
+
+ if (SchemaVariableMemoryContext != NULL)
+ {
+ MemoryContextReset(SchemaVariableMemoryContext);
+ }
+}
+
+/*
+ * Drop variable by OID
+ */
+void
+RemoveVariableById(Oid varid)
+{
+ Relation rel;
+ HeapTuple tup;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ CatalogTupleDelete(rel, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Creates new variable - entry in pg_catalog.pg_variable table
+ */
+ObjectAddress
+DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt)
+{
+ Oid namespaceid;
+ AclResult aclresult;
+ Oid typid;
+ int32 typmod;
+ Oid varowner = GetUserId();
+ Oid collation;
+ Oid typcollation;
+
+ Node *cooked_default = NULL;
+
+ namespaceid =
+ RangeVarGetAndCheckCreationNamespace(stmt->variable, NoLock, NULL);
+
+ typenameTypeIdAndMod(pstate, stmt->typeName, &typid, &typmod);
+ typcollation = get_typcollation(typid);
+
+ aclresult = pg_type_aclcheck(typid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error_type(aclresult, typid);
+
+ if (stmt->collClause)
+ collation = LookupCollation(pstate,
+ stmt->collClause->collname,
+ stmt->collClause->location);
+ else
+ collation = typcollation;;
+
+ /* Complain if COLLATE is applied to an uncollatable type */
+ if (OidIsValid(collation) && !OidIsValid(typcollation))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("collations are not supported by type %s",
+ format_type_be(typid)),
+ parser_errposition(pstate, stmt->collClause->location)));
+
+ if (stmt->defexpr)
+ {
+ cooked_default = transformExpr(pstate, stmt->defexpr,
+ EXPR_KIND_VARIABLE_DEFAULT);
+
+ cooked_default = coerce_to_specific_type(pstate,
+ cooked_default, typid, "DEFAULT");
+ assign_expr_collations(pstate, cooked_default);
+ }
+
+ return VariableCreate(stmt->variable->relname,
+ namespaceid,
+ typid,
+ typmod,
+ varowner,
+ collation,
+ cooked_default,
+ stmt->if_not_exists);
+}
+
+/*
+ * Try to search value in hash table. If doesn't
+ * exists insert it (and calculate defexpr if exists.
+ */
+static SchemaVariable
+PrepareSchemaVariableForReading(Oid varid)
+{
+ SchemaVariable svar;
+ Variable *var;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+ if (!found)
+ {
+ var = GetVariable(varid, false);
+ get_typlenbyval(var->typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = var->typid;
+ svar->typmod = var->typmod;
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+ svar->is_rowtype = type_is_rowtype(var->typid);
+
+ /* when we don't need calculate defexpr, value is valid already */
+ svar->is_valid = var->defexpr ? false : true;
+ }
+ else if (!svar->is_valid)
+ {
+ /* we need var to recalculate defexpr */
+ var = GetVariable(varid, false);
+ }
+ else
+ /* we don't need to go to sys cache */
+ var = NULL;
+
+ /*
+ * Initialize variable when it is necessary. It is fresh
+ * or last initialization was not successfull.
+ */
+ if (var != NULL && var->defexpr && !svar->is_valid)
+ {
+ MemoryContext oldcontext = NULL;
+
+ Datum value = (Datum) 0;
+ bool null;
+ EState *estate = NULL;
+ Expr *defexpr;
+ ExprState *defexprs;
+
+ /* Prepare default expr */
+ estate = CreateExecutorState();
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ defexpr = expression_planner((Expr *) var->defexpr);
+ defexprs = ExecInitExpr(defexpr, NULL);
+ value = ExecEvalExprSwitchContext(defexprs, GetPerTupleExprContext(estate), &null);
+
+ MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!null)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ FreeExecutorState(estate);
+ }
+
+ if (!svar->is_valid)
+ elog(ERROR, "the content of variable is not valid");
+
+ return svar;
+}
+
+/*
+ * Returns content of variable. We expext secured access now.
+ * Secure check should be done before.
+ */
+Datum
+GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid, bool copy)
+{
+ SchemaVariable svar;
+ Datum value;
+ bool isnull;
+
+ svar = PrepareSchemaVariableForReading(varid);
+ Assert(svar != NULL);
+
+ if (expected_typid != svar->typid)
+ elog(ERROR, "type of variable \"%s\" is different than expected",
+ schema_variable_get_name(varid));
+
+ value = svar->value;
+ isnull = svar->isnull;
+
+ *isNull = isnull;
+
+ if (!isnull && copy)
+ return datumCopy(value, svar->typbyval, svar->typlen);
+
+ return value;
+}
+
+/*
+ * Write value to variable. We expect secured access in this moment.
+ * In this time, we recheck syschache about used type.
+ */
+void
+SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod)
+{
+ MemoryContext oldcontext = NULL;
+
+ SchemaVariable svar;
+ Oid var_typid;
+ int32 var_typmod;
+ Oid var_collid;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+
+ get_schema_variable_type_typmod_collid(varid,
+ &var_typid,
+ &var_typmod,
+ &var_collid);
+
+ /* check types first */
+ if (var_typid != typid)
+ elog(ERROR, "type of expression is different than schema variable type");
+
+ if (found)
+ {
+ /* release current content first */
+ if (svar->freeval)
+ {
+ pfree(DatumGetPointer(svar->value));
+ svar->value = (Datum) 0;
+ svar->isnull = true;
+ svar->freeval = false;
+ }
+ }
+
+ get_typlenbyval(typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = typid;
+ svar->typmod = typmod;
+
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+
+ svar->is_rowtype = type_is_rowtype(typid);
+ svar->is_valid = false;
+
+ oldcontext = MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!isNull)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+}
+
+void
+doLetStmt(PlannedStmt *pstmt,
+ ParamListInfo params,
+ QueryEnvironment *queryEnv,
+ const char *queryString)
+{
+ QueryDesc *queryDesc;
+ DestReceiver *dest;
+
+ PushCopiedSnapshot(GetActiveSnapshot());
+ UpdateActiveSnapshotCommandId();
+
+ /* Create dest receiver for LET */
+ dest = CreateDestReceiver(DestVariable);
+
+ SetVariableDestReceiverParams(dest, pstmt->resultVariable);
+
+ /* Create a QueryDesc requesting no output */
+ queryDesc = CreateQueryDesc(pstmt, queryString,
+ GetActiveSnapshot(),
+ InvalidSnapshot,
+ dest, params, queryEnv, 0);
+
+ ExecutorStart(queryDesc, 0);
+ ExecutorRun(queryDesc, ForwardScanDirection, 2L, true);
+ ExecutorFinish(queryDesc);
+ ExecutorEnd(queryDesc);
+
+ FreeQueryDesc(queryDesc);
+
+ PopActiveSnapshot();
+}
+
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f6210226e9..59dc30f14d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9650,6 +9650,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
/*
* We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index cc09895fa5..ee8ff7da9e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -29,6 +29,6 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
nodeCtescan.o nodeNamedtuplestorescan.o nodeWorktablescan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o \
- nodeTableFuncscan.o
+ nodeTableFuncscan.o svariableReceiver.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d7..bb9bf53e1c 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -33,6 +33,7 @@
#include "access/nbtree.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -727,6 +728,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
Param *param = (Param *) node;
ParamListInfo params;
+ AclResult aclresult;
switch (param->paramkind)
{
@@ -736,6 +738,28 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
break;
+
+ case PARAM_VARIABLE:
+
+ /* Check permission to read schema variable */
+ aclresult = pg_variable_aclcheck(param->paramid, GetUserId(), ACL_READ);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE,
+ schema_variable_get_name(param->paramid));
+
+ /*
+ * Using varoid as paramid is not practical. Better to recount
+ * used schema variables from zero, and later to use paramid like
+ * offset.
+ */
+ scratch.opcode = EEOP_PARAM_VARIABLE;
+ scratch.d.vparam.paramid = state->nvariables++;
+ scratch.d.vparam.varoid = param->paramid;
+ scratch.d.vparam.paramtype = param->paramtype;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+
case PARAM_EXTERN:
/*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25aae5..4462dcc952 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -351,6 +352,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1007,6 +1009,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_VARIABLE)
+ {
+ /* iut of line implementation; too large */
+ ExecEvalParamVariable(state, op, econtext);
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_CASE_TESTVAL)
{
/*
@@ -2323,6 +2332,79 @@ ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
errmsg("no value found for parameter %d", paramId)));
}
+/*
+ * Evaluate a PARAM_VARIABLE parameter
+ */
+void
+ExecEvalParamVariable(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ EState *estate = econtext->ecxt_estate;
+
+ /*
+ * We should to ensure stable behave of schema variables in queries. It is
+ * important, because optimizer uses these values as stable, like extern
+ * parameters, what is nice, because queries are optimized well. So, don't
+ * try to access variables directly, use this query variable cache.
+ * This cache cannot be used when EState is shared - PLpgSQL did it for
+ * simple expressions.
+ */
+ if (estate && !estate->es_shared)
+ {
+ int paramid = op->d.vparam.paramid;
+
+ if (estate->es_nvariables == 0)
+ {
+ MemoryContext old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* initialize estate schema variable cache */
+
+ estate->es_nvariables = state->nvariables;
+ estate->es_varnulls = palloc(sizeof(bool) * state->nvariables);
+ estate->es_vartypes = palloc0(sizeof(Oid) * state->nvariables);
+ estate->es_varvalues = palloc(sizeof(Datum) * state->nvariables);
+
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ Assert(estate->es_nvariables == state->nvariables);
+ Assert(estate->es_nvariables > paramid);
+
+ if (!OidIsValid(estate->es_vartypes[paramid]))
+ {
+ MemoryContext old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* copy variable to estate schema variable cache */
+ estate->es_varvalues[paramid] =
+ GetSchemaVariable(op->d.vparam.varoid,
+ &estate->es_varnulls[paramid],
+ op->d.vparam.paramtype,
+ true);
+ estate->es_vartypes[paramid] = op->d.vparam.paramtype;
+
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ Assert(OidIsValid(estate->es_vartypes[paramid]));
+
+ *op->resvalue = estate->es_varvalues[paramid];
+ *op->resnull = estate->es_varnulls[paramid];
+ }
+ else
+ {
+ Datum d;
+ bool isnull;
+
+ /* read content of variable directly */
+ d = GetSchemaVariable(op->d.vparam.varoid,
+ &isnull,
+ op->d.vparam.paramtype,
+ false);
+
+ *op->resvalue = d;
+ *op->resnull = isnull;
+ }
+}
+
/*
* Evaluate a SQLValueFunction expression.
*/
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index c583e020a0..797c1f43b3 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,9 +43,11 @@
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_publication.h"
+#include "catalog/pg_variable.h"
#include "commands/matview.h"
#include "commands/trigger.h"
#include "executor/execdebug.h"
+#include "executor/svariableReceiver.h"
#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -204,12 +206,18 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
*/
estate->es_queryEnv = queryDesc->queryEnv;
+ /*
+ * Result can be stored in schema variable.
+ */
+ estate->es_result_variable = queryDesc->plannedstmt->resultVariable;
+
/*
* If non-read-only query, set the command ID to mark output tuples with
*/
switch (queryDesc->operation)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
@@ -345,6 +353,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate->es_lastoid = InvalidOid;
sendTuples = (operation == CMD_SELECT ||
+ OidIsValid(estate->es_result_variable) ||
queryDesc->plannedstmt->hasReturning);
if (sendTuples)
@@ -924,6 +933,17 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_num_root_result_relations = 0;
}
+ if (OidIsValid(estate->es_result_variable))
+ {
+ AclResult aclresult;
+ Oid varid = estate->es_result_variable;
+
+ /* Ensure this variable is writeable */
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, schema_variable_get_name(varid));
+ }
+
/*
* Similarly, we have to lock relations selected FOR [KEY] UPDATE/SHARE
* before we initialize the plan tree, else we'd be risking lock upgrades.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5b3eaec80b..eca7805517 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -102,6 +102,7 @@ CreateExecutorState(void)
/*
* Initialize all fields of the Executor State structure
*/
+ estate->es_shared = false;
estate->es_direction = ForwardScanDirection;
estate->es_snapshot = InvalidSnapshot; /* caller must initialize this */
estate->es_crosscheck_snapshot = InvalidSnapshot; /* no crosscheck */
diff --git a/src/backend/executor/svariableReceiver.c b/src/backend/executor/svariableReceiver.c
new file mode 100644
index 0000000000..0eac4b5d0c
--- /dev/null
+++ b/src/backend/executor/svariableReceiver.c
@@ -0,0 +1,145 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.c
+ * An implementation of DestReceiver that stores the result value in
+ * a schema variable.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/svariableReceiver.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/tuptoaster.h"
+#include "executor/svariableReceiver.h"
+#include "commands/schemavariable.h"
+
+typedef struct
+{
+ DestReceiver pub;
+ Oid varid;
+ Oid typid;
+ int32 typmod;
+ int typlen;
+ int slot_offset;
+ int rows;
+} svariableState;
+
+
+/*
+ * Prepare to receive tuples from executor.
+ */
+static void
+svariableStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+ svariableState *myState = (svariableState *) self;
+ int natts = typeinfo->natts;
+ int outcols = 0;
+ int i;
+
+ for (i = 0; i < natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+
+ if (attr->attisdropped)
+ continue;
+
+ if (++outcols > 1)
+ elog(ERROR, "svariable DestReceiver can take only one attribute");
+
+ myState->typid = attr->atttypid;
+ myState->typmod = attr->atttypmod;
+ myState->typlen = attr->attlen;
+ myState->slot_offset = i;
+ }
+
+ myState->rows = 0;
+}
+
+/*
+ * Receive a tuple from the executor and store it in schema variable.
+ */
+static bool
+svariableReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
+{
+ svariableState *myState = (svariableState *) self;
+ Datum value;
+ bool isnull;
+ bool freeval = false;
+
+ /* Make sure the tuple is fully deconstructed */
+ slot_getallattrs(slot);
+
+ value = slot->tts_values[myState->slot_offset];
+ isnull = slot->tts_isnull[myState->slot_offset];
+
+ if (myState->typlen == -1 && !isnull && VARATT_IS_EXTERNAL(DatumGetPointer(value)))
+ {
+ value = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+ DatumGetPointer(value)));
+ freeval = true;
+ }
+
+ SetSchemaVariable(myState->varid, value, isnull, myState->typid, myState->typmod);
+
+ if (freeval)
+ pfree(DatumGetPointer(value));
+
+ return true;
+}
+
+/*
+ * Clean up at end of an executor run
+ */
+static void
+svariableShutdownReceiver(DestReceiver *self)
+{
+ /* Do nothing */
+}
+
+/*
+ * Destroy receiver when done with it
+ */
+static void
+svariableDestroyReceiver(DestReceiver *self)
+{
+ pfree(self);
+}
+
+/*
+ * Initially create a DestReceiver object.
+ */
+DestReceiver *
+CreateVariableDestReceiver(void)
+{
+ svariableState *self = (svariableState *) palloc0(sizeof(svariableState));
+
+ self->pub.receiveSlot = svariableReceiveSlot;
+ self->pub.rStartup = svariableStartupReceiver;
+ self->pub.rShutdown = svariableShutdownReceiver;
+ self->pub.rDestroy = svariableDestroyReceiver;
+ self->pub.mydest = DestVariable;
+
+ /* private fields will be set by SetVariableDestReceiverParams */
+
+ return (DestReceiver *) self;
+}
+
+/*
+ * Set parameters for a VariableDestReceiver
+ */
+void
+SetVariableDestReceiverParams(DestReceiver *self, Oid varid)
+{
+ svariableState *myState = (svariableState *) self;
+
+ Assert(myState->pub.mydest == DestVariable);
+ Assert(OidIsValid(varid));
+
+ myState->varid = varid;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c8220cf65..fcaa2db51a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(nonleafResultRelations);
COPY_NODE_FIELD(rootResultRelations);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks);
@@ -3000,6 +3001,7 @@ _copyQuery(const Query *from)
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_SCALAR_FIELD(hasAggs);
COPY_SCALAR_FIELD(hasWindowFuncs);
COPY_SCALAR_FIELD(hasTargetSRFs);
@@ -3118,6 +3120,18 @@ _copySelectStmt(const SelectStmt *from)
return newnode;
}
+static LetStmt *
+_copyLetStmt(const LetStmt *from)
+{
+ LetStmt *newnode = makeNode(LetStmt);
+
+ COPY_NODE_FIELD(target);
+ COPY_NODE_FIELD(selectStmt);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
static SetOperationStmt *
_copySetOperationStmt(const SetOperationStmt *from)
{
@@ -5166,6 +5180,9 @@ copyObjectImpl(const void *from)
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
+ case T_LetStmt:
+ retval = _copyLetStmt(from);
+ break;
case T_SetOperationStmt:
retval = _copySetOperationStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 378f2facb8..3ec472e19b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -949,6 +949,7 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_SCALAR_FIELD(canSetTag);
COMPARE_NODE_FIELD(utilityStmt);
COMPARE_SCALAR_FIELD(resultRelation);
+ COMPARE_SCALAR_FIELD(resultVariable);
COMPARE_SCALAR_FIELD(hasAggs);
COMPARE_SCALAR_FIELD(hasWindowFuncs);
COMPARE_SCALAR_FIELD(hasTargetSRFs);
@@ -1057,6 +1058,16 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
return true;
}
+static bool
+_equalLetStmt(const LetStmt *a, const LetStmt *b)
+{
+ COMPARE_NODE_FIELD(target);
+ COMPARE_NODE_FIELD(selectStmt);
+
+ return true;
+}
+
+
static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
@@ -3225,6 +3236,9 @@ equal(const void *a, const void *b)
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
break;
+ case T_LetStmt:
+ retval = _equalLetStmt(a, b);
+ break;
case T_SetOperationStmt:
retval = _equalSetOperationStmt(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6269f474d2..46404ff9ac 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -278,6 +278,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(nonleafResultRelations);
WRITE_NODE_FIELD(rootResultRelations);
+ WRITE_OID_FIELD(resultVariable);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks);
@@ -2793,6 +2794,16 @@ _outSelectStmt(StringInfo str, const SelectStmt *node)
WRITE_NODE_FIELD(rarg);
}
+static void
+_outLetStmt(StringInfo str, const LetStmt *node)
+{
+ WRITE_NODE_TYPE("LET");
+
+ WRITE_NODE_FIELD(target);
+ WRITE_NODE_FIELD(selectStmt);
+ WRITE_LOCATION_FIELD(location);
+}
+
static void
_outFuncCall(StringInfo str, const FuncCall *node)
{
@@ -2971,6 +2982,7 @@ _outQuery(StringInfo str, const Query *node)
appendStringInfoString(str, " :utilityStmt <>");
WRITE_INT_FIELD(resultRelation);
+ WRITE_INT_FIELD(resultVariable);
WRITE_BOOL_FIELD(hasAggs);
WRITE_BOOL_FIELD(hasWindowFuncs);
WRITE_BOOL_FIELD(hasTargetSRFs);
@@ -4191,6 +4203,9 @@ outNode(StringInfo str, const void *obj)
case T_SelectStmt:
_outSelectStmt(str, obj);
break;
+ case T_LetStmt:
+ _outLetStmt(str, obj);
+ break;
case T_ColumnDef:
_outColumnDef(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3254524223..4454327549 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -242,6 +242,7 @@ _readQuery(void)
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
+ READ_INT_FIELD(resultVariable);
READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasWindowFuncs);
READ_BOOL_FIELD(hasTargetSRFs);
@@ -1485,6 +1486,7 @@ _readPlannedStmt(void)
READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(nonleafResultRelations);
READ_NODE_FIELD(rootResultRelations);
+ READ_OID_FIELD(resultVariable);
READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 96bf0601a8..4573a88f35 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -335,7 +335,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
*/
if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
IsUnderPostmaster &&
- parse->commandType == CMD_SELECT &&
+ (parse->commandType == CMD_SELECT ||
+ parse->commandType == CMD_PLAN_UTILITY) &&
!parse->hasModifyingCTE &&
max_parallel_workers_per_gather > 0 &&
!IsParallelWorker() &&
@@ -352,6 +353,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->parallelModeOK = false;
}
+
+
/*
* glob->parallelModeNeeded is normally set to false here and changed to
* true during plan creation if a Gather or Gather Merge plan is actually
@@ -521,6 +524,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->resultRelations = glob->resultRelations;
result->nonleafResultRelations = glob->nonleafResultRelations;
result->rootResultRelations = glob->rootResultRelations;
+ result->resultVariable = parse->resultVariable;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks;
@@ -2173,7 +2177,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
* inheritance_planner, add the ModifyTable node.
*/
- if (parse->commandType != CMD_SELECT && !inheritance_update)
+ if (parse->commandType != CMD_SELECT && parse->commandType != CMD_PLAN_UTILITY && !inheritance_update)
{
List *withCheckOptionLists;
List *returningLists;
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 8603feef2b..2923e3fcc7 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -71,6 +71,7 @@ preprocess_targetlist(PlannerInfo *root)
{
Query *parse = root->parse;
int result_relation = parse->resultRelation;
+ int result_variable = parse->resultVariable;
List *range_table = parse->rtable;
CmdType command_type = parse->commandType;
RangeTblEntry *target_rte = NULL;
@@ -96,6 +97,10 @@ preprocess_targetlist(PlannerInfo *root)
target_relation = heap_open(target_rte->relid, NoLock);
}
+ else if (result_variable)
+ {
+ Assert(command_type == CMD_PLAN_UTILITY);
+ }
else
Assert(command_type == CMD_SELECT);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a04ad6e99e..8f023225c6 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1254,7 +1254,8 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
{
Param *param = (Param *) node;
- if (param->paramkind == PARAM_EXTERN)
+ if (param->paramkind == PARAM_EXTERN ||
+ param->paramkind == PARAM_VARIABLE)
return false;
if (param->paramkind != PARAM_EXEC ||
@@ -4799,7 +4800,7 @@ substitute_actual_parameters_mutator(Node *node,
{
if (node == NULL)
return NULL;
- if (IsA(node, Param))
+ if (IsA(node, Param) && ((Param *) node)->paramkind != PARAM_VARIABLE)
{
Param *param = (Param *) node;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8369e3ad62..fc0cf34c7d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1272,7 +1272,7 @@ get_relation_constraints(PlannerInfo *root,
* descriptor, instead of constraint exclusion which is driven by the
* individual partition's partition constraint.
*/
- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
+ if (enable_partition_pruning && root->parse->commandType != CMD_SELECT && root->parse->commandType != CMD_PLAN_UTILITY)
{
List *pcqual = RelationGetPartitionQual(relation);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c601b6d40d..8a724fe3bf 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,7 +25,10 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/namespace.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -44,6 +47,8 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -78,6 +83,8 @@ static Query *transformCreateTableAsStmt(ParseState *pstate,
CreateTableAsStmt *stmt);
static Query *transformCallStmt(ParseState *pstate,
CallStmt *stmt);
+static Query *transformLetStmt(ParseState *pstate,
+ LetStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown);
#ifdef RAW_EXPRESSION_COVERAGE_TEST
@@ -267,6 +274,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
+ case T_LetStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
@@ -327,6 +335,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
(CallStmt *) parseTree);
break;
+ case T_LetStmt:
+ result = transformLetStmt(pstate,
+ (LetStmt *) parseTree);
+ break;
+
default:
/*
@@ -367,6 +380,7 @@ analyze_requires_snapshot(RawStmt *parseTree)
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
+ case T_LetStmt:
result = true;
break;
@@ -1567,6 +1581,204 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
return qry;
}
+/*
+ * transformLetStmt -
+ * transform an Let Statement
+ */
+static Query *
+transformLetStmt(ParseState *pstate, LetStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ List *exprList = NIL;
+ List *exprListCoer = NIL;
+ List *indirection = NIL;
+ ListCell *lc;
+ Query *selectQuery;
+ int i = 0;
+
+ Oid varid;
+
+ ParseExprKind sv_expr_kind;
+ char *attrname = NULL;
+ bool not_unique;
+ bool is_rowtype;
+ Oid typid;
+ int32 typmod;
+ Oid collid;
+
+ AclResult aclresult;
+ List *names = NULL;
+ int indirection_start;
+
+ sv_expr_kind = pstate->p_expr_kind;
+ pstate->p_expr_kind = EXPR_KIND_LET;
+
+ /* There can't be any outer WITH to worry about */
+ Assert(pstate->p_ctenamespace == NIL);
+
+ /* Exec this command as utility */
+ qry->commandType = CMD_PLAN_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ names = NamesFromList(stmt->target);
+
+ varid = identify_variable(names, &attrname, ¬_unique);
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("target \"%s\" of LET command is ambiguous",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ if (!OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("schema variable \"%s\" doesn't exists",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ qry->resultVariable = varid;
+
+ get_schema_variable_type_typmod_collid(varid, &typid, &typmod, &collid);
+
+ is_rowtype = type_is_rowtype(typid);
+
+ if (attrname && !is_rowtype)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("target variable \"%s\" is not row type",
+ schema_variable_get_name(varid)),
+ parser_errposition(pstate, stmt->location)));
+
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, NameListToString(names));
+
+ selectQuery = transformStmt(pstate, stmt->selectStmt);
+
+ /* The grammar should have produced a SELECT */
+ if (!IsA(selectQuery, Query) ||
+ selectQuery->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in LET ... SELECT");
+
+ /*----------
+ * Generate an expression list for the LET that selects all the
+ * non-resjunk columns from the subquery.
+ *----------
+ */
+ exprList = NIL;
+ foreach(lc, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->resjunk)
+ continue;
+
+ exprList = lappend(exprList, tle->expr);
+ }
+
+ /*
+ * Because doesn't support pattern matching, don't allow multicolumn result
+ */
+ if (list_length(exprList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expression is not scalar value"),
+ parser_errposition(pstate,
+ exprLocation((Node *) exprList))));
+
+ indirection_start = list_length(names) - (attrname ? 1 : 0);
+ indirection = list_copy_tail(stmt->target, indirection_start);
+
+ exprListCoer = NIL;
+ foreach(lc, exprList)
+ {
+ Node *orig_expr = (Node*) lfirst(lc);
+ Oid exprtypid = exprType((Node *) orig_expr);
+ Param *param = makeNode(Param);
+ Expr *expr = NULL;
+
+ param->paramkind = PARAM_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (indirection != NULL)
+ {
+ bool targetIsArray;
+ char *targetName;
+
+ targetName = attrname != NULL ? attrname : get_schema_variable_name(varid);
+ targetIsArray = OidIsValid(get_element_type(typid));
+
+ expr = (Expr *)
+ transformAssignmentIndirection(pstate,
+ (Node *) param,
+ targetName,
+ targetIsArray,
+ typid,
+ typmod,
+ InvalidOid,
+ list_head(indirection),
+ (Node *) orig_expr,
+ stmt->location);
+ }
+ else
+ expr = (Expr *)
+ coerce_to_target_type(pstate,
+ (Node *) orig_expr,
+ exprtypid,
+ typid, typmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ stmt->location);
+
+ if (expr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable \"%s\" is of type %s,"
+ " but expression is of type %s",
+ schema_variable_get_name(varid),
+ format_type_be(typid),
+ format_type_be(exprtypid)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *) orig_expr))));
+
+ exprListCoer = lappend(exprListCoer, expr);
+ }
+
+ /*
+ * Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
+ */
+ qry->targetList = NIL;
+ foreach(lc, exprListCoer)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ tle = makeTargetEntry(expr,
+ i + 1,
+ FigureColname((Node *)expr),
+ false);
+ qry->targetList = lappend(qry->targetList, tle);
+ }
+
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+ qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+
+ assign_query_collations(pstate, qry);
+
+ pstate->p_expr_kind = sv_expr_kind;
+
+ return qry;
+}
+
+
/*
* transformSetOperationStmt -
* transforms a set-operations tree
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 87f5e95827..4310c28538 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -257,8 +257,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSchemaVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -268,7 +268,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DropTransformStmt
DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
- ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
+ LetStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -400,6 +400,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
TriggerTransitions TriggerReferencing
publication_name_list
vacuum_relation_list opt_vacuum_relation_list
+ let_target
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -584,6 +585,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> optSchemaVarDefExpr
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -649,7 +651,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+ LEADING LEAKPROOF LEAST LEFT LET LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -687,8 +689,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -878,6 +880,7 @@ stmt :
| CreatePolicyStmt
| CreatePLangStmt
| CreateSchemaStmt
+ | CreateSchemaVarStmt
| CreateSeqStmt
| CreateStmt
| CreateSubscriptionStmt
@@ -917,6 +920,7 @@ stmt :
| ImportForeignSchemaStmt
| IndexStmt
| InsertStmt
+ | LetStmt
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
@@ -1808,7 +1812,12 @@ DiscardStmt:
n->target = DISCARD_SEQUENCES;
$$ = (Node *) n;
}
-
+ | DISCARD VARIABLES
+ {
+ DiscardStmt *n = makeNode(DiscardStmt);
+ n->target = DISCARD_VARIABLES;
+ $$ = (Node *) n;
+ }
;
@@ -4479,6 +4488,44 @@ create_extension_opt_item:
}
;
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE VARIABLE varname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSchemaVarStmt:
+ CREATE OptTemp VARIABLE qualified_name opt_as Typename opt_collate_clause optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $4->relpersistence = $2;
+ n->variable = $4;
+ n->typeName = $6;
+ n->collClause = $7;
+ n->defexpr = $8;
+ n->if_not_exists = false;
+ $$ = (Node *) n;
+ }
+ | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename opt_collate_clause optSchemaVarDefExpr
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $7->relpersistence = $2;
+ n->variable = $7;
+ n->typeName = $9;
+ n->collClause = $10;
+ n->defexpr = $11;
+ n->if_not_exists = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+optSchemaVarDefExpr: DEFAULT b_expr { $$ = $2; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+
+
/*****************************************************************************
*
* ALTER EXTENSION name UPDATE [ TO version ]
@@ -6335,6 +6382,7 @@ drop_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name_list */
@@ -6604,6 +6652,7 @@ comment_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -6742,6 +6791,7 @@ security_label_type_any_name:
| TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -7163,6 +7213,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| ALL TABLES IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7203,6 +7261,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
;
@@ -7363,6 +7429,7 @@ defacl_privilege_target:
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
;
@@ -8959,6 +9026,25 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newname = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
opt_column: COLUMN { $$ = COLUMN; }
@@ -9277,6 +9363,25 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newschema = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
/*****************************************************************************
@@ -9512,6 +9617,14 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
@@ -10693,6 +10806,7 @@ ExplainableStmt:
| CreateMatViewStmt
| RefreshMatViewStmt
| ExecuteStmt /* by default all are $$=$1 */
+ | LetStmt
;
explain_option_list:
@@ -10750,6 +10864,7 @@ PreparableStmt:
| InsertStmt
| UpdateStmt
| DeleteStmt /* by default all are $$=$1 */
+ | LetStmt
;
/*****************************************************************************
@@ -11148,6 +11263,44 @@ opt_hold: /* EMPTY */ { $$ = 0; }
| WITHOUT HOLD { $$ = 0; }
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * LET STATEMENTS
+ *
+ *****************************************************************************/
+LetStmt: LET let_target '=' a_expr
+ {
+ LetStmt *n = makeNode(LetStmt);
+ SelectStmt *select = makeNode(SelectStmt);
+ ResTarget *res = makeNode(ResTarget);
+
+ n->target = $2;
+
+ /* Create target list for implicit query */
+ res->name = NULL;
+ res->indirection = NIL;
+ res->val = (Node *) $4;
+ res->location = @4;
+
+ select->targetList = list_make1(res);
+ n->selectStmt = (Node *) select;
+
+ n->location = @2;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+let_target:
+ ColId opt_indirection
+ {
+ $$ = list_make1(makeString($1));
+ if ($2)
+ $$ = list_concat($$,
+ check_indirection($2, yyscanner));
+ }
+
/*****************************************************************************
*
* QUERY:
@@ -15127,6 +15280,7 @@ unreserved_keyword:
| LARGE_P
| LAST_P
| LEAKPROOF
+ | LET
| LEVEL
| LISTEN
| LOAD
@@ -15275,6 +15429,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 61727e1d71..6823612fba 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -349,6 +349,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
Assert(false); /* can't happen */
break;
case EXPR_KIND_OTHER:
+ case EXPR_KIND_LET:
/*
* Accept aggregate/grouping here; caller must throw error if
@@ -465,6 +466,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
if (isAgg)
err = _("aggregate functions are not allowed in DEFAULT expressions");
@@ -879,6 +881,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("window functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -902,6 +905,8 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_CALL_ARGUMENT:
err = _("window functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("window functions are not allowed in LET statement");
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a9b6..cc614b3902 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -37,6 +38,7 @@
#include "utils/date.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
+#include "utils/typcache.h"
#include "utils/xml.h"
@@ -116,6 +118,9 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
+static Node *makeParamSchemaVariable(ParseState *pstate,
+ Oid varid, Oid typid, int32 typmod, Oid collid,
+ char *attrname, int location);
static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
int location);
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
@@ -512,6 +517,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
+ Oid varid = InvalidOid;
+ char *attrname = NULL;
+ bool not_unique;
+
RangeTblEntry *rte;
int levels_up;
enum
@@ -749,6 +758,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break;
}
+ varid = identify_variable(cref->fields, &attrname, ¬_unique);
+
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("schema variable reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+
/*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
@@ -773,6 +791,72 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
parser_errposition(pstate, cref->location)));
}
+ if (OidIsValid(varid))
+ {
+ Oid typid;
+ int32 typmod;
+ Oid collid;
+
+ get_schema_variable_type_typmod_collid(varid, &typid, &typmod, &collid);
+
+ if (node != NULL)
+ {
+ /*
+ * some collision can be solved simply here to reduce errors
+ * based on simply existence of some variables. Often error
+ * can be using alias same like variable name. In this case,
+ * when we found column reference, and we found reference to
+ * possible composite variable, but the variable is not composite,
+ * then we can ignore the variable as simply improper, and we
+ * use column reference only.
+ */
+ if (attrname)
+ {
+ if (type_is_rowtype(typid))
+ {
+ TupleDesc tupdesc;
+ bool found = false;
+ int i;
+
+ /* slow part, I hope it will not be to often */
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ if (namestrcmp(&(TupleDescAttr(tupdesc, i)->attname), attrname) == 0 &&
+ !TupleDescAttr(tupdesc, i)->attisdropped)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ FreeTupleDesc(tupdesc);
+
+ /* there are not composite variable with this field */
+ if (!found)
+ varid = InvalidOid;
+ }
+ else
+ /* there are not composite variable with this name */
+ varid = InvalidOid;
+ }
+
+ /* Raise error if varid is still valid. It should be really amigonuous */
+ if (OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ errdetail("The qualified identifier can be column reference or schema variable reference"),
+ parser_errposition(pstate, cref->location)));
+ }
+
+ if (OidIsValid(varid))
+ node = makeParamSchemaVariable(pstate,
+ varid, typid, typmod, collid,
+ attrname, cref->location);
+ }
+
/*
* Throw error if no translation found.
*/
@@ -807,6 +891,60 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return node;
}
+/*
+ * Generate param variable for reference to schema variable
+ */
+static Node *
+makeParamSchemaVariable(ParseState *pstate, Oid varid, Oid typid, int32 typmod, Oid collid, char *attrname, int location)
+{
+ Param *param;
+
+ param = makeNode(Param);
+
+ param->paramkind = PARAM_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+ param->paramcollid = collid;
+
+ if (attrname != NULL)
+ {
+ TupleDesc tupdesc;
+ int i;
+
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+
+ if (strcmp(attrname, NameStr(att->attname)) == 0 &&
+ !att->attisdropped)
+ {
+ /* Success, so generate a FieldSelect expression */
+ FieldSelect *fselect = makeNode(FieldSelect);
+
+ fselect->arg = (Expr *) param;
+ fselect->fieldnum = i + 1;
+ fselect->resulttype = att->atttypid;
+ fselect->resulttypmod = att->atttypmod;
+ /* save attribute's collation for parse_collate.c */
+ fselect->resultcollid = att->attcollation;
+
+ ReleaseTupleDesc(tupdesc);
+ return (Node *) fselect;
+ }
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("could not identify column \"%s\" in variable", attrname),
+ parser_errposition(pstate, location)));
+ }
+
+ return (Node *) param;
+}
+
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
@@ -1818,6 +1956,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
case EXPR_KIND_VALUES_SINGLE:
+ case EXPR_KIND_LET:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
@@ -1826,6 +1965,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -3460,6 +3600,7 @@ ParseExprKindName(ParseExprKind exprKind)
return "CHECK";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
return "DEFAULT";
case EXPR_KIND_INDEX_EXPRESSION:
return "index expression";
@@ -3475,6 +3616,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "PARTITION BY";
case EXPR_KIND_CALL_ARGUMENT:
return "CALL";
+ case EXPR_KIND_LET:
+ return "LET";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 44257154b8..b2c9900e00 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2347,6 +2347,7 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("set-returning functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -2370,6 +2371,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_CALL_ARGUMENT:
err = _("set-returning functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("set-returning functions are not allowed in CALL arguments");
+ break;
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4932e58022..c60fe011f7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -35,16 +35,6 @@
static void markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
Var *var, int levelsup);
-static Node *transformAssignmentIndirection(ParseState *pstate,
- Node *basenode,
- const char *targetName,
- bool targetIsArray,
- Oid targetTypeId,
- int32 targetTypMod,
- Oid targetCollation,
- ListCell *indirection,
- Node *rhs,
- int location);
static Node *transformAssignmentSubscripts(ParseState *pstate,
Node *basenode,
const char *targetName,
@@ -672,7 +662,7 @@ updateTargetListEntry(ParseState *pstate,
* might want to decorate indirection cells with their own location info,
* in which case the location argument could probably be dropped.)
*/
-static Node *
+Node *
transformAssignmentIndirection(ParseState *pstate,
Node *basenode,
const char *targetName,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 3123ee274d..10737d422d 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3350,7 +3350,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
* get executed. Also, utilities aren't rewritten at all (do we still
* need that check?)
*/
- if (event != CMD_SELECT && event != CMD_UTILITY)
+ if (event != CMD_SELECT && event != CMD_UTILITY && event != CMD_PLAN_UTILITY)
{
int result_relation;
RangeTblEntry *rt_entry;
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 61ef396d8a..6a068af799 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -212,7 +212,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
}
/*
- * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
+ * For SELECT, LET, UPDATE and DELETE, add security quals to enforce the USING
* policies. These security quals control access to existing table rows.
* Restrictive policies are combined together using AND, and permissive
* policies are combined together using OR.
@@ -222,6 +222,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
&restrictive_policies);
if (commandType == CMD_SELECT ||
+ commandType == CMD_PLAN_UTILITY ||
commandType == CMD_UPDATE ||
commandType == CMD_DELETE)
add_security_quals(rt_index,
@@ -423,6 +424,7 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
switch (cmd)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
if (policy->polcmd == ACL_SELECT_CHR)
cmd_matches = true;
break;
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index c95a4d519d..47fb0f38b1 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -37,6 +37,7 @@
#include "executor/functions.h"
#include "executor/tqueue.h"
#include "executor/tstoreReceiver.h"
+#include "executor/svariableReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/portal.h"
@@ -143,6 +144,9 @@ CreateDestReceiver(CommandDest dest)
case DestTupleQueue:
return CreateTupleQueueDestReceiver(NULL);
+
+ case DestVariable:
+ return CreateVariableDestReceiver();
}
/* should never get here */
@@ -178,6 +182,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -222,6 +227,7 @@ NullCommand(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -268,6 +274,7 @@ ReadyForQuery(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b5804f64ad..35199fd0dc 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
@@ -344,7 +345,7 @@ ProcessUtility(PlannedStmt *pstmt,
char *completionTag)
{
Assert(IsA(pstmt, PlannedStmt));
- Assert(pstmt->commandType == CMD_UTILITY);
+ Assert(pstmt->commandType == CMD_UTILITY || pstmt->commandType == CMD_PLAN_UTILITY);
Assert(queryString != NULL); /* required as of 8.4 */
/*
@@ -915,6 +916,14 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
}
+ case T_LetStmt:
+ {
+ doLetStmt(pstmt, params, queryEnv, queryString);
+ if (completionTag)
+ strcpy(completionTag, "LET");
+ }
+ break;
+
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
@@ -1221,6 +1230,10 @@ ProcessUtilitySlow(ParseState *pstate,
}
break;
+ case T_CreateSchemaVarStmt:
+ address = DefineSchemaVariable(pstate, (CreateSchemaVarStmt *) parsetree);
+ break;
+
/*
* ************* object creation / destruction **************
*/
@@ -2055,6 +2068,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_STATISTIC_EXT:
tag = "ALTER STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "ALTER VARIABLE";
+ break;
default:
tag = "???";
break;
@@ -2104,6 +2120,10 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ case T_LetStmt:
+ tag = "LET";
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
@@ -2358,6 +2378,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "DROP VARIABLE";
+ break;
default:
tag = "???";
}
@@ -2639,6 +2662,9 @@ CreateCommandTag(Node *parsetree)
case DISCARD_SEQUENCES:
tag = "DISCARD SEQUENCES";
break;
+ case DISCARD_VARIABLES:
+ tag = "DISCARD VARIABLES";
+ break;
default:
tag = "???";
}
@@ -2844,6 +2870,7 @@ CreateCommandTag(Node *parsetree)
tag = "DELETE";
break;
case CMD_UTILITY:
+ case CMD_PLAN_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
default:
@@ -2915,6 +2942,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_CreateSchemaVarStmt:
+ tag = "CREATE VARIABLE";
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -2961,6 +2992,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ case T_LetStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a45e093de7..952c0d9628 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -315,6 +315,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_READ_CHR:
+ read = ACL_READ;
+ break;
+ case ACL_WRITE_CHR:
+ read = ACL_WRITE;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -808,6 +814,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -903,6 +913,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'T':
objtype = OBJECT_TYPE;
break;
+ case 'V':
+ objtype = OBJECT_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
}
@@ -1627,6 +1640,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CONNECT;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
+ if (pg_strcasecmp(priv_type, "READ") == 0)
+ return ACL_READ;
+ if (pg_strcasecmp(priv_type, "WRITE") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1721,6 +1738,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 03e9a28a63..cc8f3326ac 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -38,6 +38,7 @@
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
#include "common/keywords.h"
@@ -7362,6 +7363,14 @@ get_parameter(Param *param, deparse_context *context)
return;
}
+ /* translate paramid to original schema variable name */
+ if (param->paramkind == PARAM_VARIABLE)
+ {
+ appendStringInfo(context->buf, "%s",
+ schema_variable_get_name(param->paramid));
+ return;
+ }
+
/*
* Not PARAM_EXEC, or couldn't find referent: just print $N.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bba595ad1d..858a6dd4be 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1691,6 +1691,18 @@ get_relname_relid(const char *relname, Oid relnamespace)
ObjectIdGetDatum(relnamespace));
}
+/*
+ * get_varname_varid
+ * Given name and namespace of variable, look up the OID.
+ */
+Oid
+get_varname_varid(const char *varname, Oid varnamespace)
+{
+ return GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(varnamespace));
+}
+
#ifdef NOT_USED
/*
* get_relnatts
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b381782a3..35dc32f649 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -73,6 +73,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "utils/rel.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
@@ -968,6 +969,28 @@ static const struct cachedesc cacheinfo[] = {
0
},
2
+ },
+ {VariableRelationId, /* VARIABLENAMENSP */
+ VariableNameNspIndexId,
+ 2,
+ {
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ 0,
+ 0
+ },
+ 8
+ },
+ {VariableRelationId, /* VARIABLEOID */
+ VariableObjectIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
}
};
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 0d147cb08d..6d97931d85 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -296,6 +296,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading variables\n");
+ getVariables(fout);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 83c976eaf7..c9bc91ca68 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3471,6 +3471,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(type, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(type, "STATISTICS") == 0 ||
+ strcmp(type, "VARIABLE") == 0 ||
/* non-schema-specified objects */
strcmp(type, "DATABASE") == 0 ||
strcmp(type, "PROCEDURAL LANGUAGE") == 0 ||
@@ -3670,7 +3671,8 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
strcmp(te->desc, "SERVER") == 0 ||
strcmp(te->desc, "STATISTICS") == 0 ||
strcmp(te->desc, "PUBLICATION") == 0 ||
- strcmp(te->desc, "SUBSCRIPTION") == 0)
+ strcmp(te->desc, "SUBSCRIPTION") == 0 ||
+ strcmp(te->desc, "VARIABLE") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 9baf7b2fde..f825a00c9d 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -260,6 +260,7 @@ static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
+static void dumpVariable(Archive *fout, VariableInfo *varinfo);
static void dumpDatabase(Archive *AH);
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
const char *dbname, Oid dboid);
@@ -4221,6 +4222,208 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+/*
+ * getVariables
+ * get information about variables
+ */
+void
+getVariables(Archive *fout)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer init_acl_subquery = createPQExpBuffer();
+ PQExpBuffer init_racl_subquery = createPQExpBuffer();
+ PGresult *res;
+ VariableInfo *varinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_varname;
+ int i_varnamespace;
+ int i_vartype;
+ int i_vartypname;
+ int i_vardefexpr;
+ int i_rolname;
+ int i_varacl;
+ int i_rvaracl;
+ int i_initvaracl;
+ int i_initrvaracl;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion <= 110000)
+ return;
+
+ acl_subquery = createPQExpBuffer();
+ racl_subquery = createPQExpBuffer();
+ init_acl_subquery = createPQExpBuffer();
+ init_racl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
+ init_racl_subquery, "v.varacl", "v.varowner", "'V'",
+ dopt->binary_upgrade);
+
+ query = createPQExpBuffer();
+
+ resetPQExpBuffer(query);
+
+ /* Get the variables in current database. */
+ appendPQExpBuffer(query,
+ "SELECT v.tableoid, v.oid, v.varname, "
+ "v.varnamespace,"
+ "(%s varowner) AS rolname, "
+ "%s as varacl, "
+ "%s as rvaracl, "
+ "%s as initvaracl, "
+ "%s as initrvaracl, "
+ "v.vartype, "
+ "pg_catalog.format_type(v.vartype, v.vartypmod) as vartypname, "
+ "pg_catalog.pg_get_expr(v.vardefexpr,0) as vardefexpr "
+ "FROM pg_variable v "
+ "LEFT JOIN pg_init_privs pip "
+ "ON (v.oid = pip.objoid "
+ "AND pip.classoid = 'pg_variable'::regclass "
+ "AND pip.objsubid = 0)",
+ username_subquery,
+ acl_subquery->data,
+ racl_subquery->data,
+ init_acl_subquery->data,
+ init_racl_subquery->data);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(init_acl_subquery);
+ destroyPQExpBuffer(init_racl_subquery);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_varname = PQfnumber(res, "varname");
+ i_varnamespace = PQfnumber(res, "varnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_vartype = PQfnumber(res, "vartype");
+ i_vartypname = PQfnumber(res, "vartypname");
+ i_vardefexpr = PQfnumber(res, "vardefexpr");
+ i_varacl = PQfnumber(res, "varacl");
+ i_rvaracl = PQfnumber(res, "rvaracl");
+ i_initvaracl = PQfnumber(res, "initvaracl");
+ i_initrvaracl = PQfnumber(res, "initrvaracl");
+
+ varinfo = pg_malloc(ntups * sizeof(VariableInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ TypeInfo *vtype;
+
+ varinfo[i].dobj.objType = DO_VARIABLE;
+ varinfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ varinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&varinfo[i].dobj);
+ varinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_varname));
+ varinfo[i].dobj.namespace =
+ findNamespace(fout,
+ atooid(PQgetvalue(res, i, i_varnamespace)));
+
+ varinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ varinfo[i].vartype = atooid(PQgetvalue(res, i, i_vartype));
+ varinfo[i].vartypname = pg_strdup(PQgetvalue(res, i, i_vartypname));
+
+ varinfo[i].varacl = pg_strdup(PQgetvalue(res, i, i_varacl));
+ varinfo[i].rvaracl = pg_strdup(PQgetvalue(res, i, i_rvaracl));
+ varinfo[i].initvaracl = pg_strdup(PQgetvalue(res, i, i_initvaracl));
+ varinfo[i].initrvaracl = pg_strdup(PQgetvalue(res, i, i_initrvaracl));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ /* Do not try to dump ACL if no ACL exists. */
+ if (PQgetisnull(res, i, i_varacl) && PQgetisnull(res, i, i_rvaracl) &&
+ PQgetisnull(res, i, i_initvaracl) &&
+ PQgetisnull(res, i, i_initrvaracl))
+ varinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+
+ if (PQgetisnull(res, i, i_vardefexpr))
+ varinfo[i].vardefexpr = NULL;
+ else
+ varinfo[i].vardefexpr = pg_strdup(PQgetvalue(res, i, i_vardefexpr));
+
+ if (strlen(varinfo[i].rolname) == 0)
+ write_msg(NULL, "WARNING: owner of variable \"%s\" appears to be invalid\n",
+ varinfo[i].dobj.name);
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ vtype = findTypeByOid(varinfo[i].vartype);
+ addObjectDependency(&varinfo[i].dobj, vtype->dobj.dumpId);
+ }
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpVariable
+ * dump the definition of the given variables
+ */
+static void
+dumpVariable(Archive *fout, VariableInfo *varinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ const char *varname;
+ const char *vartypname;
+ const char *vardefexpr;
+
+ /* Skip if not to be dumped */
+ if (!varinfo->dobj.dump || dopt->dataOnly)
+ return;
+
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ varname = fmtQualifiedDumpable(varinfo);
+ vartypname = varinfo->vartypname;
+ vardefexpr = varinfo->vardefexpr;
+
+ appendPQExpBuffer(delq, "DROP VARIABLE %s;\n",
+ varname);
+
+ appendPQExpBuffer(query, "CREATE VARIABLE %s AS %s",
+ varname, vartypname);
+
+ if (vardefexpr)
+ appendPQExpBuffer(query, " DEFAULT %s",
+ vardefexpr);
+
+ appendPQExpBuffer(query, ";\n");
+
+ ArchiveEntry(fout, varinfo->dobj.catId, varinfo->dobj.dumpId,
+ varinfo->dobj.name,
+ NULL,
+ NULL,
+ varinfo->rolname, false,
+ "VARIABLE", SECTION_PRE_DATA,
+ query->data, delq->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ if (varinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, "VARIABLE", varname,
+ NULL, varinfo->rolname,
+ varinfo->dobj.catId, 0, varinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -9849,6 +10052,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_VARIABLE:
+ dumpVariable(fout, (VariableInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -17935,6 +18141,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_OPFAMILY:
case DO_COLLATION:
case DO_CONVERSION:
+ case DO_VARIABLE:
case DO_TABLE:
case DO_ATTRDEF:
case DO_PROCLANG:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 1448005f30..0d49bb7ed7 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -84,7 +84,8 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_VARIABLE
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -625,6 +626,22 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/*
+ * The VariableInfo struct is used to represent schema variables
+ */
+typedef struct _VariableInfo
+{
+ DumpableObject dobj;
+ Oid vartype;
+ char *vartypname;
+ char *rolname; /* name of owner, or empty string */
+ char *vardefexpr;
+ char *varacl;
+ char *rvaracl;
+ char *initvaracl;
+ char *initrvaracl;
+} VariableInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -725,5 +742,6 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern void getVariables(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 6227a8fd26..969a021771 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -1477,6 +1477,10 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POST-DATA BOUNDARY (ID %d)",
obj->dumpId);
return;
+ case DO_VARIABLE:
+ snprintf(buf, bufsize,
+ "VARIABLE %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
}
/* shouldn't get here */
snprintf(buf, bufsize,
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index ec751a7c23..2a67766ed4 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2601,6 +2601,38 @@ my %tests = (
},
},
+ 'CREATE VARIABLE test_variable' => {
+ all_runs => 1,
+ catch_all => 'CREATE ... commands',
+ create_order => 61,
+ create_sql => 'CREATE VARIABLE dump_test.variable AS integer DEFAULT 0;',
+ regexp => qr/^
+ \QCREATE VARIABLE dump_test.variable AS integer DEFAULT 0;\E/xm,
+ like => {
+ binary_upgrade => 1,
+ clean => 1,
+ clean_if_exists => 1,
+ createdb => 1,
+ defaults => 1,
+ exclude_test_table => 1,
+ exclude_test_table_data => 1,
+ no_blobs => 1,
+ no_privs => 1,
+ no_owner => 1,
+ only_dump_test_schema => 1,
+ pg_dumpall_dbprivs => 1,
+ schema_only => 1,
+ section_pre_data => 1,
+ test_schema_plus_blobs => 1,
+ with_oids => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_test_table => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1,
+ role => 1,
+ section_post_data => 1, }, },
+
'CREATE VIEW test_view' => {
create_order => 61,
create_sql => 'CREATE VIEW dump_test.test_view
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..73a752fd7e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -853,6 +853,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
break;
}
break;
+ case 'V': /* Variables */
+ success = listVariables(pattern, show_verbose);
+ break;
case 'x': /* Extensions */
if (show_verbose)
success = listExtensionContents(pattern);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 80d8338b96..d645bba7af 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4178,6 +4178,80 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
return true;
}
+/*
+ * \dV
+ *
+ * listVariables()
+ */
+bool
+listVariables(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false, false, false, false, false};
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname as \"%s\",\n"
+ " v.varname as \"%s\",\n"
+ " pg_catalog.format_type(v.vartype, v.vartypmod) as \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(v.varowner) as \"%s\",\n"
+ " pg_catalog.pg_get_expr(v.vardefexpr, 0) as \"%s\"",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Type"),
+ gettext_noop("Owner"),
+ gettext_noop("Default"));
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_variable v"
+ "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = v.varnamespace");
+
+ appendPQExpBufferStr(&buf, "\nWHERE true\n");
+ if (!pattern)
+ appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
+ " AND n.nspname <> 'information_schema'\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "v.varname", NULL,
+ "pg_catalog.pg_variable_is_visible(v.oid)");
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ /*
+ * Most functions in this file are content to print an empty table when
+ * there are no matching objects. We intentionally deviate from that
+ * here, but only in !quiet mode, for historical reasons.
+ */
+ if (PQntuples(res) == 0 && !pset.quiet)
+ {
+ if (pattern)
+ psql_error("Did not find any schema variable named \"%s\".\n",
+ pattern);
+ else
+ psql_error("Did not find any schema variables.\n");
+ }
+ else
+ {
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of variables");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+ }
+
+ PQclear(res);
+ return true;
+}
/*
* \dFp
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..ecc4e3a531 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
/* \dt, \di, \ds, \dS, etc. */
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
+/* \dV */
+extern bool listVariables(const char *pattern, bool varbose);
+
/* \dD */
extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 316030d358..adcc36cb6e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
@@ -257,6 +257,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[S+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dV [PATTERN] list variables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index bb696f8ee9..a7583810e8 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -805,6 +805,22 @@ static const SchemaQuery Query_for_list_of_statistics = {
NULL
};
+static const SchemaQuery Query_for_list_of_variables = {
+ /* min_server_version */
+ 0,
+ /* catname */
+ "pg_catalog.pg_variable v",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_variable_is_visible(v.oid)",
+ /* namespace */
+ "v.varnamespace",
+ /* result */
+ "pg_catalog.quote_ident(v.varname)",
+ /* qualresult */
+ NULL
+};
/*
* Queries to get lists of names of various kinds of things, possibly
@@ -1249,6 +1265,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
{"USER MAPPING FOR", NULL, NULL, NULL},
+ {"VARIABLE", NULL, NULL, &Query_for_list_of_variables},
{"VIEW", NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
@@ -1604,7 +1621,7 @@ psql_completion(const char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
- "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LET", "LISTEN", "LOAD", "LOCK",
"MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
"RESET", "REVOKE", "ROLLBACK",
@@ -1621,9 +1638,9 @@ psql_completion(const char *text, int start, int end)
"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
- "\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+ "\\dm", "\\dn", "\\do", "\\dO", "\\dp"
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+ "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", "\\dV",
"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
"\\endif", "\\errverbose", "\\ev",
"\\f",
@@ -1988,6 +2005,9 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
else if (Matches4("ALTER", "SYSTEM", "SET", MatchAny))
COMPLETE_WITH_CONST("TO");
+ /* ALTER VARIABLE <name> */
+ else if (Matches3("ALTER", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
/* ALTER VIEW <name> */
else if (Matches3("ALTER", "VIEW", MatchAny))
COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
@@ -2837,6 +2857,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
COMPLETE_WITH_LIST2("GROUP", "ROLE");
+/* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
+ /* Complete CREATE VARIABLE <name> with AS */
+ else if (TailMatches3("CREATE", "VARIABLE", MatchAny))
+ COMPLETE_WITH_CONST("AS");
+ /* Complete CREATE VARIABLE <name> with AS types*/
+ else if (TailMatches4("CREATE", "VARIABLE", MatchAny, "AS"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE VIEW <name> with AS */
else if (TailMatches3("CREATE", "VIEW", MatchAny))
@@ -2890,7 +2918,7 @@ psql_completion(const char *text, int start, int end)
/* DISCARD */
else if (Matches1("DISCARD"))
- COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP");
+ COMPLETE_WITH_LIST5("ALL", "PLANS", "SEQUENCES", "TEMP", "VARIABLES");
/* DO */
else if (Matches1("DO"))
@@ -2992,6 +3020,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP VARIABLE */
+ else if (Matches2("DROP", "VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ else if (Matches3("DROP", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3002,14 +3036,14 @@ psql_completion(const char *text, int start, int end)
* Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "ANALYZE", "VERBOSE");
+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "ANALYZE", "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "ANALYZE"))
- COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "VERBOSE");
+ COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "VERBOSE") ||
Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
- COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
+ COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "LET");
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
@@ -3118,6 +3152,7 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'ALL ROUTINES IN SCHEMA'"
" UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
" UNION SELECT 'ALL TABLES IN SCHEMA'"
+ " UNION SELECT 'ALL VARIABLES IN SCHEMA'"
" UNION SELECT 'DATABASE'"
" UNION SELECT 'DOMAIN'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
@@ -3131,14 +3166,16 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'SEQUENCE'"
" UNION SELECT 'TABLE'"
" UNION SELECT 'TABLESPACE'"
- " UNION SELECT 'TYPE'");
+ " UNION SELECT 'TYPE'"
+ " UNION SELECT 'VARIABLE'");
}
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
- COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA",
+ COMPLETE_WITH_LIST6("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
- "TABLES IN SCHEMA");
+ "TABLES IN SCHEMA",
+ "VARIABLES IN SCHEMA");
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
@@ -3172,6 +3209,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches1("TYPE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+ else if (TailMatches1("VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
COMPLETE_WITH_CONST("TO");
else
@@ -3324,7 +3363,7 @@ psql_completion(const char *text, int start, int end)
/* PREPARE xx AS */
else if (Matches3("PREPARE", MatchAny, "AS"))
- COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
+ COMPLETE_WITH_LIST5("SELECT", "UPDATE", "INSERT", "DELETE FROM", "LET");
/*
* PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -3547,6 +3586,14 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
COMPLETE_WITH_CONST("=");
+/* LET --- can be inside EXPLAIN, PREPARE etc */
+ /* If prev. word is LET suggest a list of variables */
+ else if (TailMatches1("LET"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ /* Complete LET <variable> with "=" */
+ else if (TailMatches2("LET", MatchAny))
+ COMPLETE_WITH_CONST("=");
+
/* USER MAPPING */
else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 46c271a46c..3e38a05e55 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -180,7 +180,8 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_VARIABLE /* pg_variable */
} ObjectClass;
#define LAST_OCLASS OCLASS_TRANSFORM
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 24915824ca..dae80c20a8 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -360,4 +360,10 @@ DECLARE_UNIQUE_INDEX(pg_subscription_subname_index, 6115, on pg_subscription usi
DECLARE_UNIQUE_INDEX(pg_subscription_rel_srrelid_srsubid_index, 6117, on pg_subscription_rel using btree(srrelid oid_ops, srsubid oid_ops));
#define SubscriptionRelSrrelidSrsubidIndexId 6117
+DECLARE_UNIQUE_INDEX(pg_variable_oid_index, 4288, on pg_variable using btree(oid oid_ops));
+#define VariableObjectIndexId 4288
+
+DECLARE_UNIQUE_INDEX(pg_variable_varname_nsp_index, 4289, on pg_variable using btree(varname name_ops, varnamespace oid_ops));
+#define VariableNameNspIndexId 4289
+
#endif /* INDEXING_H */
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 0e202372d5..8812075b2e 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -75,10 +75,13 @@ extern Oid RangeVarGetAndCheckCreationNamespace(RangeVar *newRelation,
extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
+extern bool VariableIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
+extern bool VariableIsVisible(Oid varid);
+
extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
bool expand_variadic,
@@ -146,6 +149,10 @@ extern void SetTempNamespaceState(Oid tempNamespaceId,
Oid tempToastNamespaceId);
extern void ResetTempTableNamespace(void);
+extern List *NamesFromList(List *names);
+extern Oid lookup_variable(const char *nspname, const char *varname, bool missing_ok);
+extern Oid identify_variable(List *names, char **attrname, bool *not_uniq);
+
extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
extern OverrideSearchPath *CopyOverrideSearchPath(OverrideSearchPath *path);
extern bool OverrideSearchPathMatchesCurrent(OverrideSearchPath *path);
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index d0410f5586..56deef1a45 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -57,6 +57,7 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a14651010f..61cbe65805 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5961,6 +5961,9 @@
proname => 'pg_collation_is_visible', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid',
prosrc => 'pg_collation_is_visible' },
+{ oid => '4187', descr => 'is schema variable visible in search path?',
+ proname => 'pg_variable_is_visible', procost => '10', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid', prosrc => 'pg_variable_is_visible' },
{ oid => '2854', descr => 'get OID of current session\'s temp schema, if any',
proname => 'pg_my_temp_schema', provolatile => 's', proparallel => 'r',
diff --git a/src/include/catalog/pg_variable.h b/src/include/catalog/pg_variable.h
new file mode 100644
index 0000000000..2a2b1f3e08
--- /dev/null
+++ b/src/include/catalog/pg_variable.h
@@ -0,0 +1,91 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.h
+ * definition of schema variables system catalog (pg_variables)
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_variable.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VARIABLE_H
+#define PG_VARIABLE_H
+
+#include "catalog/genbki.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable_d.h"
+#include "utils/acl.h"
+
+/* ----------------
+ * pg_variable definition. cpp turns this into
+ * typedef struct FormData_pg_variable
+ * ----------------
+ */
+CATALOG(pg_variable,4287,VariableRelationId)
+{
+ NameData varname; /* variable name */
+ Oid varnamespace; /* OID of namespace containing variable class */
+ Oid vartype; /* OID of entry in pg_type for variable's type */
+ int32 vartypmod; /* typmode for variable's type */
+ Oid varowner; /* class owner */
+ Oid varcollation; /* variable collation */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+
+ /* list of expression trees for variable default (NULL if none) */
+ pg_node_tree vardefexpr BKI_DEFAULT(_null_);
+
+ aclitem varacl[1] BKI_DEFAULT(_null_); /* access permissions */
+
+#endif
+} FormData_pg_variable;
+
+/* ----------------
+ * Form_pg_variable corresponds to a pointer to a tuple with
+ * the format of pg_variable relation.
+ * ----------------
+ */
+typedef FormData_pg_variable *Form_pg_variable;
+
+typedef struct Variable
+{
+ Oid oid;
+ char *name;
+ Oid namespace;
+ Oid typid;
+ int32 typmod;
+ Oid owner;
+ Oid collation;
+ Node *defexpr;
+ Acl *acl;
+} Variable;
+
+/* returns fields from pg_variable table */
+extern char *get_schema_variable_name(Oid varid);
+extern void get_schema_variable_type_typmod_collid(Oid varid,
+ Oid *typid,
+ int32 *typmod,
+ Oid *collid);
+
+/* returns name of variable based on current search path */
+extern char *schema_variable_get_name(Oid varid);
+
+extern Variable *GetVariable(Oid varid, bool missing_ok);
+extern ObjectAddress VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Oid varCollation,
+ Node *varDefexpr,
+ bool if_not_exists);
+
+
+#endif /* PG_VARIABLE_H */
diff --git a/src/include/commands/schemavariable.h b/src/include/commands/schemavariable.h
new file mode 100644
index 0000000000..2823d35b7c
--- /dev/null
+++ b/src/include/commands/schemavariable.h
@@ -0,0 +1,35 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavariable.h
+ * prototypes for schemavariable.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/schemavariable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SCHEMAVARIABLE_H
+#define SCHEMAVARIABLE_H
+
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable.h"
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "utils/queryenvironment.h"
+
+extern void ResetSchemaVariableCache(void);
+
+extern void RemoveVariableById(Oid varid);
+extern ObjectAddress DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt);
+
+extern Datum GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid, bool copy);
+extern void SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod);
+
+extern void doLetStmt(PlannedStmt *pstmt, ParamListInfo params, QueryEnvironment *queryEnv, const char *queryString);
+
+#endif
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77616..4fdceb6cee 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -344,13 +345,22 @@ typedef struct ExprEvalStep
TupleDesc argdesc;
} nulltest_row;
- /* for EEOP_PARAM_EXEC/EXTERN */
+ /* for EEOP_PARAM_EXEC/EXTERN/VARIABLE */
struct
{
- int paramid; /* numeric ID for parameter */
- Oid paramtype; /* OID of parameter's datatype */
+ int paramid; /* numeric ID for parameter */
+ Oid paramtype; /* OID of parameter's datatype */
} param;
+ /* for EEOP_PARAM_VARIABLE */
+ struct
+ {
+ int paramid; /* numeric ID for parameter */
+ Oid varoid; /* OID of assigned variable */
+ Oid paramtype; /* OID of parameter's datatype */
+ } vparam;
+
+
/* for EEOP_PARAM_CALLBACK */
struct
{
@@ -700,6 +710,8 @@ extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
extern void ExecEvalParamExecParams(Bitmapset *params, EState *estate);
extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern void ExecEvalParamVariable(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext);
extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op);
diff --git a/src/include/executor/svariableReceiver.h b/src/include/executor/svariableReceiver.h
new file mode 100644
index 0000000000..8c8117701f
--- /dev/null
+++ b/src/include/executor/svariableReceiver.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.h
+ * prototypes for svariableReceiver.c
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/svariableReceiver.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SVARIABLE_RECEIVER_H
+#define SVARIABLE_RECEIVER_H
+
+#include "tcop/dest.h"
+
+
+extern DestReceiver *CreateVariableDestReceiver(void);
+
+extern void SetVariableDestReceiverParams(DestReceiver *self, Oid varid);
+
+#endif /* SVARIABLE_RECEIVER_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 41fa2052a2..1d6eaf9ce7 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -100,6 +100,8 @@ typedef struct ExprState
int steps_len; /* number of steps currently */
int steps_alloc; /* allocated length of steps array */
+ int nvariables; /* number of used variables */
+
struct PlanState *parent; /* parent PlanState node, if any */
ParamListInfo ext_params; /* for compiling PARAM_EXTERN nodes */
@@ -472,6 +474,7 @@ typedef struct ResultRelInfo
typedef struct EState
{
NodeTag type;
+ bool es_shared; /* plpgsql uses share estate */
/* Basic state for all query types: */
ScanDirection es_direction; /* current scan direction */
@@ -564,6 +567,14 @@ typedef struct EState
/* The per-query shared memory area to use for parallel execution. */
struct dsa_area *es_query_dsa;
+ int es_result_variable; /* Oid of target variable */
+
+ /* query schema variable cache */
+ int es_nvariables;
+ bool *es_varnulls;
+ Oid *es_vartypes;
+ Datum *es_varvalues;
+
/*
* JIT information. es_jit_flags indicates whether JIT should be performed
* and with which options. es_jit is created on-demand when JITing is
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 697d3d7a5f..dd7fd8ed42 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -348,6 +348,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSchemaVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
@@ -419,6 +420,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
+ T_LetStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -663,6 +665,7 @@ typedef enum CmdType
CMD_DELETE,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
+ CMD_PLAN_UTILITY, /* only let stmt now, requires planning */
CMD_NOTHING /* dummy command for instead nothing rules
* with qual */
} CmdType;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07ab1a3dde..72e26b9f35 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -84,7 +84,9 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
-#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_READ (1<<12) /* for variables */
+#define ACL_WRITE (1<<13) /* for variables */
+#define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -121,6 +123,7 @@ typedef struct Query
int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */
+ int resultVariable; /* Oid of target variable or 0 */
bool hasAggs; /* has aggregates in tlist or havingQual */
bool hasWindowFuncs; /* has window functions in tlist */
@@ -1505,6 +1508,18 @@ typedef struct UpdateStmt
WithClause *withClause; /* WITH clause */
} UpdateStmt;
+/* ----------------------
+ * Let Statement
+ * ----------------------
+ */
+typedef struct LetStmt
+{
+ NodeTag type;
+ List *target; /* target variable */
+ Node *selectStmt; /* source expression */
+ int location;
+} LetStmt;
+
/* ----------------------
* Select Statement
*
@@ -1682,6 +1697,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -2497,6 +2513,20 @@ typedef struct AlterSeqStmt
bool missing_ok; /* skip error if a role is missing? */
} AlterSeqStmt;
+/* ----------------------
+ * {Create|Alter} VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSchemaVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the type of variable */
+ CollateClause *collClause;
+ Node *defexpr; /* default expression */
+ bool if_not_exists; /* do nothing if it already exists */
+} CreateSchemaVarStmt;
+
/* ----------------------
* Create {Aggregate|Operator|Type} Statement
* ----------------------
@@ -3238,7 +3268,8 @@ typedef enum DiscardMode
DISCARD_ALL,
DISCARD_PLANS,
DISCARD_SEQUENCES,
- DISCARD_TEMP
+ DISCARD_TEMP,
+ DISCARD_VARIABLES
} DiscardMode;
typedef struct DiscardStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd03a..2588f1455f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -43,7 +43,7 @@ typedef struct PlannedStmt
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete|utility */
+ CmdType commandType; /* select|let|insert|update|delete|utility */
uint64 queryId; /* query identifier (copied from Query) */
@@ -81,6 +81,9 @@ typedef struct PlannedStmt
*/
List *rootResultRelations;
+ /* Oid of target variable for LET command */
+ Oid resultVariable;
+
List *subplans; /* Plan trees for SubPlan expressions; note
* that some could be NULL */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..97f838a54e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -229,13 +229,17 @@ typedef struct Const
* of the `paramid' field contain the SubLink's subLinkId, and
* the low-order 16 bits contain the column number. (This type
* of Param is also converted to PARAM_EXEC during planning.)
+ *
+ * PARAM_VARIABLE: The parameter is a access to schema variable
+ * paramid holds varid.
*/
typedef enum ParamKind
{
PARAM_EXTERN,
PARAM_EXEC,
PARAM_SUBLINK,
- PARAM_MULTIEXPR
+ PARAM_MULTIEXPR,
+ PARAM_VARIABLE
} ParamKind;
typedef struct Param
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db40147b..d3ed3f4d0f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -231,6 +231,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("let", LET, UNRESERVED_KEYWORD)
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -434,6 +435,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 0230543810..f7c2e67f33 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -69,7 +69,9 @@ typedef enum ParseExprKind
EXPR_KIND_TRIGGER_WHEN, /* WHEN condition in CREATE TRIGGER */
EXPR_KIND_POLICY, /* USING or WITH CHECK expr in policy */
EXPR_KIND_PARTITION_EXPRESSION, /* PARTITION BY expression */
- EXPR_KIND_CALL_ARGUMENT /* procedure argument in CALL */
+ EXPR_KIND_CALL_ARGUMENT, /* procedure argument in CALL */
+ EXPR_KIND_VARIABLE_DEFAULT, /* default value for schema variable */
+ EXPR_KIND_LET /* LET assignment (should be same like UPDATE) */
} ParseExprKind;
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index ec6e0c102f..1ee199ed8f 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -32,6 +32,16 @@ extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
int attrno,
List *indirection,
int location);
+extern Node *transformAssignmentIndirection(ParseState *pstate,
+ Node *basenode,
+ const char *targetName,
+ bool targetIsArray,
+ Oid targetTypeId,
+ int32 targetTypMod,
+ Oid targetCollation,
+ ListCell *indirection,
+ Node *rhs,
+ int location);
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
char *colname, int attrno,
List *indirection,
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 82f0f2e741..c49b653555 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -96,7 +96,8 @@ typedef enum
DestCopyOut, /* results sent to COPY TO code */
DestSQLFunction, /* results sent to SQL-language func mgr */
DestTransientRel, /* results sent to transient relation */
- DestTupleQueue /* results sent to tuple queue */
+ DestTupleQueue, /* results sent to tuple queue */
+ DestVariable /* results sents to schema variable */
} CommandDest;
/* ----------------
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f4d4be8d0d..c624d8dd0b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -147,9 +147,11 @@ typedef ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
+#define ACL_READ_CHR 'S' /* 'R' is occupated by old RULE priv */
+#define ACL_WRITE_CHR 'W'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcSW"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -166,6 +168,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_READ|ACL_WRITE)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -253,6 +256,8 @@ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_variable_aclmask(Oid var_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode);
@@ -269,6 +274,7 @@ extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
+extern AclResult pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, ObjectType objtype,
const char *objectname);
@@ -305,6 +311,7 @@ extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool pg_publication_ownercheck(Oid pub_oid, Oid roleid);
extern bool pg_subscription_ownercheck(Oid sub_oid, Oid roleid);
extern bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid);
+extern bool pg_variable_ownercheck(Oid stat_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index e55ea4035b..cb3f4aaca9 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -122,6 +122,7 @@ extern bool get_func_leakproof(Oid funcid);
extern float4 get_func_cost(Oid funcid);
extern float4 get_func_rows(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
+extern Oid get_varname_varid(const char *varname, Oid varnamespace);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4f333586ee..453699be3c 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -107,9 +107,11 @@ enum SysCacheIdentifier
TYPENAMENSP,
TYPEOID,
USERMAPPINGOID,
- USERMAPPINGUSERSERVER
+ USERMAPPINGUSERSERVER,
+ VARIABLENAMENSP,
+ VARIABLEOID
-#define SysCacheSize (USERMAPPINGUSERSERVER + 1)
+#define SysCacheSize (VARIABLEOID + 1)
};
extern void InitCatalogCache(void);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 380d1de8f4..ac71dd7d7a 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8049,6 +8049,7 @@ plpgsql_create_econtext(PLpgSQL_execstate *estate)
{
oldcontext = MemoryContextSwitchTo(TopTransactionContext);
shared_simple_eval_estate = CreateExecutorState();
+ shared_simple_eval_estate->es_shared = true;
MemoryContextSwitchTo(oldcontext);
}
estate->simple_eval_estate = shared_simple_eval_estate;
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 7d3647a12d..7f183d4f1b 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -332,6 +332,7 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
/* Create a private EState for simple-expression execution */
simple_eval_estate = CreateExecutorState();
+ simple_eval_estate->es_shared = true;
/* And run the function */
PG_TRY();
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b500..48286f8e1a 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -105,5 +105,7 @@ ORDER BY 1, 2;
pg_index | indpred | pg_node_tree
pg_largeobject | data | bytea
pg_largeobject_metadata | lomacl | aclitem[]
-(11 rows)
+ pg_variable | varacl | aclitem[]
+ pg_variable | vardefexpr | pg_node_tree
+(13 rows)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 0aa5357917..848b041a4b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -163,6 +163,7 @@ pg_ts_parser|t
pg_ts_template|t
pg_type|t
pg_user_mapping|t
+pg_variable|t
point_tbl|t
polygon_tbl|t
quad_box_tbl|t
diff --git a/src/test/regress/expected/schema_variables.out b/src/test/regress/expected/schema_variables.out
new file mode 100644
index 0000000000..f8c72ccd85
--- /dev/null
+++ b/src/test/regress/expected/schema_variables.out
@@ -0,0 +1,366 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+DROP VARIABLE var1, var2;
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT var1;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT READ ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT var1;
+ var1
+------
+
+(1 row)
+
+SET ROLE TO DEFAULT;
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to work
+LET var1 = 333;
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var1;
+ERROR: permission denied for schema variable var1
+-- should to work;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: ((g.v)::numeric = var1)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var1;
+SELECT * FROM schema_var_view;
+ var1
+------
+ 333
+(1 row)
+
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ var1
+------
+
+(1 row)
+
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+----------------------------
+ Result
+ Output: 3.14159265358979
+(2 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+SELECT inc(1);
+ inc
+-----
+ 1
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 2
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 3
+(1 row)
+
+SELECT inc(1) FROM generate_series(1,10);
+ inc
+-----
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+(10 rows)
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+-- composite variables
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+\d v1
+\d v2
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+SELECT v1;
+ v1
+------------
+ (1,2,3.14)
+(1 row)
+
+SELECT v2;
+ v2
+---------------
+ (10,20,31.40)
+(1 row)
+
+SELECT (v1).*;
+ x | y | z
+---+---+------
+ 1 | 2 | 3.14
+(1 row)
+
+SELECT (v2).*;
+ x | y | z
+----+----+-------
+ 10 | 20 | 31.40
+(1 row)
+
+SELECT v1.x + v1.z;
+ ?column?
+----------
+ 4.14
+(1 row)
+
+SELECT v2.x + v2.z;
+ ?column?
+----------
+ 41.40
+(1 row)
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+SELECT v2.x;
+ERROR: permission denied for schema variable v2
+SET ROLE TO DEFAULT;
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+DROP ROLE var_test_role;
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+ relname
+----------
+ pg_class
+(1 row)
+
+-- should to fail
+SELECT varx.xxx;
+ERROR: type text is not composite
+-- variables can be updated under RO transaction
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+SELECT varx;
+ varx
+-------
+ hello
+(1 row)
+
+DROP VARIABLE varx;
+CREATE TYPE t1 AS (a int, b numeric, c text);
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+ v1
+----------------------------
+ (1,3.14159265358979,hello)
+(1 row)
+
+LET v1.b = 10.2222;
+SELECT v1;
+ v1
+-------------------
+ (1,10.2222,hello)
+(1 row)
+
+-- should to fail
+LET v1.x = 10;
+ERROR: cannot assign to field "x" of column "x" because there is no such column in data type t1
+LINE 1: LET v1.x = 10;
+ ^
+DROP VARIABLE v1;
+DROP TYPE t1;
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+ va1
+------------
+ {10.1,2.1}
+(1 row)
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+ va2
+--------------------
+ (10.2,"{0.0,0.0}")
+(1 row)
+
+LET va2.b[1] = 10.3;
+SELECT va2;
+ va2
+---------------------
+ (10.2,"{10.3,0.0}")
+(1 row)
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+ v1
+------------------
+ 6.28318530717958
+(1 row)
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+ v2
+--------------------------
+ (3.14159265358979,Hello)
+(1 row)
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+ERROR: cannot drop type t2 because other objects depend on it
+DETAIL: schema variable v2 depends on type t2
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+ var1
+------
+ 1000
+(1 row)
+
+CREATE ROLE var_test_role;
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+DROP VARIABLE var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+SET ROLE TO DEFAULT;
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+SET ROLE TO var_test_role;
+SELECT public.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+SELECT public.var1_renamed;
+ var1_renamed
+--------------
+ 1000
+(1 row)
+
+DROP VARIABLE public.var1_renamed;
+SET ROLE TO DEFAULt;
+DROP ROLE var_test_role;
+CREATE VARIABLE xx AS text DEFAULT 'hello';
+SELECT xx, upper(xx);
+ xx | upper
+-------+-------
+ hello | HELLO
+(1 row)
+
+LET xx = 'Hi';
+SELECT xx;
+ xx
+----
+ Hi
+(1 row)
+
+DROP VARIABLE xx;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 16f979c8d9..9bf379b87b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml schema_variables
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 42632be675..42bf4ecb3f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -191,3 +191,4 @@ test: partition_aggregate
test: event_trigger
test: fast_default
test: stats
+test: schema_variables
diff --git a/src/test/regress/sql/schema_variables.sql b/src/test/regress/sql/schema_variables.sql
new file mode 100644
index 0000000000..6bd801e771
--- /dev/null
+++ b/src/test/regress/sql/schema_variables.sql
@@ -0,0 +1,257 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+
+DROP VARIABLE var1, var2;
+
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+
+CREATE ROLE var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT READ ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+-- should to work
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to work
+LET var1 = 333;
+
+SET ROLE TO DEFAULT;
+
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+
+SELECT secure_var();
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT public.var1;
+
+-- should to work;
+SELECT secure_var();
+
+SET ROLE TO DEFAULT;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+
+CREATE VIEW schema_var_view AS SELECT var1;
+
+SELECT * FROM schema_var_view;
+
+\c -
+
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+
+LET var1 = pi();
+
+SELECT var1;
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+
+EXECUTE var_pp(100, 1.23456);
+
+SELECT var1;
+
+CREATE VARIABLE var3 AS int;
+
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT inc(1);
+SELECT inc(1);
+SELECT inc(1);
+
+SELECT inc(1) FROM generate_series(1,10);
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+LET var3 = 0;
+
+SET ROLE TO DEFAULT;
+
+DROP VIEW schema_var_view;
+
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+
+-- composite variables
+
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+
+\d v1
+\d v2
+
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+
+SELECT v1;
+SELECT v2;
+SELECT (v1).*;
+SELECT (v2).*;
+
+SELECT v1.x + v1.z;
+SELECT v2.x + v2.z;
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+
+SELECT v2.x;
+
+SET ROLE TO DEFAULT;
+
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+DROP ROLE var_test_role;
+
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+
+-- should to fail
+SELECT varx.xxx;
+
+-- variables can be updated under RO transaction
+
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+
+SELECT varx;
+
+DROP VARIABLE varx;
+
+CREATE TYPE t1 AS (a int, b numeric, c text);
+
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+LET v1.b = 10.2222;
+SELECT v1;
+
+-- should to fail
+LET v1.x = 10;
+
+DROP VARIABLE v1;
+DROP TYPE t1;
+
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+LET va2.b[1] = 10.3;
+SELECT va2;
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+
+CREATE ROLE var_test_role;
+
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+DROP VARIABLE var_schema2.var1;
+
+SET ROLE TO DEFAULT;
+
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+
+SET ROLE TO var_test_role;
+SELECT public.var1;
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+
+SELECT public.var1_renamed;
+
+DROP VARIABLE public.var1_renamed;
+
+SET ROLE TO DEFAULt;
+
+DROP ROLE var_test_role;
+
+CREATE VARIABLE xx AS text DEFAULT 'hello';
+
+SELECT xx, upper(xx);
+
+LET xx = 'Hi';
+
+SELECT xx;
+
+DROP VARIABLE xx;
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-08-21 17:55 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Fabien COELHO @ 2018-08-21 17:55 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hello Pavel,
AFAICR, I had an objection on such new objects when you first proposed
something similar in October 2016.
Namely, if session variables are not transactional, they cannot be used to
implement security related auditing features which were advertised as the
motivating use case: an the audit check may fail on a commit because of a
differed constraint, but the variable would keep its "okay" value unduly,
which would create a latent security issue, the audit check having failed
but the variable saying the opposite.
So my point was that they should be transactional by default, although I
would be ok with an option for having a voluntary non transactional
version.
Is this issue addressed somehow with this version?
--
Fabien.
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
@ 2018-08-21 18:48 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-08-21 18:48 UTC (permalink / raw)
To: Fabien COELHO <coelho@cri.ensmp.fr>; +Cc: Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi Fabien
Dne út 21. 8. 2018 19:56 uživatel Fabien COELHO <coelho@cri.ensmp.fr>
napsal:
>
> Hello Pavel,
>
> AFAICR, I had an objection on such new objects when you first proposed
> something similar in October 2016.
>
> Namely, if session variables are not transactional, they cannot be used to
> implement security related auditing features which were advertised as the
> motivating use case: an the audit check may fail on a commit because of a
> differed constraint, but the variable would keep its "okay" value unduly,
> which would create a latent security issue, the audit check having failed
> but the variable saying the opposite.
>
> So my point was that they should be transactional by default, although I
> would be ok with an option for having a voluntary non transactional
> version.
>
> Is this issue addressed somehow with this ?
1. I respect your opinion, but I dont agree with it. Oracle, db2 has
similar or very similar feature non transactional, and I didnt find any
requests to change it.
2. the prototype implementation was based on relclass items, and some
transactional behave was possible. Peter E. had objections to this design
and proposed own catalog table. I did it. Now, the transactional behave is
harder to implement, although it is not impossible. This patch is not small
now, so I didnt implement it. I have a strong opinion so default behave
have to be non transactional.
Transactional variables significantly increases complexity of this patch,
now is simple, because we can reset variable on drop variable command.
Maybe I miss some simply implementation, but I spent on it more than few
days. Still, any cooperation are welcome.
Regards
Pavel
> --
> Fabien.
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-08-22 07:00 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Fabien COELHO @ 2018-08-22 07:00 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hello Pavel,
>> AFAICR, I had an objection on such new objects when you first proposed
>> something similar in October 2016.
>>
>> Namely, if session variables are not transactional, they cannot be used to
>> implement security related auditing features which were advertised as the
>> motivating use case: an the audit check may fail on a commit because of a
>> differed constraint, but the variable would keep its "okay" value unduly,
>> which would create a latent security issue, the audit check having failed
>> but the variable saying the opposite.
>>
>> So my point was that they should be transactional by default, although I
>> would be ok with an option for having a voluntary non transactional
>> version.
>>
>> Is this issue addressed somehow with this ?
>
>
> 1. I respect your opinion, but I dont agree with it. Oracle, db2 has
> similar or very similar feature non transactional, and I didnt find any
> requests to change it.
The argument of authority that "X does it like that" is not a valid answer
to my technical objection about security implications of this feature.
> 2. the prototype implementation was based on relclass items, and some
> transactional behave was possible. Peter E. had objections to this design
> and proposed own catalog table. I did it. Now, the transactional behave is
> harder to implement, although it is not impossible. This patch is not small
> now, so I didnt implement it.
"It is harder to implement" does not look like a valid answer either.
> I have a strong opinion so default behave have to be non transactional.
The fact that you have a "strong opinion" does not really answer my
objection. Moreover, I said that I would be ok with a non transactional
option, provided that a default transactional is available.
> Transactional variables significantly increases complexity of this patch,
> now is simple, because we can reset variable on drop variable command.
> Maybe I miss some simply implementation, but I spent on it more than few
> days. Still, any cooperation are welcome.
"It is simpler to implement this way" is not an answer either, especially
as you said that it could have been on point 2.
As I do not see any clear answer to my objection about security
implications, I understand that it is not addressed by this patch.
At the bare minimum, if this feature ever made it as is, I think that a
clear caveat must be included in the documentation about not using it for
any security-related purpose.
Also, I'm not really sure how useful such a non-transactional object can
be for other purposes: the user should take into account that the
transaction may fail and the value of the session variable be inconsistent
as a result. Sometimes it may not matter, but if it matters there is no
easy way around the fact.
--
Fabien.
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
@ 2018-08-23 05:35 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-08-23 05:35 UTC (permalink / raw)
To: Fabien COELHO <coelho@cri.ensmp.fr>; +Cc: Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
2018-08-22 9:00 GMT+02:00 Fabien COELHO <coelho@cri.ensmp.fr>:
>
> Hello Pavel,
>
> AFAICR, I had an objection on such new objects when you first proposed
>>> something similar in October 2016.
>>>
>>> Namely, if session variables are not transactional, they cannot be used
>>> to
>>> implement security related auditing features which were advertised as the
>>> motivating use case: an the audit check may fail on a commit because of a
>>> differed constraint, but the variable would keep its "okay" value unduly,
>>> which would create a latent security issue, the audit check having failed
>>> but the variable saying the opposite.
>>>
>>> So my point was that they should be transactional by default, although I
>>> would be ok with an option for having a voluntary non transactional
>>> version.
>>>
>>> Is this issue addressed somehow with this ?
>>>
>>
>>
>> 1. I respect your opinion, but I dont agree with it. Oracle, db2 has
>> similar or very similar feature non transactional, and I didnt find any
>> requests to change it.
>>
>
> The argument of authority that "X does it like that" is not a valid answer
> to my technical objection about security implications of this feature.
>
> 2. the prototype implementation was based on relclass items, and some
>> transactional behave was possible. Peter E. had objections to this design
>> and proposed own catalog table. I did it. Now, the transactional behave is
>> harder to implement, although it is not impossible. This patch is not
>> small
>> now, so I didnt implement it.
>>
>
> "It is harder to implement" does not look like a valid answer either.
>
> I have a strong opinion so default behave have to be non transactional.
>>
>
> The fact that you have a "strong opinion" does not really answer my
> objection. Moreover, I said that I would be ok with a non transactional
> option, provided that a default transactional is available.
>
> Transactional variables significantly increases complexity of this patch,
>> now is simple, because we can reset variable on drop variable command.
>> Maybe I miss some simply implementation, but I spent on it more than few
>> days. Still, any cooperation are welcome.
>>
>
> "It is simpler to implement this way" is not an answer either, especially
> as you said that it could have been on point 2.
>
>
> As I do not see any clear answer to my objection about security
> implications, I understand that it is not addressed by this patch.
>
>
> At the bare minimum, if this feature ever made it as is, I think that a
> clear caveat must be included in the documentation about not using it for
> any security-related purpose.
>
> Also, I'm not really sure how useful such a non-transactional object can
> be for other purposes: the user should take into account that the
> transaction may fail and the value of the session variable be inconsistent
> as a result. Sometimes it may not matter, but if it matters there is no
> easy way around the fact.
>
I agree, so it should be well documented to be clear, what is possible,
what not, and to be correct expectations.
This feature has two (three) purposes
1. global variables for PL language
2. holding some session based informations, that can be used in security
definer functions.
3. Because it is not transactional, then it allows write operation on read
only hot stand by instances.
It is not transactional safe, but it is secure in sense a possibility to
set a access rights. I understand, so some patterns are not possible, but
when you need hold some keys per session, then this simply solution can be
good enough. The variables are clean after session end.
I think it is possible for some more complex patterns, but then developer
should be smarter, and should to enocode state result to content of
variable. There is strong benefit - read write access to variables is very
cheap and fast.
I invite any patch to doc (or everywhere) with explanation and about
possible risks.
Regards
Pavel
> --
> Fabien.
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-08-23 08:17 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Fabien COELHO @ 2018-08-23 08:17 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hello Pavel,
> 2. holding some session based informations, that can be used in security
> definer functions.
Hmmm, I see our disagreement. My point is that this feature is *NOT* fit
for security-related uses because if the transaction fails the variable
would keep the value it had if the transaction had not failed...
> 3. Because it is not transactional, then it allows write operation on read
> It is not transactional safe, but it is secure in sense a possibility to
> set a access rights.
This is a misleading play on words. It is secure wrt to access right, but
unsecure wrt security purposes which is the only point for having such a
feature in the first place.
> I understand, so some patterns are not possible, but when you need hold
> some keys per session, then this simply solution can be good enough.
Security vs "good enough in some cases" looks bad to me.
> I think it is possible for some more complex patterns,
I'm not sure of any pattern which would be correct wrt security if it
depends on the success of a transaction.
> but then developer should be smarter, and should to enocode state result
> to content of variable.
I do not see how the developer can be smarter if they need a transactional
for security but they do not have it.
> There is strong benefit - read write access to variables is very cheap
> and fast.
I'd say that PostgreSQL is about "ACID & security" first, not "cheap &
fast" first.
> I invite any patch to doc (or everywhere) with explanation and about
> possible risks.
Hmmm... You are the one proposing the feature...
Here is something, thanks for adjusting it to the syntax you are proposing
and inserting it where appropriate. Possibly in the corresponding CREATE
doc?
"""
<caution>
<par>
Beware that session variables are not transactional.
This is a concern in a security context where the variable must be set to
some trusted value depending on the success of the transaction:
if the transaction fails, the variable keeps its trusted value unduly.
</par>
<par>
For instance, the following pattern does NOT work:
<programlisting>
CREATE USER auditer;
SET ROLE auditer;
CREATE SESSION VARIABLE is_audited BOOLEAN DEFAULT FALSE ...;
-- ensure that only "auditer" can write "is_audited":
REVOKE ... ON SESSION VARIABLE is_audited FROM ...;
-- create an audit function
CREATE FUNCTION audit_session(...) SECURITY DEFINER AS $$
-- record the session and checks in some place...
-- then tell it was done:
LET is_audited = TRUE;
$$;
-- the intention is that other security definier functions can check that
-- the session is audited by checking on "is_audited", eg:
CREATE FUNCTION only_for_audited(...) SECURITY DEFINER AS $$
IF NOT is_audited THEN RAISE "security error";
-- do protected stuff here.
$$;
</programlisting>
The above pattern can be attacked with the following approach:
<programlisting>
BEGIN;
SELECT audit_session(...);
-- success, "is_audited" is set...
ROLLBACK;
-- the audit login has been reverted, but "is_audited" retains its value.
-- any subsequent operation believes wrongly that the session is audited,
-- but its logging has really been removed by the ROLLBACK.
-- ok but should not:
SELECT only_for_audited(...);
</programlisting>
</par>
</caution>
"""
For the record, I'm "-1" on this feature as proposed, for what it's worth,
because of the misleading security implications. This feature would just
help people have their security wrong.
--
Fabien.
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
@ 2018-08-23 08:44 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 09:46 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
0 siblings, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-08-23 08:44 UTC (permalink / raw)
To: Fabien COELHO <coelho@cri.ensmp.fr>; +Cc: Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
2018-08-23 10:17 GMT+02:00 Fabien COELHO <coelho@cri.ensmp.fr>:
>
> Hello Pavel,
>
> 2. holding some session based informations, that can be used in security
>> definer functions.
>>
>
> Hmmm, I see our disagreement. My point is that this feature is *NOT* fit
> for security-related uses because if the transaction fails the variable
> would keep the value it had if the transaction had not failed...
>
> 3. Because it is not transactional, then it allows write operation on read
>>
>
> It is not transactional safe, but it is secure in sense a possibility to
>> set a access rights.
>>
>
> This is a misleading play on words. It is secure wrt to access right, but
> unsecure wrt security purposes which is the only point for having such a
> feature in the first place.
>
> I understand, so some patterns are not possible, but when you need hold
>> some keys per session, then this simply solution can be good enough.
>>
>
> Security vs "good enough in some cases" looks bad to me.
>
We don't find a agreement, because you are concentrated on transation, me
on session. And we have different expectations.
> I think it is possible for some more complex patterns,
>>
>
> I'm not sure of any pattern which would be correct wrt security if it
> depends on the success of a transaction.
>
> but then developer should be smarter, and should to enocode state result
>> to content of variable.
>>
>
> I do not see how the developer can be smarter if they need a transactional
> for security but they do not have it.
>
> There is strong benefit - read write access to variables is very cheap and
>> fast.
>>
>
> I'd say that PostgreSQL is about "ACID & security" first, not "cheap &
> fast" first.
>
> I invite any patch to doc (or everywhere) with explanation and about
>> possible risks.
>>
>
> Hmmm... You are the one proposing the feature...
>
> Here is something, thanks for adjusting it to the syntax you are proposing
> and inserting it where appropriate. Possibly in the corresponding CREATE
> doc?
>
> """
> <caution>
> <par>
> Beware that session variables are not transactional.
> This is a concern in a security context where the variable must be set to
> some trusted value depending on the success of the transaction:
> if the transaction fails, the variable keeps its trusted value unduly.
> </par>
>
> <par>
> For instance, the following pattern does NOT work:
>
> <programlisting>
> CREATE USER auditer;
> SET ROLE auditer;
> CREATE SESSION VARIABLE is_audited BOOLEAN DEFAULT FALSE ...;
> -- ensure that only "auditer" can write "is_audited":
> REVOKE ... ON SESSION VARIABLE is_audited FROM ...;
>
> -- create an audit function
> CREATE FUNCTION audit_session(...) SECURITY DEFINER AS $$
> -- record the session and checks in some place...
> -- then tell it was done:
> LET is_audited = TRUE;
> $$;
>
> -- the intention is that other security definier functions can check that
> -- the session is audited by checking on "is_audited", eg:
> CREATE FUNCTION only_for_audited(...) SECURITY DEFINER AS $$
> IF NOT is_audited THEN RAISE "security error";
> -- do protected stuff here.
> $$;
> </programlisting>
>
> The above pattern can be attacked with the following approach:
> <programlisting>
> BEGIN;
> SELECT audit_session(...);
> -- success, "is_audited" is set...
> ROLLBACK;
> -- the audit login has been reverted, but "is_audited" retains its value.
>
> -- any subsequent operation believes wrongly that the session is audited,
> -- but its logging has really been removed by the ROLLBACK.
>
> -- ok but should not:
> SELECT only_for_audited(...);
> </programlisting>
> </par>
> </caution>
> """
>
>
It is good example of not supported pattern. It is not designed for this.
I'll merge this doc.
Note: I am not sure, if I have all relations to described issue, but if I
understand well, then solution can be reset on transaction end, maybe reset
on rollback. This is solvable, I'll look how it is complex.
>
> For the record, I'm "-1" on this feature as proposed, for what it's worth,
> because of the misleading security implications. This feature would just
> help people have their security wrong.
>
I respect your opinion - and I hope so integration of your proposed doc is
good warning for users that would to use not transactional variable like
transactional source.
Regards
Pavel
>
> --
> Fabien.
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-08-23 09:46 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 12:39 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
1 sibling, 1 reply; 433+ messages in thread
From: Fabien COELHO @ 2018-08-23 09:46 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
>> Security vs "good enough in some cases" looks bad to me.
>
> We don't find a agreement, because you are concentrated on transation,
> me on session. And we have different expectations.
I do not understand your point, as usual. I raise a factual issue about
security, and you do not answer how this can be solved with your proposal,
but appeal to argument of authority and declare your "strong opinion".
I do not see any intrinsic opposition between having session objects and
transactions. Nothing prevents a session object to be transactional beyond
your willingness that it should not be.
Now, I do expect all PostgreSQL features to be security-wise, whatever
their scope.
I do not think that security should be traded for "cheap & fast", esp as
the sole use case for a feature is a security pattern that cannot be
implemented securely with it. This appears to me as a huge contradiction,
hence my opposition against this feature as proposed.
The good news is that I'm a nobody: if a committer is happy with your
patch, it will get committed, you do not need my approval.
--
Fabien.
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 09:46 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
@ 2018-08-23 12:39 ` Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-08-29 18:10 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Luzanov @ 2018-08-23 12:39 UTC (permalink / raw)
To: pgsql-hackers@lists.postgresql.org
On 23.08.2018 12:46, Fabien COELHO wrote:
> I do not understand your point, as usual. I raise a factual issue
> about security, and you do not answer how this can be solved with your
> proposal, but appeal to argument of authority and declare your "strong
> opinion".
>
> I do not see any intrinsic opposition between having session objects
> and transactions. Nothing prevents a session object to be
> transactional beyond your willingness that it should not be.
>
> Now, I do expect all PostgreSQL features to be security-wise, whatever
> their scope.
>
> I do not think that security should be traded for "cheap & fast", esp
> as the sole use case for a feature is a security pattern that cannot
> be implemented securely with it. This appears to me as a huge
> contradiction, hence my opposition against this feature as proposed.
I can't to agree with your position.
Consider this example.
I want to record some inappropriate user actions to audit table and
rollback transaction.
But aborting transaction will also abort record to audit table.
So, do not use tables, becouse they have security implications.
This is very similar to your approach.
Schema variables is a very needed and important feature, but for others
purposes.
-----
Pavel Luzanov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 09:46 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 12:39 ` Re: [HACKERS] proposal: schema variables Pavel Luzanov <p.luzanov@postgrespro.ru>
@ 2018-08-29 18:10 ` Fabien COELHO <coelho@cri.ensmp.fr>
0 siblings, 0 replies; 433+ messages in thread
From: Fabien COELHO @ 2018-08-29 18:10 UTC (permalink / raw)
To: Pavel Luzanov <p.luzanov@postgrespro.ru>; +Cc: PostgreSQL Developers <pgsql-hackers@lists.postgresql.org>
Hello Pavel L.
>> I do not understand your point, as usual. I raise a factual issue about
>> security, and you do not answer how this can be solved with your proposal,
>> but appeal to argument of authority and declare your "strong opinion".
>>
>> I do not see any intrinsic opposition between having session objects and
>> transactions. Nothing prevents a session object to be transactional beyond
>> your willingness that it should not be.
>>
>> Now, I do expect all PostgreSQL features to be security-wise, whatever
>> their scope.
>>
>> I do not think that security should be traded for "cheap & fast", esp as
>> the sole use case for a feature is a security pattern that cannot be
>> implemented securely with it. This appears to me as a huge contradiction,
>> hence my opposition against this feature as proposed.
>
> I can't to agree with your position.
>
> Consider this example. I want to record some inappropriate user actions
> to audit table and rollback transaction. But aborting transaction will
> also abort record to audit table. So, do not use tables, becouse they
> have security implications.
Indeed, you cannot record a transaction failure from a transaction.
> This is very similar to your approach.
I understand that your point is that some use case could require a non
transactional session variable. I'm not sure of how the use case would go
on though, because once the "attacker" disconnects, the session variable
disappears, so it does not record that there was a problem.
Anyway, I'm not against having session variables per se. I'm argumenting
that there is a good case to have them transactional by default, and
possibly an option to have them non transactional if this is really needed
by some use case to provide.
The only use case put forward by Pavel S. is the security audit one
where a session variable stores that audit checks have been performed,
which AFAICS cannot be implemented securely with the proposed non
transactional session variables.
--
Fabien.
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-04 07:21 ` Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-04 13:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 2 replies; 433+ messages in thread
From: Dean Rasheed @ 2018-09-04 07:21 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
AFAICS this patch does nothing to consider parallel safety -- that is,
as things stand, a variable is allowed in a query that may be
parallelised, but its value is not copied to workers, leading to
incorrect results. For example:
create table foo(a int);
insert into foo select * from generate_series(1,1000000);
create variable zero int;
let zero = 0;
explain (costs off) select count(*) from foo where a%10 = zero;
QUERY PLAN
-----------------------------------------------
Finalize Aggregate
-> Gather
Workers Planned: 2
-> Partial Aggregate
-> Parallel Seq Scan on foo
Filter: ((a % 10) = zero)
(6 rows)
select count(*) from foo where a%10 = zero;
count
-------
38037 -- Different random result each time, should be 100,000
(1 row)
Thoughts?
Regards,
Dean
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
@ 2018-09-04 13:00 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-06 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-04 13:00 UTC (permalink / raw)
To: Dean Rasheed <dean.a.rasheed@gmail.com>; +Cc: Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
2018-09-04 9:21 GMT+02:00 Dean Rasheed <dean.a.rasheed@gmail.com>:
> AFAICS this patch does nothing to consider parallel safety -- that is,
> as things stand, a variable is allowed in a query that may be
> parallelised, but its value is not copied to workers, leading to
> incorrect results. For example:
>
> create table foo(a int);
> insert into foo select * from generate_series(1,1000000);
> create variable zero int;
> let zero = 0;
>
> explain (costs off) select count(*) from foo where a%10 = zero;
>
> QUERY PLAN
> -----------------------------------------------
> Finalize Aggregate
> -> Gather
> Workers Planned: 2
> -> Partial Aggregate
> -> Parallel Seq Scan on foo
> Filter: ((a % 10) = zero)
> (6 rows)
>
> select count(*) from foo where a%10 = zero;
>
> count
> -------
> 38037 -- Different random result each time, should be 100,000
> (1 row)
>
> Thoughts?
>
The query use copy of values of variables now - but unfortunately, these
values are not passed to workers. Should be fixed.
Thank you for test case.
Pavel
> Regards,
> Dean
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-04 13:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-06 08:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-07 12:34 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-06 08:30 UTC (permalink / raw)
To: Dean Rasheed <dean.a.rasheed@gmail.com>; +Cc: Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
here is updated patch - I wrote some transactional support
I am not sure how these new features are understandable and if these
features does it better or not.
There are possibility to reset to default value when
a) any transaction is finished - the scope of value is limited by
transaction
CREATE VARIABLE foo int ON TRANSACTION END RESET;
b) when transaction finished by rollback
CREATE VARIABLE foo int ON ROLLBACK RESET
Now, when I am thinking about it, the @b is simple, but not too practical -
when some fails, then we lost a value (any transaction inside session can
fails). The @a has sense - the behave is global value (what is not possible
in Postgres now), but this value is destroyed by any unhandled exceptions,
and it cleaned on transaction end. The @b is just for information and for
discussion, but I'll remove it - because it is obscure.
The open question is syntax. PostgreSQL has already ON COMMIT xxx . It is
little bit unclean, because it has semantic "on transaction end", but if I
didn't implement @b, then ON COMMIT syntax can be used.
Regards
Pavel
Attachments:
[text/x-patch] schema-variables-180906-01.patch (208.8K, ../../CAFj8pRB+PDcKX0WJqovhxMJb=O=k4qV+EekFDKFpyVSZLzFzfA@mail.gmail.com/3-schema-variables-180906-01.patch)
download | inline diff:
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 0179deea2e..8bb478f5fd 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -359,6 +359,11 @@
<entry><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link></entry>
<entry>mappings of users to foreign servers</entry>
</row>
+
+ <row>
+ <entry><link linkend="catalog-pg-variable"><structname>pg_variable</structname></link></entry>
+ <entry>schema variables</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -11303,4 +11308,123 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
+ <sect1 id="catalog-pg-variable">
+ <title><structname>pg_variable</structname></title>
+
+ <indexterm zone="catalog-pg-variable">
+ <primary>pg_variable</primary>
+ </indexterm>
+
+ <para>
+ The table <structname>pg_variable</structname> holds metadata
+ of schema variables.
+ </para>
+
+ <table>
+ <title><structname>pg_views</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><structfield>oid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry></entry>
+ <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Name of the schema variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the namespace that contains this variable
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartype</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the data type of this variable.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartypmod</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ <structfield>vartypmod</structfield> records type-specific data
+ supplied at table creation time (for example, the maximum
+ length of a <type>varchar</type> column). It is passed to
+ type-specific input functions and length coercion functions.
+ The value will generally be -1 for types that do not need <structfield>vartypmod</structfield>.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>varowner</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>Owner of the variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varcollation</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+ <entry>
+ The defined collation of the variable, or zero if the variable is
+ not of a collatable data type.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>varoncommitreset</structfield></entry>
+ <entry><type>bool</type></entry>
+ <entry></entry>
+ <entry>
+ When it is true, then content of variable is thrown on transaction end.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vardefexpr</structfield></entry>
+ <entry><type>pg_node_tree</type></entry>
+ <entry></entry>
+ <entry>The internal representation of the variable default value</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>
+ Access privileges; see
+ <xref linkend="sql-grant"/> and
+ <xref linkend="sql-revoke"/>
+ for details
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..0631c9ed56 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -47,6 +47,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterType SYSTEM "alter_type.sgml">
<!ENTITY alterUser SYSTEM "alter_user.sgml">
<!ENTITY alterUserMapping SYSTEM "alter_user_mapping.sgml">
+<!ENTITY alterVariable SYSTEM "alter_variable.sgml">
<!ENTITY alterView SYSTEM "alter_view.sgml">
<!ENTITY analyze SYSTEM "analyze.sgml">
<!ENTITY begin SYSTEM "begin.sgml">
@@ -99,6 +100,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createType SYSTEM "create_type.sgml">
<!ENTITY createUser SYSTEM "create_user.sgml">
<!ENTITY createUserMapping SYSTEM "create_user_mapping.sgml">
+<!ENTITY createVariable SYSTEM "create_variable.sgml">
<!ENTITY createView SYSTEM "create_view.sgml">
<!ENTITY deallocate SYSTEM "deallocate.sgml">
<!ENTITY declare SYSTEM "declare.sgml">
@@ -148,6 +150,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropUser SYSTEM "drop_user.sgml">
<!ENTITY dropUserMapping SYSTEM "drop_user_mapping.sgml">
<!ENTITY dropView SYSTEM "drop_view.sgml">
+<!ENTITY dropVariable SYSTEM "drop_variable.sgml">
<!ENTITY end SYSTEM "end.sgml">
<!ENTITY execute SYSTEM "execute.sgml">
<!ENTITY explain SYSTEM "explain.sgml">
@@ -155,6 +158,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY grant SYSTEM "grant.sgml">
<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
<!ENTITY insert SYSTEM "insert.sgml">
+<!ENTITY let SYSTEM "let.sgml">
<!ENTITY listen SYSTEM "listen.sgml">
<!ENTITY load SYSTEM "load.sgml">
<!ENTITY lock SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/alter_variable.sgml b/doc/src/sgml/ref/alter_variable.sgml
new file mode 100644
index 0000000000..6376ac716b
--- /dev/null
+++ b/doc/src/sgml/ref/alter_variable.sgml
@@ -0,0 +1,170 @@
+<!--
+doc/src/sgml/ref/alter_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-altervariable">
+ <indexterm zone="sql-altervariable">
+ <primary>ALTER VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>ALTER VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ALTER VARIABLE</refname>
+ <refpurpose>
+ change the definition of a variable
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="parameter">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>ALTER VARIABLE</command> changes the definition of an existing variable.
+ There are several subforms:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>OWNER</literal></term>
+ <listitem>
+ <para>
+ This form changes the owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RENAME</literal></term>
+ <listitem>
+ <para>
+ This form changes the name of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET SCHEMA</literal></term>
+ <listitem>
+ <para>
+ This form moves the variable into another schema.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </para>
+
+ <para>
+ You must own the variable to use <command>ALTER VARIABLE</command>.
+ To change the schema of a variable, you must also have
+ <literal>CREATE</literal> privilege on the new schema.
+ To alter the owner, you must also be a direct or indirect member of the new
+ owning role, and that role must have <literal>CREATE</literal> privilege on
+ the variable's schema. (These restrictions enforce that altering the owner
+ doesn't do anything you couldn't do by dropping and recreating the variable.
+ However, a superuser can alter ownership of any type anyway.)
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (possibly schema-qualified) of an existing variable to
+ alter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_name</replaceable></term>
+ <listitem>
+ <para>
+ The new name for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_owner</replaceable></term>
+ <listitem>
+ <para>
+ The user name of the new owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_schema</replaceable></term>
+ <listitem>
+ <para>
+ The new schema for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To rename a variable:
+<programlisting>
+ALTER VARIABLE foo RENAME TO boo;
+</programlisting>
+ </para>
+
+ <para>
+ To change the owner of the variable <literal>boo</literal>
+ to <literal>joe</literal>:
+<programlisting>
+ALTER VARIABLE boo OWNER TO joe;
+</programlisting>
+ </para>
+
+ <para>
+ To change the schema of the variable <literal>boo</literal>
+ to <literal>private</literal>:
+<programlisting>
+ALTER VARIABLE boo SET SCHEMA private;
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ This comman is a PostgreSQL extension.
+ </para>
+ </refsect1>
+
+ <refsect1 id="sql-altervariable-see-also">
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
new file mode 100644
index 0000000000..1bf127eccd
--- /dev/null
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -0,0 +1,145 @@
+<!--
+doc/src/sgml/ref/create_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-createvariable">
+ <indexterm zone="sql-createvariable">
+ <primary>CREATE VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE VARIABLE</refname>
+ <refpurpose>define a new permissioned typed schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ] [ COLLATE <replaceable class="parameter">collation</replaceable> ]
+</synopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> creates a new schema variable.
+ These variables are scalar typed, non-transactional, and, like relations,
+ exist within a schema with access controlled via
+ <command>GRANT</command> and <command>REVOKE</command>.
+ </para>
+
+ <para>
+ The value of a schema variable is session-local. Retrieving
+ a variable's value will return NULL unless its value has been set
+ to something else in the current session.
+ </para>
+
+ <para>
+ Retrieval is done via the <function>get_schema_variable</function>dunxrion or the SQL
+ command <command>SELECT</command>. Setting of values is done via the
+ <function>set_schema_variable</function> function or the SQL command
+ <command>LET</command>.
+ Notably, while schema variables are in many ways a kind of table you cannot use
+ <command>UPDATE</command> on them.
+ </para>
+
+ <para>
+ For purposes of name uniqueness relation-like objects (e.g., tables, indexes)
+ within the same schema are considered. i.e., you cannot give a table and a
+ schema variable the same name. This is a consequence of them being treated
+ like relations for purposes of <command>SELECT</command>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF NOT EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the name already exists. A notice is issued in this case.
+ Note that type of the variable is not considered, nor could it be since the namespace
+ searched contains non-variable objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the data type of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>COLLATE <replaceable>collation</replaceable></literal></term>
+ <listitem>
+ <para>
+ The <literal>COLLATE</literal> clause assigns a collation to
+ the variable (which must be of a collatable data type).
+ If not specified, the variable data type's default collation is used.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Use <command>DROP VARIABLE</command> to remove a variable.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Create an integer variable <literal>var1</literal>:
+<programlisting>
+CREATE VARIABLE var1 AS integer;
+SELECT var1;
+</programlisting>
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> is a PostgreSQL feature.
+ <!-- The choice of wording here seems to be left to personal preference... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index 6b909b7232..d83ad811fd 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
+DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES }
</synopsis>
</refsynopsisdiv>
@@ -75,6 +75,17 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>VARIABLES</literal></term>
+ <listitem>
+ <para>
+ Resets the value of all schema variables. When variables
+ will be used later, then will be initialized again to
+ NULL or default value.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
new file mode 100644
index 0000000000..c1c1a2bd67
--- /dev/null
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -0,0 +1,93 @@
+<!--
+doc/src/sgml/ref/drop_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropvariable">
+ <indexterm zone="sql-dropvariable">
+ <primary>DROP VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP VARIABLE</refname>
+ <refpurpose>remove a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP VARIABLE</command> removes a schema variable.
+ A variable can only be dropped by its owner or a superuser.
+ <!-- this would suggest that we need an alter variable owner to command -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the variable does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To remove the schema variable <literal>var1</literal>:
+
+<programlisting>
+DROP VARIABLE var1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ <!-- create variable is a "PostgreSQL feature",
+ this is a "proprietary PostgreSQL command" ... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-createvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index ff64c7a3ba..a83920a7a1 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -79,6 +79,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE <replaceable>type_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
[ GROUP ] <replaceable class="parameter">role_name</replaceable>
@@ -167,6 +171,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
foreign servers,
large objects,
schemas,
+ schema variable
or tablespaces.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
@@ -385,6 +390,24 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>READ</literal></term>
+ <listitem>
+ <para>
+ Allows to read a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WRITE</literal></term>
+ <listitem>
+ <para>
+ Allows to set a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL PRIVILEGES</literal></term>
<listitem>
@@ -550,6 +573,8 @@ rolename=xxxx -- privileges granted to a role
C -- CREATE
c -- CONNECT
T -- TEMPORARY
+ S -- READ
+ w -- WRITE
arwdDxt -- ALL PRIVILEGES (for tables, varies for other objects)
* -- grant option for preceding privilege
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
new file mode 100644
index 0000000000..e8bf3f6dd4
--- /dev/null
+++ b/doc/src/sgml/ref/let.sgml
@@ -0,0 +1,90 @@
+<!--
+doc/src/sgml/ref/let.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-let">
+ <indexterm zone="sql-let">
+ <primary>LET</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>LET</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>LET</refname>
+ <refpurpose>change a schema variable's value</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable class="parameter">sql_expression</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ The <command>LET</command> command updates the specified schema variable' value.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>schema_variable</literal></term>
+ <listitem>
+ <para>
+ The name of schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sql expression</literal></term>
+ <listitem>
+ <para>
+ An SQL expression, the result is cast to the schema variable's type.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Example:
+<programlisting>
+CREATE VARIABLE myvar AS integer;
+LET myvar = 10;
+LET myvar = (SELECT sum(val) FROM tab);
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <!-- this feels like it needs to be more specific,
+ but I don't know enough to make it so -->
+ <literal>LET</literal> extends syntax defined in the SQL
+ standard. The standard knows <literal>SET</literal> command,
+ that is used for different purpouse in PostgreSQL.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 5317f8ccba..8435e05957 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -108,6 +108,12 @@ REVOKE [ GRANT OPTION FOR ]
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_name</replaceable> [, ...]
[ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { READ | WRITE } [, ...] | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..5fb82df51e 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -75,6 +75,7 @@
&alterType;
&alterUser;
&alterUserMapping;
+ &alterVariable;
&alterView;
&analyze;
&begin;
@@ -127,6 +128,7 @@
&createType;
&createUser;
&createUserMapping;
+ &createVariable;
&createView;
&deallocate;
&declare;
@@ -175,6 +177,7 @@
&dropType;
&dropUser;
&dropUserMapping;
+ &dropVariable;
&dropView;
&end;
&execute;
@@ -183,6 +186,7 @@
&grant;
&importForeignSchema;
&insert;
+ &let;
&listen;
&load;
&lock;
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index cd8270d5fb..f3aa9d6c8f 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -33,6 +33,7 @@
#include "catalog/namespace.h"
#include "catalog/storage.h"
#include "commands/async.h"
+#include "commands/schemavariable.h"
#include "commands/tablecmds.h"
#include "commands/trigger.h"
#include "executor/spi.h"
@@ -1996,6 +1997,7 @@ CommitTransaction(void)
* cursors, to avoid dangling-reference problems)
*/
PreCommit_on_commit_actions();
+ SchemaVariablePreCommit_on_commit_actions();
/* close large objects before lower-level cleanup */
AtEOXact_LargeObject(true);
@@ -2121,6 +2123,7 @@ CommitTransaction(void)
AtEOXact_GUC(true, 1);
AtEOXact_SPI(true);
AtEOXact_on_commit_actions(true);
+ AtEOXact_SchemaVariables_on_commit_actions(true);
AtEOXact_Namespace(true, is_parallel_worker);
AtEOXact_SMgr();
AtEOXact_Files(true);
@@ -2601,6 +2604,7 @@ AbortTransaction(void)
AtEOXact_GUC(false, 1);
AtEOXact_SPI(false);
AtEOXact_on_commit_actions(false);
+ AtEOXact_SchemaVariables_on_commit_actions(false);
AtEOXact_Namespace(false, is_parallel_worker);
AtEOXact_SMgr();
AtEOXact_Files(false);
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 0865240f11..1f7c4d1223 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -19,7 +19,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_publication.o pg_range.o \
pg_db_role_setting.o pg_shdepend.o pg_subscription.o pg_type.o \
- storage.o toasting.o
+ pg_variable.o storage.o toasting.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
@@ -46,7 +46,7 @@ CATALOG_HEADERS := \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
- pg_subscription_rel.h
+ pg_subscription_rel.h pg_variable.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 578e4c6592..86917e15a8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -57,6 +57,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_transform.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
@@ -112,6 +113,7 @@ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
+static void ExecGrant_Variable(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
@@ -284,6 +286,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -507,6 +512,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -609,6 +618,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_VARIABLE:
+ ExecGrant_Variable(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -768,6 +780,16 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_VARIABLE:
+ foreach(cell, objnames)
+ {
+ RangeVar *varvar = (RangeVar *) lfirst(cell);
+ Oid relOid;
+
+ relOid = lookup_variable(varvar->schemaname, varvar->relname, false);
+ objects = lappend_oid(objects, relOid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -855,6 +877,31 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
heap_close(rel, AccessShareLock);
}
break;
+ case OBJECT_VARIABLE:
+ {
+ ScanKeyData key;
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&key,
+ Anum_pg_variable_varnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(namespaceId));
+
+ rel = heap_open(VariableRelationId, AccessShareLock);
+ scan = heap_beginscan_catalog(rel, 1, &key);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ }
+ break;
+
default:
/* should not happen */
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
@@ -1018,6 +1065,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1215,6 +1266,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1441,6 +1498,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -3266,6 +3326,129 @@ ExecGrant_Type(InternalGrant *istmt)
heap_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Variable(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_VARIABLE;
+
+ relation = heap_open(VariableRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid varId = lfirst_oid(cell);
+ Form_pg_variable pg_variable_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_variable];
+ bool nulls[Natts_pg_variable];
+ bool replaces[Natts_pg_variable];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for schema variables %u", varId);
+
+ pg_variable_tuple = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_variable_tuple->varowner;
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple, Anum_pg_variable_varacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_VARIABLE, ownerId);
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ oldmembers = NULL;
+ }
+ else
+ {
+ old_acl = DatumGetAclPCopy(aclDatum);
+ /* Get the roles mentioned in the existing ACL */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+ }
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ varId, grantorId, OBJECT_VARIABLE,
+ NameStr(pg_variable_tuple->varname),
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ */
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_variable_varacl - 1] = true;
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(varId, VariableRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(VariableRelationId, varId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3298,6 +3481,10 @@ string_to_privilege(const char *privname)
return ACL_CONNECT;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
+ if (strcmp(privname, "read") == 0)
+ return ACL_READ;
+ if (strcmp(privname, "write") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized privilege type \"%s\"", privname)));
@@ -3333,6 +3520,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3456,6 +3647,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3566,6 +3760,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3710,6 +3907,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
+ case OBJECT_VARIABLE:
+ return pg_variable_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objtype: %d",
(int) objtype);
@@ -4499,6 +4698,67 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a variable.
+ */
+AclMode
+pg_variable_aclmask(Oid var_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_variable varForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the type's tuple from pg_type
+ */
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable with OID %u does not exist",
+ var_oid)));
+ varForm = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Now get the type's owner and ACL from the tuple
+ */
+ ownerId = varForm->varowner;
+
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple,
+ Anum_pg_variable_varacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_VARIABLE, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+
/*
* Exported routine for checking a user's access privileges to a column
*
@@ -4744,6 +5004,18 @@ pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a variable
+ */
+AclResult
+pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
+{
+ if (pg_variable_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Ownership check for a relation (specified by OID).
*/
@@ -5361,6 +5633,33 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a schema variables (specified by OID).
+ */
+bool
+pg_variable_ownercheck(Oid db_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(db_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("variable with OID %u does not exist", db_oid)));
+
+ ownerId = ((Form_pg_variable) GETSTRUCT(tuple))->varowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+
/*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
@@ -5486,6 +5785,10 @@ get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
+ case OBJECT_VARIABLE:
+ defaclobjtype = DEFACLOBJ_VARIABLE;
+ break;
+
default:
return NULL;
}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 4f1d365357..782ddb1655 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -59,6 +59,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -67,6 +68,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
@@ -1280,6 +1282,10 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_VARIABLE:
+ RemoveVariableById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2537,6 +2543,9 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case VariableRelationId:
+ return OCLASS_VARIABLE;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 5d13e6a3d7..453ec1c5a1 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -39,6 +39,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
@@ -755,6 +756,71 @@ RelationIsVisible(Oid relid)
return visible;
}
+/*
+ * VariableIsVisible
+ * Determine whether a variable (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified variable name".
+ */
+bool
+VariableIsVisible(Oid varid)
+{
+ HeapTuple vartup;
+ Form_pg_variable varform;
+ Oid varnamespace;
+ bool visible;
+
+ vartup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(vartup))
+ elog(ERROR, "cache lookup failed for schema variable %u", varid);
+ varform = (Form_pg_variable) GETSTRUCT(vartup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ varnamespace = varform->varnamespace;
+ if (varnamespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, varnamespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another relation of the same name earlier in the path. So
+ * we must do a slow check for conflicting relations.
+ */
+ char *varname = NameStr(varform->varname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == varnamespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (OidIsValid(get_varname_varid(varname, namespaceId)))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(vartup);
+
+ return visible;
+}
+
+
/*
* TypenameGetTypid
@@ -2776,6 +2842,202 @@ TSConfigIsVisible(Oid cfgid)
return visible;
}
+/*
+ * When we know a variable name, then we can find variable simply
+ */
+Oid
+lookup_variable(const char *nspname, const char *varname, bool missing_ok)
+{
+ Oid namespaceId;
+ Oid varoid = InvalidOid;
+ ListCell *l;
+
+ if (nspname)
+ {
+ namespaceId = LookupExplicitNamespace(nspname, missing_ok);
+ if (!OidIsValid(namespaceId))
+ return InvalidOid;
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+
+ if (OidIsValid(varoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(varoid) && !missing_ok)
+ {
+ if (nspname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\".\"%s\" does not exist",
+ nspname, varname)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\" does not exist",
+ varname)));
+ }
+
+ return varoid;
+}
+
+List *
+NamesFromList(List *names)
+{
+ ListCell *l;
+ List *result = NIL;
+
+ foreach(l, names)
+ {
+ Node *n = lfirst(l);
+
+ if (IsA(n, String))
+ {
+ result = lappend(result, n);
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * identify_variable
+ *
+ * Returns oid of not ambigonuous variable specified by qualified path
+ * or InvalidOid. When the path is ambigonuous, then not_uniq flag is
+ * is true.
+ */
+Oid
+identify_variable(List *names, char **attrname, bool *not_uniq)
+{
+ char *a = NULL;
+ char *b = NULL;
+ char *c = NULL;
+ char *d = NULL;
+ Oid varoid_without_attr;
+ Oid varoid_with_attr;
+
+ *not_uniq = false;
+
+ switch (list_length(names))
+ {
+ case 1:
+ a = strVal(linitial(names));
+ return lookup_variable(NULL, a, true);
+
+ case 2:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+
+ /*
+ * a.b can mean "schema"."variable" or "variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(a, b, true);
+ varoid_with_attr = lookup_variable(NULL, a, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = b;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 3:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+
+ /*
+ * a.b.c can mean "catalog"."schema"."variable" or "schema"."variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(b, c, true);
+ varoid_with_attr = lookup_variable(a, b, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = c;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 4:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+ d = strVal(lfourth(names));
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ *attrname = d;
+ return lookup_variable(b, c, true);
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(names))));
+ break;
+ }
+}
/*
* DeconstructQualifiedName
@@ -4490,3 +4752,14 @@ pg_is_other_temp_schema(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(isOtherTempNamespace(oid));
}
+
+Datum
+pg_variable_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ if (!SearchSysCacheExists1(VARIABLEOID, ObjectIdGetDatum(oid)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_BOOL(VariableIsVisible(oid));
+}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7db942dcba..cc3d415e61 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -58,6 +58,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -489,6 +490,18 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
OBJECT_STATISTIC_EXT,
true
+ },
+ {
+ VariableRelationId,
+ VariableObjectIndexId,
+ VARIABLEOID,
+ VARIABLENAMENSP,
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ Anum_pg_variable_varowner,
+ Anum_pg_variable_varacl,
+ OBJECT_VARIABLE,
+ true
}
};
@@ -714,6 +727,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_VARIABLE */
+ {
+ "schema variable", OBJECT_VARIABLE
}
};
@@ -739,6 +756,7 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
bool missing_ok);
static ObjectAddress get_object_address_type(ObjectType objtype,
TypeName *typename, bool missing_ok);
+static ObjectAddress get_object_address_variable(List *object, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object,
bool missing_ok);
static ObjectAddress get_object_address_opf_member(ObjectType objtype,
@@ -996,6 +1014,10 @@ get_object_address(ObjectType objtype, Node *object,
missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_VARIABLE:
+ address = get_object_address_variable(castNode(List, object), missing_ok);
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
@@ -1848,16 +1870,20 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type \"%c\"", objtype),
- errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
+ errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
DEFACLOBJ_RELATION,
DEFACLOBJ_SEQUENCE,
DEFACLOBJ_FUNCTION,
DEFACLOBJ_TYPE,
- DEFACLOBJ_NAMESPACE)));
+ DEFACLOBJ_NAMESPACE,
+ DEFACLOBJ_VARIABLE)));
}
/*
@@ -1942,6 +1968,24 @@ textarray_to_strvaluelist(ArrayType *arr)
return list;
}
+/*
+ * Find the ObjectAddress for a type or domain
+ */
+static ObjectAddress
+get_object_address_variable(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ char *nspname = NULL;
+ char *varname = NULL;
+
+ ObjectAddressSet(address, VariableRelationId, InvalidOid);
+
+ DeconstructQualifiedName(object, &nspname, &varname);
+ address.objectId = lookup_variable(nspname, varname, missing_ok);
+
+ return address;
+}
+
/*
* SQL-callable version of get_object_address
*/
@@ -2131,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_TABCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
+ case OBJECT_VARIABLE:
objnode = (Node *) name;
break;
case OBJECT_ACCESS_METHOD:
@@ -2415,6 +2460,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
break;
+ case OBJECT_VARIABLE:
+ if (!pg_variable_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
default:
elog(ERROR, "unrecognized object type: %d",
(int) objtype);
@@ -3157,6 +3207,32 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_VARIABLE:
+ {
+ char *nspname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ if (VariableIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ appendStringInfo(&buffer, _("schema variable %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(varform->varname)));
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_TSPARSER:
{
HeapTuple tup;
@@ -3422,6 +3498,16 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new schemas belonging to role %s"),
rolename);
break;
+ case DEFACLOBJ_VARIABLE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s"),
+ rolename);
+ break;
default:
/* shouldn't get here */
if (nspname)
@@ -4070,6 +4156,10 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_VARIABLE:
+ appendStringInfoString(&buffer, "schema variable");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4962,6 +5052,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on variables");
+ break;
}
if (objname)
@@ -5121,6 +5215,33 @@ getObjectIdentityParts(const ObjectAddress *object,
}
break;
+ case OCLASS_VARIABLE:
+ {
+ char *schema;
+ char *varname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ schema = get_namespace_name_or_temp(varform->varnamespace);
+ varname = NameStr(varform->varname);
+
+ appendStringInfo(&buffer, "%s",
+ quote_qualified_identifier(schema, varname));
+
+ if (objname)
+ *objname = list_make2(schema, varname);
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
diff --git a/src/backend/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c
new file mode 100644
index 0000000000..79543bbd4d
--- /dev/null
+++ b/src/backend/catalog/pg_variable.c
@@ -0,0 +1,358 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.c
+ * schema variables
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_variable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
+#include "nodes/makefuncs.h"
+#include "nodes/primnodes.h"
+#include "storage/lmgr.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+static VariableEOXActionCodes
+to_eoxaction_code(VariableEOXAction action)
+{
+ switch (action)
+ {
+ case VARIABLE_EOX_NOOP:
+ return VARIABLE_EOX_CODE_NOOP;
+
+ case VARIABLE_EOX_DROP:
+ return VARIABLE_EOX_CODE_DROP;
+
+ case VARIABLE_EOX_RESET:
+ return VARIABLE_EOX_CODE_RESET;
+
+ case VARIABLE_EOX_ROLLBACK_RESET:
+ return VARIABLE_EOX_CODE_ROLLBACK_RESET;
+
+ default:
+ elog(ERROR, "unexpected action");
+ }
+
+}
+
+static VariableEOXAction
+to_eoxaction(VariableEOXActionCodes code)
+{
+ switch (code)
+ {
+ case VARIABLE_EOX_CODE_NOOP:
+ return VARIABLE_EOX_NOOP;
+
+ case VARIABLE_EOX_CODE_DROP:
+ return VARIABLE_EOX_DROP;
+
+ case VARIABLE_EOX_CODE_RESET:
+ return VARIABLE_EOX_RESET;
+
+ case VARIABLE_EOX_CODE_ROLLBACK_RESET:
+ return VARIABLE_EOX_ROLLBACK_RESET;
+
+ default:
+ elog(ERROR, "unexpected code");
+ }
+}
+
+/*
+ * Returns name of schema variable. When variable is not on path,
+ * then the name is qualified.
+ */
+char *
+schema_variable_get_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+ char *nspname;
+ char *result;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ if (VariableIsVisible(varid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ result = quote_qualified_identifier(nspname, varname);
+
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/*
+ * Returns varname field of pg_variable
+ */
+char *
+get_schema_variable_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ ReleaseSysCache(tup);
+
+ return varname;
+}
+
+/*
+ * Returns type, typmod of schema variable
+ */
+void
+get_schema_variable_type_typmod_collid(Oid varid, Oid *typid, int32 *typmod, Oid *collid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ *typid = varform->vartype;
+ *typmod = varform->vartypmod;
+ *collid = varform->varcollation;
+
+ ReleaseSysCache(tup);
+
+ return;
+}
+
+/*
+ * Fetch all fields of schema variable from the syscache.
+ */
+Variable *
+GetVariable(Oid varid, bool missing_ok)
+{
+ HeapTuple tup;
+ Variable *var;
+ Form_pg_variable varform;
+ Datum aclDatum;
+ Datum defexprDatum;
+ bool isnull;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (missing_ok)
+ return NULL;
+
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+ }
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ var = (Variable *) palloc(sizeof(Variable));
+ var->oid = varid;
+ var->name = pstrdup(NameStr(varform->varname));
+ var->namespace = varform->varnamespace;
+ var->typid = varform->vartype;
+ var->typmod = varform->vartypmod;
+ var->owner = varform->varowner;
+ var->collation = varform->varcollation;
+ var->eoxaction = to_eoxaction(varform->vareoxaction);
+
+ /* Get defexpr */
+ defexprDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_vardefexpr,
+ &isnull);
+
+ if (!isnull)
+ var->defexpr = stringToNode(TextDatumGetCString(defexprDatum));
+ else
+ var->defexpr = NULL;
+
+ /* Get varacl */
+ aclDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_varacl,
+ &isnull);
+ if (!isnull)
+ var->acl = DatumGetAclPCopy(aclDatum);
+ else
+ var->acl = NULL;
+
+ ReleaseSysCache(tup);
+
+ return var;
+}
+
+ObjectAddress
+VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Oid varCollation,
+ Node *varDefexpr,
+ VariableEOXAction eoxaction,
+ bool if_not_exists)
+{
+ Acl *varacl;
+ NameData varname;
+ bool nulls[Natts_pg_variable];
+ Datum values[Natts_pg_variable];
+ Relation rel;
+ HeapTuple tup,
+ oldtup;
+ TupleDesc tupdesc;
+ ObjectAddress myself,
+ referenced;
+ Oid retval;
+ int i;
+
+ for (i = 0; i < Natts_pg_variable; i++)
+ {
+ nulls[i] = false;
+ values[i] = (Datum) 0;
+ }
+
+ namestrcpy(&varname, varName);
+ values[Anum_pg_variable_varname - 1] = NameGetDatum(&varname);
+ values[Anum_pg_variable_varnamespace - 1] = ObjectIdGetDatum(varNamespace);
+ values[Anum_pg_variable_vartype - 1] = ObjectIdGetDatum(varType);
+ values[Anum_pg_variable_vartypmod - 1] = Int32GetDatum(varTypmod);
+ values[Anum_pg_variable_varowner - 1] = ObjectIdGetDatum(varOwner);
+ values[Anum_pg_variable_varcollation - 1] = ObjectIdGetDatum(varCollation);
+ values[Anum_pg_variable_vareoxaction - 1] = CharGetDatum((char) to_eoxaction_code(eoxaction));
+ /* proacl will be determined later */
+
+ if (varDefexpr)
+ values[Anum_pg_variable_vardefexpr - 1] = CStringGetTextDatum(nodeToString(varDefexpr));
+ else
+ nulls[Anum_pg_variable_vardefexpr - 1] = true;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+ tupdesc = RelationGetDescr(rel);
+
+ oldtup = SearchSysCache2(VARIABLENAMENSP,
+ PointerGetDatum(varName),
+ ObjectIdGetDatum(varNamespace));
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ if (if_not_exists)
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists, skipping",
+ varName)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists",
+ varName)));
+
+ heap_freetuple(oldtup);
+ heap_close(rel, RowExclusiveLock);
+
+ return InvalidObjectAddress;
+ }
+
+ varacl = get_user_default_acl(OBJECT_VARIABLE, varOwner,
+ varNamespace);
+
+ if (varacl != NULL)
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(varacl);
+ else
+ nulls[Anum_pg_variable_varacl - 1] = true;
+
+ tup = heap_form_tuple(tupdesc, values, nulls);
+ CatalogTupleInsert(rel, tup);
+
+ retval = HeapTupleGetOid(tup);
+
+ myself.classId = VariableRelationId;
+ myself.objectId = retval;
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = varNamespace;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on used type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = varType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on any roles mentioned in ACL */
+ if (varacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(varacl, &newmembers);
+ updateAclDependencies(VariableRelationId, retval, 0,
+ varOwner,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ /* register on commit action if it is necessary */
+ register_variable_on_commit_action(myself.objectId, eoxaction);
+
+ heap_freetuple(tup);
+
+ /* Post creation hook for new function */
+ InvokeObjectPostCreateHook(VariableRelationId, retval, 0);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..2cb5b1172d 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,7 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
- schemacmds.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
+ schemacmds.o seclabel.o sequence.o schemavariable.o statscmds.o subscriptioncmds.o \
tablecmds.o tablespace.o trigger.o tsearchcmds.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index eff325cc7d..a9d5e5e0ad 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -387,6 +387,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_VARIABLE:
{
ObjectAddress address;
Relation catalog;
@@ -504,6 +505,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
@@ -594,6 +596,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_TSDICT:
case OCLASS_TSTEMPLATE:
case OCLASS_TSCONFIG:
+ case OCLASS_VARIABLE:
{
Relation catalog;
@@ -852,6 +855,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_TABLESPACE:
case OBJECT_TSDICTIONARY:
case OBJECT_TSCONFIGURATION:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 01a999c2ac..fec2495e93 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -19,6 +19,7 @@
#include "commands/discard.h"
#include "commands/prepare.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "utils/guc.h"
#include "utils/portal.h"
@@ -48,6 +49,10 @@ DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
ResetTempTableNamespace();
break;
+ case DISCARD_VARIABLES:
+ ResetSchemaVariableCache();
+ break;
+
default:
elog(ERROR, "unrecognized DISCARD target: %d", stmt->target);
}
@@ -75,4 +80,5 @@ DiscardAll(bool isTopLevel)
ResetPlanCache();
ResetTempTableNamespace();
ResetSequenceCaches();
+ ResetSchemaVariableCache();
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e..426df246b3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -126,6 +126,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"TEXT SEARCH TEMPLATE", true},
{"TYPE", true},
{"USER MAPPING", true},
+ {"VARIABLE", true},
{"VIEW", true},
{NULL, false}
};
@@ -297,7 +298,8 @@ check_ddl_tag(const char *tag)
pg_strcasecmp(tag, "REVOKE") == 0 ||
pg_strcasecmp(tag, "DROP OWNED") == 0 ||
pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
+ pg_strcasecmp(tag, "SECURITY LABEL") == 0 ||
+ pg_strcasecmp(tag, "CREATE VARIABLE") == 0)
return EVENT_TRIGGER_COMMAND_TAG_OK;
/*
@@ -1146,6 +1148,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -1209,6 +1212,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
return true;
/*
@@ -2244,6 +2248,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2326,6 +2332,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b1556a..eb8c08baf3 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_PLAN_UTILITY:
/* OK */
break;
default:
diff --git a/src/backend/commands/schemavariable.c b/src/backend/commands/schemavariable.c
new file mode 100644
index 0000000000..3ef576825d
--- /dev/null
+++ b/src/backend/commands/schemavariable.c
@@ -0,0 +1,691 @@
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
+#include "executor/executor.h"
+#include "executor/svariableReceiver.h"
+#include "nodes/execnodes.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/inval.h"
+#include "utils/memutils.h"
+#include "utils/lsyscache.h"
+#include "utils/snapmgr.h"
+#include "utils/syscache.h"
+
+/*
+ * ON COMMIT action list
+ */
+typedef struct OnCommitItem
+{
+ Oid varid; /* relid of relation */
+ VariableEOXAction eoxaction; /* what to do at end of xact */
+ bool deleted; /* true, when varid should be deleted */
+ bool conditional_reset; /* when is true, then variable is reseted on rollback */
+} OnCommitItem;
+
+static List *on_commits = NIL;
+
+/*
+ * The content of variables is not transactional. Due this fact the
+ * implementation of DROP can be simple, because although DROP VARIABLE
+ * can be reverted, the content of variable can be lost. In this example,
+ * DROP VARIABLE is same like reset variable.
+ */
+
+typedef struct SchemaVariableData
+{
+ Oid varid; /* pg_variable OID of this sequence (hash key) */
+ Oid typid; /* OID of the data type */
+ int32 typmod;
+ int16 typlen;
+ bool typbyval;
+ bool isnull;
+ bool freeval;
+ Datum value;
+ bool is_rowtype; /* true when variable is composite */
+ bool is_valid; /* true when variable was successfuly initialized */
+} SchemaVariableData;
+
+typedef SchemaVariableData *SchemaVariable;
+
+static HTAB *schemavarhashtab = NULL; /* hash table for session variables */
+static MemoryContext SchemaVariableMemoryContext = NULL;
+
+static bool first_time = true;
+static void create_schemavar_hashtable(void);
+static bool clean_cache_req = false;
+
+static void clean_cache(void);
+static void force_clean_cache(XactEvent event, void *arg);
+static void remove_variable_on_commit_actions(Oid varid);
+
+
+/*
+ * Save info about ncessity to clean hash table, because some
+ * schema variable was dropped. Don't do here more, recheck
+ * needs to be in transaction state.
+ */
+static void
+InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ if (cacheid != VARIABLEOID)
+ return;
+
+ clean_cache_req = true;
+}
+
+static void
+force_clean_cache(XactEvent event, void *arg)
+{
+ /*
+ * should continue only in transaction time, when
+ * syscache is available.
+ */
+ if (clean_cache_req && IsTransactionState())
+ {
+ clean_cache();
+ clean_cache_req = false;
+ }
+}
+
+static void
+clean_cache(void)
+{
+ HASH_SEQ_STATUS status;
+ SchemaVariable var;
+
+ if (!schemavarhashtab)
+ return;
+
+ hash_seq_init(&status, schemavarhashtab);
+
+ /*
+ * Every valid variable have to have entry in system
+ * catalog. Removed if there is nothing.
+ */
+ while ((var = (SchemaVariable) hash_seq_search(&status)) != NULL)
+ {
+ HeapTuple tp = InvalidOid;
+
+ tp = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var->varid));
+ if (!HeapTupleIsValid(tp))
+ {
+ elog(DEBUG1, "variable %d is removed from cache", var->varid);
+
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (hash_search(schemavarhashtab,
+ (void *) &var->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(DEBUG1, "hash table corrupted");
+ }
+ else
+ ReleaseSysCache(tp);
+ }
+}
+
+/*
+ * Clean variable defined by varid
+ */
+static void
+clean_cache_varid(Oid varid)
+{
+ SchemaVariable svar;
+ bool found;
+
+ if (!schemavarhashtab)
+ return;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_FIND, &found);
+ if (found)
+ {
+ /* clean content, if it is necessary */
+ if (svar->freeval)
+ pfree(DatumGetPointer(svar->value));
+
+ if (hash_search(schemavarhashtab,
+ (void *) &svar->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(DEBUG1, "hash table corrupted");
+
+ remove_variable_on_commit_actions(varid);
+ }
+}
+
+/*
+ * Create the hash table for storing schema variables
+ */
+static void
+create_schemavar_hashtable(void)
+{
+ HASHCTL ctl;
+
+ /* set callbacks */
+ if (first_time)
+ {
+ CacheRegisterSyscacheCallback(VARIABLEOID,
+ InvalidateSchemaVarCacheCallback,
+ (Datum) 0);
+
+ RegisterXactCallback(force_clean_cache, NULL);
+
+ first_time = false;
+ }
+
+ /* needs own long life memory context */
+ if (SchemaVariableMemoryContext == NULL)
+ {
+ SchemaVariableMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "schema variables",
+ ALLOCSET_START_SMALL_SIZES);
+ }
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(SchemaVariableData);
+ ctl.hcxt = SchemaVariableMemoryContext;
+
+ schemavarhashtab = hash_create("Schema variables", 64, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/*
+ * Fast drop complete content of schema variables
+ */
+void
+ResetSchemaVariableCache(void)
+{
+ if (schemavarhashtab)
+ {
+ hash_destroy(schemavarhashtab);
+ schemavarhashtab = NULL;
+ }
+
+ if (SchemaVariableMemoryContext != NULL)
+ {
+ MemoryContextReset(SchemaVariableMemoryContext);
+ }
+}
+
+/*
+ * Drop variable by OID
+ */
+void
+RemoveVariableById(Oid varid)
+{
+ Relation rel;
+ HeapTuple tup;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ CatalogTupleDelete(rel, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(rel, RowExclusiveLock);
+
+ /* remove variable from on_commits list */
+ remove_variable_on_commit_actions(varid);
+}
+
+/*
+ * Creates new variable - entry in pg_catalog.pg_variable table
+ */
+ObjectAddress
+DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt)
+{
+ Oid namespaceid;
+ AclResult aclresult;
+ Oid typid;
+ int32 typmod;
+ Oid varowner = GetUserId();
+ Oid collation;
+ Oid typcollation;
+ ObjectAddress variable;
+
+ Node *cooked_default = NULL;
+
+ /*
+ * Check consistency of arguments
+ */
+ if (stmt->eoxaction == VARIABLE_EOX_DROP
+ && stmt->variable->relpersistence != RELPERSISTENCE_TEMP)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("ON COMMIT DROP can only be used on temporary variables")));
+
+ namespaceid =
+ RangeVarGetAndCheckCreationNamespace(stmt->variable, NoLock, NULL);
+
+ typenameTypeIdAndMod(pstate, stmt->typeName, &typid, &typmod);
+ typcollation = get_typcollation(typid);
+
+ aclresult = pg_type_aclcheck(typid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error_type(aclresult, typid);
+
+ if (stmt->collClause)
+ collation = LookupCollation(pstate,
+ stmt->collClause->collname,
+ stmt->collClause->location);
+ else
+ collation = typcollation;;
+
+ /* Complain if COLLATE is applied to an uncollatable type */
+ if (OidIsValid(collation) && !OidIsValid(typcollation))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("collations are not supported by type %s",
+ format_type_be(typid)),
+ parser_errposition(pstate, stmt->collClause->location)));
+
+ if (stmt->defexpr)
+ {
+ cooked_default = transformExpr(pstate, stmt->defexpr,
+ EXPR_KIND_VARIABLE_DEFAULT);
+
+ cooked_default = coerce_to_specific_type(pstate,
+ cooked_default, typid, "DEFAULT");
+ assign_expr_collations(pstate, cooked_default);
+ }
+
+ return VariableCreate(stmt->variable->relname,
+ namespaceid,
+ typid,
+ typmod,
+ varowner,
+ collation,
+ cooked_default,
+ stmt->eoxaction,
+ stmt->if_not_exists);
+
+ return variable;
+}
+
+/*
+ * Try to search value in hash table. If doesn't
+ * exists insert it (and calculate defexpr if exists.
+ */
+static SchemaVariable
+PrepareSchemaVariableForReading(Oid varid)
+{
+ SchemaVariable svar;
+ Variable *var;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+ if (!found)
+ {
+ var = GetVariable(varid, false);
+ get_typlenbyval(var->typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = var->typid;
+ svar->typmod = var->typmod;
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+ svar->is_rowtype = type_is_rowtype(var->typid);
+
+ /* when we don't need calculate defexpr, value is valid already */
+ svar->is_valid = var->defexpr ? false : true;
+
+ if (var->eoxaction != VARIABLE_EOX_NOOP)
+ register_variable_on_commit_action(varid, var->eoxaction);
+ }
+ else if (!svar->is_valid)
+ {
+ /* we need var to recalculate defexpr */
+ var = GetVariable(varid, false);
+ }
+ else
+ /* we don't need to go to sys cache */
+ var = NULL;
+
+ /*
+ * Initialize variable when it is necessary. It is fresh
+ * or last initialization was not successfull.
+ */
+ if (var != NULL && var->defexpr && !svar->is_valid)
+ {
+ MemoryContext oldcontext = NULL;
+
+ Datum value = (Datum) 0;
+ bool null;
+ EState *estate = NULL;
+ Expr *defexpr;
+ ExprState *defexprs;
+
+ /* Prepare default expr */
+ estate = CreateExecutorState();
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ defexpr = expression_planner((Expr *) var->defexpr);
+ defexprs = ExecInitExpr(defexpr, NULL);
+ value = ExecEvalExprSwitchContext(defexprs, GetPerTupleExprContext(estate), &null);
+
+ MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!null)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ FreeExecutorState(estate);
+ }
+
+ if (!svar->is_valid)
+ elog(ERROR, "the content of variable is not valid");
+
+ return svar;
+}
+
+/*
+ * Returns content of variable. We expext secured access now.
+ * Secure check should be done before.
+ */
+Datum
+GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid, bool copy)
+{
+ SchemaVariable svar;
+ Datum value;
+ bool isnull;
+
+ svar = PrepareSchemaVariableForReading(varid);
+ Assert(svar != NULL);
+
+ if (expected_typid != svar->typid)
+ elog(ERROR, "type of variable \"%s\" is different than expected",
+ schema_variable_get_name(varid));
+
+ value = svar->value;
+ isnull = svar->isnull;
+
+ *isNull = isnull;
+
+ if (!isnull && copy)
+ return datumCopy(value, svar->typbyval, svar->typlen);
+
+ return value;
+}
+
+/*
+ * Write value to variable. We expect secured access in this moment.
+ * In this time, we recheck syschache about used type.
+ */
+void
+SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod)
+{
+ MemoryContext oldcontext = NULL;
+
+ SchemaVariable svar;
+ Oid var_typid;
+ int32 var_typmod;
+ Oid var_collid;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+
+ get_schema_variable_type_typmod_collid(varid,
+ &var_typid,
+ &var_typmod,
+ &var_collid);
+
+ /* check types first */
+ if (var_typid != typid)
+ elog(ERROR, "type of expression is different than schema variable type");
+
+ if (found)
+ {
+ /* release current content first */
+ if (svar->freeval)
+ {
+ pfree(DatumGetPointer(svar->value));
+ svar->value = (Datum) 0;
+ svar->isnull = true;
+ svar->freeval = false;
+ }
+ }
+ else
+ {
+ Variable *var = GetVariable(varid, false);
+
+ register_variable_on_commit_action(varid, var->eoxaction);
+ }
+
+ get_typlenbyval(typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = typid;
+ svar->typmod = typmod;
+
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+
+ svar->is_rowtype = type_is_rowtype(typid);
+ svar->is_valid = false;
+
+ oldcontext = MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!isNull)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+}
+
+void
+doLetStmt(PlannedStmt *pstmt,
+ ParamListInfo params,
+ QueryEnvironment *queryEnv,
+ const char *queryString)
+{
+ QueryDesc *queryDesc;
+ DestReceiver *dest;
+
+ PushCopiedSnapshot(GetActiveSnapshot());
+ UpdateActiveSnapshotCommandId();
+
+ /* Create dest receiver for LET */
+ dest = CreateDestReceiver(DestVariable);
+
+ SetVariableDestReceiverParams(dest, pstmt->resultVariable);
+
+ /* Create a QueryDesc requesting no output */
+ queryDesc = CreateQueryDesc(pstmt, queryString,
+ GetActiveSnapshot(),
+ InvalidSnapshot,
+ dest, params, queryEnv, 0);
+
+ ExecutorStart(queryDesc, 0);
+ ExecutorRun(queryDesc, ForwardScanDirection, 2L, true);
+ ExecutorFinish(queryDesc);
+ ExecutorEnd(queryDesc);
+
+ FreeQueryDesc(queryDesc);
+
+ PopActiveSnapshot();
+}
+
+/*
+ * Register a newly-created relation's ON COMMIT action.
+ */
+void
+register_variable_on_commit_action(Oid varid, VariableEOXAction action)
+{
+ OnCommitItem *oc;
+ MemoryContext oldcxt;
+
+ /*
+ * We needn't bother registering the relation unless there is an ON COMMIT
+ * action we need to take.
+ */
+ if (action == VARIABLE_EOX_NOOP)
+ return;
+
+ oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+ oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
+ oc->varid = varid;
+ oc->eoxaction = action;
+ oc->deleted = false;
+
+ on_commits = lcons(oc, on_commits);
+
+ MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * Remove variable from on_commits action
+ */
+static void
+remove_variable_on_commit_actions(Oid varid)
+{
+ ListCell *l;
+
+ foreach(l, on_commits)
+ {
+ OnCommitItem *oc = (OnCommitItem *) lfirst(l);
+
+ if (oc->varid == varid)
+ {
+ oc->deleted = true;
+ }
+ }
+}
+
+/*
+ * Perform VARIABLE ON COMMIT action
+ */
+void
+SchemaVariablePreCommit_on_commit_actions(void)
+{
+ ListCell *l;
+
+ foreach(l, on_commits)
+ {
+ OnCommitItem *oc = (OnCommitItem *) lfirst(l);
+
+ switch (oc->eoxaction)
+ {
+ case VARIABLE_EOX_NOOP:
+ case VARIABLE_EOX_ROLLBACK_RESET:
+ /* Do nothing */
+ break;
+ case VARIABLE_EOX_RESET:
+ clean_cache_varid(oc->varid);
+ break;
+ case VARIABLE_EOX_DROP:
+ {
+ ObjectAddress object;
+
+ object.classId = VariableRelationId;
+ object.objectId = oc->varid;
+ object.objectSubId = 0;
+
+ /*
+ * Since this is an automatic drop, rather than one
+ * directly initiated by the user, we pass the
+ * PERFORM_DELETION_INTERNAL flag.
+ */
+ performDeletion(&object,
+ DROP_CASCADE, PERFORM_DELETION_INTERNAL);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Post-commit or post-abort cleanup for ON COMMIT management.
+ */
+void
+AtEOXact_SchemaVariables_on_commit_actions(bool isCommit)
+{
+ ListCell *cur_item;
+ ListCell *prev_item;
+
+ prev_item = NULL;
+ cur_item = list_head(on_commits);
+
+ while (cur_item != NULL)
+ {
+ OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
+
+ if (oc->eoxaction == VARIABLE_EOX_ROLLBACK_RESET && !isCommit)
+ clean_cache_varid(oc->varid);
+
+ if (oc->deleted)
+ {
+ /* cur_item must be removed */
+ on_commits = list_delete_cell(on_commits, cur_item, prev_item);
+ pfree(oc);
+ if (prev_item)
+ cur_item = lnext(prev_item);
+ else
+ cur_item = list_head(on_commits);
+ }
+ else
+ {
+ prev_item = cur_item;
+ cur_item = lnext(prev_item);
+ }
+ }
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f9e83c2456..d9dd23e5d6 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9655,6 +9655,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
/*
* We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index cc09895fa5..ee8ff7da9e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -29,6 +29,6 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
nodeCtescan.o nodeNamedtuplestorescan.o nodeWorktablescan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o \
- nodeTableFuncscan.o
+ nodeTableFuncscan.o svariableReceiver.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d7..bb9bf53e1c 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -33,6 +33,7 @@
#include "access/nbtree.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -727,6 +728,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
Param *param = (Param *) node;
ParamListInfo params;
+ AclResult aclresult;
switch (param->paramkind)
{
@@ -736,6 +738,28 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
break;
+
+ case PARAM_VARIABLE:
+
+ /* Check permission to read schema variable */
+ aclresult = pg_variable_aclcheck(param->paramid, GetUserId(), ACL_READ);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE,
+ schema_variable_get_name(param->paramid));
+
+ /*
+ * Using varoid as paramid is not practical. Better to recount
+ * used schema variables from zero, and later to use paramid like
+ * offset.
+ */
+ scratch.opcode = EEOP_PARAM_VARIABLE;
+ scratch.d.vparam.paramid = state->nvariables++;
+ scratch.d.vparam.varoid = param->paramid;
+ scratch.d.vparam.paramtype = param->paramtype;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+
case PARAM_EXTERN:
/*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25aae5..4462dcc952 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -351,6 +352,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1007,6 +1009,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_VARIABLE)
+ {
+ /* iut of line implementation; too large */
+ ExecEvalParamVariable(state, op, econtext);
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_CASE_TESTVAL)
{
/*
@@ -2323,6 +2332,79 @@ ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
errmsg("no value found for parameter %d", paramId)));
}
+/*
+ * Evaluate a PARAM_VARIABLE parameter
+ */
+void
+ExecEvalParamVariable(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ EState *estate = econtext->ecxt_estate;
+
+ /*
+ * We should to ensure stable behave of schema variables in queries. It is
+ * important, because optimizer uses these values as stable, like extern
+ * parameters, what is nice, because queries are optimized well. So, don't
+ * try to access variables directly, use this query variable cache.
+ * This cache cannot be used when EState is shared - PLpgSQL did it for
+ * simple expressions.
+ */
+ if (estate && !estate->es_shared)
+ {
+ int paramid = op->d.vparam.paramid;
+
+ if (estate->es_nvariables == 0)
+ {
+ MemoryContext old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* initialize estate schema variable cache */
+
+ estate->es_nvariables = state->nvariables;
+ estate->es_varnulls = palloc(sizeof(bool) * state->nvariables);
+ estate->es_vartypes = palloc0(sizeof(Oid) * state->nvariables);
+ estate->es_varvalues = palloc(sizeof(Datum) * state->nvariables);
+
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ Assert(estate->es_nvariables == state->nvariables);
+ Assert(estate->es_nvariables > paramid);
+
+ if (!OidIsValid(estate->es_vartypes[paramid]))
+ {
+ MemoryContext old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* copy variable to estate schema variable cache */
+ estate->es_varvalues[paramid] =
+ GetSchemaVariable(op->d.vparam.varoid,
+ &estate->es_varnulls[paramid],
+ op->d.vparam.paramtype,
+ true);
+ estate->es_vartypes[paramid] = op->d.vparam.paramtype;
+
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ Assert(OidIsValid(estate->es_vartypes[paramid]));
+
+ *op->resvalue = estate->es_varvalues[paramid];
+ *op->resnull = estate->es_varnulls[paramid];
+ }
+ else
+ {
+ Datum d;
+ bool isnull;
+
+ /* read content of variable directly */
+ d = GetSchemaVariable(op->d.vparam.varoid,
+ &isnull,
+ op->d.vparam.paramtype,
+ false);
+
+ *op->resvalue = d;
+ *op->resnull = isnull;
+ }
+}
+
/*
* Evaluate a SQLValueFunction expression.
*/
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index c583e020a0..797c1f43b3 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,9 +43,11 @@
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_publication.h"
+#include "catalog/pg_variable.h"
#include "commands/matview.h"
#include "commands/trigger.h"
#include "executor/execdebug.h"
+#include "executor/svariableReceiver.h"
#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -204,12 +206,18 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
*/
estate->es_queryEnv = queryDesc->queryEnv;
+ /*
+ * Result can be stored in schema variable.
+ */
+ estate->es_result_variable = queryDesc->plannedstmt->resultVariable;
+
/*
* If non-read-only query, set the command ID to mark output tuples with
*/
switch (queryDesc->operation)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
@@ -345,6 +353,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate->es_lastoid = InvalidOid;
sendTuples = (operation == CMD_SELECT ||
+ OidIsValid(estate->es_result_variable) ||
queryDesc->plannedstmt->hasReturning);
if (sendTuples)
@@ -924,6 +933,17 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_num_root_result_relations = 0;
}
+ if (OidIsValid(estate->es_result_variable))
+ {
+ AclResult aclresult;
+ Oid varid = estate->es_result_variable;
+
+ /* Ensure this variable is writeable */
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, schema_variable_get_name(varid));
+ }
+
/*
* Similarly, we have to lock relations selected FOR [KEY] UPDATE/SHARE
* before we initialize the plan tree, else we'd be risking lock upgrades.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5b3eaec80b..eca7805517 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -102,6 +102,7 @@ CreateExecutorState(void)
/*
* Initialize all fields of the Executor State structure
*/
+ estate->es_shared = false;
estate->es_direction = ForwardScanDirection;
estate->es_snapshot = InvalidSnapshot; /* caller must initialize this */
estate->es_crosscheck_snapshot = InvalidSnapshot; /* no crosscheck */
diff --git a/src/backend/executor/svariableReceiver.c b/src/backend/executor/svariableReceiver.c
new file mode 100644
index 0000000000..0eac4b5d0c
--- /dev/null
+++ b/src/backend/executor/svariableReceiver.c
@@ -0,0 +1,145 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.c
+ * An implementation of DestReceiver that stores the result value in
+ * a schema variable.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/svariableReceiver.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/tuptoaster.h"
+#include "executor/svariableReceiver.h"
+#include "commands/schemavariable.h"
+
+typedef struct
+{
+ DestReceiver pub;
+ Oid varid;
+ Oid typid;
+ int32 typmod;
+ int typlen;
+ int slot_offset;
+ int rows;
+} svariableState;
+
+
+/*
+ * Prepare to receive tuples from executor.
+ */
+static void
+svariableStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+ svariableState *myState = (svariableState *) self;
+ int natts = typeinfo->natts;
+ int outcols = 0;
+ int i;
+
+ for (i = 0; i < natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+
+ if (attr->attisdropped)
+ continue;
+
+ if (++outcols > 1)
+ elog(ERROR, "svariable DestReceiver can take only one attribute");
+
+ myState->typid = attr->atttypid;
+ myState->typmod = attr->atttypmod;
+ myState->typlen = attr->attlen;
+ myState->slot_offset = i;
+ }
+
+ myState->rows = 0;
+}
+
+/*
+ * Receive a tuple from the executor and store it in schema variable.
+ */
+static bool
+svariableReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
+{
+ svariableState *myState = (svariableState *) self;
+ Datum value;
+ bool isnull;
+ bool freeval = false;
+
+ /* Make sure the tuple is fully deconstructed */
+ slot_getallattrs(slot);
+
+ value = slot->tts_values[myState->slot_offset];
+ isnull = slot->tts_isnull[myState->slot_offset];
+
+ if (myState->typlen == -1 && !isnull && VARATT_IS_EXTERNAL(DatumGetPointer(value)))
+ {
+ value = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+ DatumGetPointer(value)));
+ freeval = true;
+ }
+
+ SetSchemaVariable(myState->varid, value, isnull, myState->typid, myState->typmod);
+
+ if (freeval)
+ pfree(DatumGetPointer(value));
+
+ return true;
+}
+
+/*
+ * Clean up at end of an executor run
+ */
+static void
+svariableShutdownReceiver(DestReceiver *self)
+{
+ /* Do nothing */
+}
+
+/*
+ * Destroy receiver when done with it
+ */
+static void
+svariableDestroyReceiver(DestReceiver *self)
+{
+ pfree(self);
+}
+
+/*
+ * Initially create a DestReceiver object.
+ */
+DestReceiver *
+CreateVariableDestReceiver(void)
+{
+ svariableState *self = (svariableState *) palloc0(sizeof(svariableState));
+
+ self->pub.receiveSlot = svariableReceiveSlot;
+ self->pub.rStartup = svariableStartupReceiver;
+ self->pub.rShutdown = svariableShutdownReceiver;
+ self->pub.rDestroy = svariableDestroyReceiver;
+ self->pub.mydest = DestVariable;
+
+ /* private fields will be set by SetVariableDestReceiverParams */
+
+ return (DestReceiver *) self;
+}
+
+/*
+ * Set parameters for a VariableDestReceiver
+ */
+void
+SetVariableDestReceiverParams(DestReceiver *self, Oid varid)
+{
+ svariableState *myState = (svariableState *) self;
+
+ Assert(myState->pub.mydest == DestVariable);
+ Assert(OidIsValid(varid));
+
+ myState->varid = varid;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c8220cf65..fcaa2db51a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(nonleafResultRelations);
COPY_NODE_FIELD(rootResultRelations);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks);
@@ -3000,6 +3001,7 @@ _copyQuery(const Query *from)
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_SCALAR_FIELD(hasAggs);
COPY_SCALAR_FIELD(hasWindowFuncs);
COPY_SCALAR_FIELD(hasTargetSRFs);
@@ -3118,6 +3120,18 @@ _copySelectStmt(const SelectStmt *from)
return newnode;
}
+static LetStmt *
+_copyLetStmt(const LetStmt *from)
+{
+ LetStmt *newnode = makeNode(LetStmt);
+
+ COPY_NODE_FIELD(target);
+ COPY_NODE_FIELD(selectStmt);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
static SetOperationStmt *
_copySetOperationStmt(const SetOperationStmt *from)
{
@@ -5166,6 +5180,9 @@ copyObjectImpl(const void *from)
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
+ case T_LetStmt:
+ retval = _copyLetStmt(from);
+ break;
case T_SetOperationStmt:
retval = _copySetOperationStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 378f2facb8..3ec472e19b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -949,6 +949,7 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_SCALAR_FIELD(canSetTag);
COMPARE_NODE_FIELD(utilityStmt);
COMPARE_SCALAR_FIELD(resultRelation);
+ COMPARE_SCALAR_FIELD(resultVariable);
COMPARE_SCALAR_FIELD(hasAggs);
COMPARE_SCALAR_FIELD(hasWindowFuncs);
COMPARE_SCALAR_FIELD(hasTargetSRFs);
@@ -1057,6 +1058,16 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
return true;
}
+static bool
+_equalLetStmt(const LetStmt *a, const LetStmt *b)
+{
+ COMPARE_NODE_FIELD(target);
+ COMPARE_NODE_FIELD(selectStmt);
+
+ return true;
+}
+
+
static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
@@ -3225,6 +3236,9 @@ equal(const void *a, const void *b)
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
break;
+ case T_LetStmt:
+ retval = _equalLetStmt(a, b);
+ break;
case T_SetOperationStmt:
retval = _equalSetOperationStmt(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b5af904c18..a32e01ae4d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -278,6 +278,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(nonleafResultRelations);
WRITE_NODE_FIELD(rootResultRelations);
+ WRITE_OID_FIELD(resultVariable);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks);
@@ -2794,6 +2795,16 @@ _outSelectStmt(StringInfo str, const SelectStmt *node)
WRITE_NODE_FIELD(rarg);
}
+static void
+_outLetStmt(StringInfo str, const LetStmt *node)
+{
+ WRITE_NODE_TYPE("LET");
+
+ WRITE_NODE_FIELD(target);
+ WRITE_NODE_FIELD(selectStmt);
+ WRITE_LOCATION_FIELD(location);
+}
+
static void
_outFuncCall(StringInfo str, const FuncCall *node)
{
@@ -2972,6 +2983,7 @@ _outQuery(StringInfo str, const Query *node)
appendStringInfoString(str, " :utilityStmt <>");
WRITE_INT_FIELD(resultRelation);
+ WRITE_INT_FIELD(resultVariable);
WRITE_BOOL_FIELD(hasAggs);
WRITE_BOOL_FIELD(hasWindowFuncs);
WRITE_BOOL_FIELD(hasTargetSRFs);
@@ -4192,6 +4204,9 @@ outNode(StringInfo str, const void *obj)
case T_SelectStmt:
_outSelectStmt(str, obj);
break;
+ case T_LetStmt:
+ _outLetStmt(str, obj);
+ break;
case T_ColumnDef:
_outColumnDef(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3254524223..4454327549 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -242,6 +242,7 @@ _readQuery(void)
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
+ READ_INT_FIELD(resultVariable);
READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasWindowFuncs);
READ_BOOL_FIELD(hasTargetSRFs);
@@ -1485,6 +1486,7 @@ _readPlannedStmt(void)
READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(nonleafResultRelations);
READ_NODE_FIELD(rootResultRelations);
+ READ_OID_FIELD(resultVariable);
READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 96bf0601a8..4573a88f35 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -335,7 +335,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
*/
if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
IsUnderPostmaster &&
- parse->commandType == CMD_SELECT &&
+ (parse->commandType == CMD_SELECT ||
+ parse->commandType == CMD_PLAN_UTILITY) &&
!parse->hasModifyingCTE &&
max_parallel_workers_per_gather > 0 &&
!IsParallelWorker() &&
@@ -352,6 +353,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->parallelModeOK = false;
}
+
+
/*
* glob->parallelModeNeeded is normally set to false here and changed to
* true during plan creation if a Gather or Gather Merge plan is actually
@@ -521,6 +524,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->resultRelations = glob->resultRelations;
result->nonleafResultRelations = glob->nonleafResultRelations;
result->rootResultRelations = glob->rootResultRelations;
+ result->resultVariable = parse->resultVariable;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks;
@@ -2173,7 +2177,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
* inheritance_planner, add the ModifyTable node.
*/
- if (parse->commandType != CMD_SELECT && !inheritance_update)
+ if (parse->commandType != CMD_SELECT && parse->commandType != CMD_PLAN_UTILITY && !inheritance_update)
{
List *withCheckOptionLists;
List *returningLists;
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 8603feef2b..2923e3fcc7 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -71,6 +71,7 @@ preprocess_targetlist(PlannerInfo *root)
{
Query *parse = root->parse;
int result_relation = parse->resultRelation;
+ int result_variable = parse->resultVariable;
List *range_table = parse->rtable;
CmdType command_type = parse->commandType;
RangeTblEntry *target_rte = NULL;
@@ -96,6 +97,10 @@ preprocess_targetlist(PlannerInfo *root)
target_relation = heap_open(target_rte->relid, NoLock);
}
+ else if (result_variable)
+ {
+ Assert(command_type == CMD_PLAN_UTILITY);
+ }
else
Assert(command_type == CMD_SELECT);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index ee6f4cdf4d..f232c6cfd1 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1268,7 +1268,8 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
{
Param *param = (Param *) node;
- if (param->paramkind == PARAM_EXTERN)
+ if (param->paramkind == PARAM_EXTERN ||
+ param->paramkind == PARAM_VARIABLE)
return false;
if (param->paramkind != PARAM_EXEC ||
@@ -4813,7 +4814,7 @@ substitute_actual_parameters_mutator(Node *node,
{
if (node == NULL)
return NULL;
- if (IsA(node, Param))
+ if (IsA(node, Param) && ((Param *) node)->paramkind != PARAM_VARIABLE)
{
Param *param = (Param *) node;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8369e3ad62..fc0cf34c7d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1272,7 +1272,7 @@ get_relation_constraints(PlannerInfo *root,
* descriptor, instead of constraint exclusion which is driven by the
* individual partition's partition constraint.
*/
- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
+ if (enable_partition_pruning && root->parse->commandType != CMD_SELECT && root->parse->commandType != CMD_PLAN_UTILITY)
{
List *pcqual = RelationGetPartitionQual(relation);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c601b6d40d..8a724fe3bf 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,7 +25,10 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/namespace.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -44,6 +47,8 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -78,6 +83,8 @@ static Query *transformCreateTableAsStmt(ParseState *pstate,
CreateTableAsStmt *stmt);
static Query *transformCallStmt(ParseState *pstate,
CallStmt *stmt);
+static Query *transformLetStmt(ParseState *pstate,
+ LetStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown);
#ifdef RAW_EXPRESSION_COVERAGE_TEST
@@ -267,6 +274,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
+ case T_LetStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
@@ -327,6 +335,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
(CallStmt *) parseTree);
break;
+ case T_LetStmt:
+ result = transformLetStmt(pstate,
+ (LetStmt *) parseTree);
+ break;
+
default:
/*
@@ -367,6 +380,7 @@ analyze_requires_snapshot(RawStmt *parseTree)
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
+ case T_LetStmt:
result = true;
break;
@@ -1567,6 +1581,204 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
return qry;
}
+/*
+ * transformLetStmt -
+ * transform an Let Statement
+ */
+static Query *
+transformLetStmt(ParseState *pstate, LetStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ List *exprList = NIL;
+ List *exprListCoer = NIL;
+ List *indirection = NIL;
+ ListCell *lc;
+ Query *selectQuery;
+ int i = 0;
+
+ Oid varid;
+
+ ParseExprKind sv_expr_kind;
+ char *attrname = NULL;
+ bool not_unique;
+ bool is_rowtype;
+ Oid typid;
+ int32 typmod;
+ Oid collid;
+
+ AclResult aclresult;
+ List *names = NULL;
+ int indirection_start;
+
+ sv_expr_kind = pstate->p_expr_kind;
+ pstate->p_expr_kind = EXPR_KIND_LET;
+
+ /* There can't be any outer WITH to worry about */
+ Assert(pstate->p_ctenamespace == NIL);
+
+ /* Exec this command as utility */
+ qry->commandType = CMD_PLAN_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ names = NamesFromList(stmt->target);
+
+ varid = identify_variable(names, &attrname, ¬_unique);
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("target \"%s\" of LET command is ambiguous",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ if (!OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("schema variable \"%s\" doesn't exists",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ qry->resultVariable = varid;
+
+ get_schema_variable_type_typmod_collid(varid, &typid, &typmod, &collid);
+
+ is_rowtype = type_is_rowtype(typid);
+
+ if (attrname && !is_rowtype)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("target variable \"%s\" is not row type",
+ schema_variable_get_name(varid)),
+ parser_errposition(pstate, stmt->location)));
+
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, NameListToString(names));
+
+ selectQuery = transformStmt(pstate, stmt->selectStmt);
+
+ /* The grammar should have produced a SELECT */
+ if (!IsA(selectQuery, Query) ||
+ selectQuery->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in LET ... SELECT");
+
+ /*----------
+ * Generate an expression list for the LET that selects all the
+ * non-resjunk columns from the subquery.
+ *----------
+ */
+ exprList = NIL;
+ foreach(lc, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->resjunk)
+ continue;
+
+ exprList = lappend(exprList, tle->expr);
+ }
+
+ /*
+ * Because doesn't support pattern matching, don't allow multicolumn result
+ */
+ if (list_length(exprList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expression is not scalar value"),
+ parser_errposition(pstate,
+ exprLocation((Node *) exprList))));
+
+ indirection_start = list_length(names) - (attrname ? 1 : 0);
+ indirection = list_copy_tail(stmt->target, indirection_start);
+
+ exprListCoer = NIL;
+ foreach(lc, exprList)
+ {
+ Node *orig_expr = (Node*) lfirst(lc);
+ Oid exprtypid = exprType((Node *) orig_expr);
+ Param *param = makeNode(Param);
+ Expr *expr = NULL;
+
+ param->paramkind = PARAM_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (indirection != NULL)
+ {
+ bool targetIsArray;
+ char *targetName;
+
+ targetName = attrname != NULL ? attrname : get_schema_variable_name(varid);
+ targetIsArray = OidIsValid(get_element_type(typid));
+
+ expr = (Expr *)
+ transformAssignmentIndirection(pstate,
+ (Node *) param,
+ targetName,
+ targetIsArray,
+ typid,
+ typmod,
+ InvalidOid,
+ list_head(indirection),
+ (Node *) orig_expr,
+ stmt->location);
+ }
+ else
+ expr = (Expr *)
+ coerce_to_target_type(pstate,
+ (Node *) orig_expr,
+ exprtypid,
+ typid, typmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ stmt->location);
+
+ if (expr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable \"%s\" is of type %s,"
+ " but expression is of type %s",
+ schema_variable_get_name(varid),
+ format_type_be(typid),
+ format_type_be(exprtypid)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *) orig_expr))));
+
+ exprListCoer = lappend(exprListCoer, expr);
+ }
+
+ /*
+ * Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
+ */
+ qry->targetList = NIL;
+ foreach(lc, exprListCoer)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ tle = makeTargetEntry(expr,
+ i + 1,
+ FigureColname((Node *)expr),
+ false);
+ qry->targetList = lappend(qry->targetList, tle);
+ }
+
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+ qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+
+ assign_query_collations(pstate, qry);
+
+ pstate->p_expr_kind = sv_expr_kind;
+
+ return qry;
+}
+
+
/*
* transformSetOperationStmt -
* transforms a set-operations tree
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4bd2223f26..0f9d67506a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -211,6 +211,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
JoinType jtype;
DropBehavior dbehavior;
OnCommitAction oncommit;
+ VariableEOXAction oneoxaction;
List *list;
Node *node;
Value *value;
@@ -257,8 +258,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSchemaVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -268,7 +269,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DropTransformStmt
DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
- ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
+ LetStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -400,6 +401,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
TriggerTransitions TriggerReferencing
publication_name_list
vacuum_relation_list opt_vacuum_relation_list
+ let_target
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -422,6 +424,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <ival> OptTemp
%type <ival> OptNoLog
%type <oncommit> OnCommitOption
+%type <oneoxaction> OnEOXActionOption
%type <ival> for_locking_strength
%type <node> for_locking_item
@@ -584,6 +587,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> optSchemaVarDefExpr
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -649,7 +653,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+ LEADING LEAKPROOF LEAST LEFT LET LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -687,8 +691,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -878,6 +882,7 @@ stmt :
| CreatePolicyStmt
| CreatePLangStmt
| CreateSchemaStmt
+ | CreateSchemaVarStmt
| CreateSeqStmt
| CreateStmt
| CreateSubscriptionStmt
@@ -917,6 +922,7 @@ stmt :
| ImportForeignSchemaStmt
| IndexStmt
| InsertStmt
+ | LetStmt
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
@@ -1808,7 +1814,12 @@ DiscardStmt:
n->target = DISCARD_SEQUENCES;
$$ = (Node *) n;
}
-
+ | DISCARD VARIABLES
+ {
+ DiscardStmt *n = makeNode(DiscardStmt);
+ n->target = DISCARD_VARIABLES;
+ $$ = (Node *) n;
+ }
;
@@ -4479,6 +4490,49 @@ create_extension_opt_item:
}
;
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE VARIABLE varname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSchemaVarStmt:
+ CREATE OptTemp VARIABLE qualified_name opt_as Typename opt_collate_clause optSchemaVarDefExpr OnEOXActionOption
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $4->relpersistence = $2;
+ n->variable = $4;
+ n->typeName = $6;
+ n->collClause = $7;
+ n->defexpr = $8;
+ n->eoxaction = $9;
+ n->if_not_exists = false;
+ $$ = (Node *) n;
+ }
+ | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename opt_collate_clause optSchemaVarDefExpr OnEOXActionOption
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $7->relpersistence = $2;
+ n->variable = $7;
+ n->typeName = $9;
+ n->collClause = $10;
+ n->defexpr = $11;
+ n->eoxaction = $12;
+ n->if_not_exists = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+optSchemaVarDefExpr: DEFAULT b_expr { $$ = $2; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+OnEOXActionOption: ON TRANSACTION END_P DROP { $$ = VARIABLE_EOX_DROP; }
+ | ON TRANSACTION END_P RESET { $$ = VARIABLE_EOX_RESET; }
+ | ON ROLLBACK RESET { $$ = VARIABLE_EOX_ROLLBACK_RESET; }
+ | /*EMPTY*/ { $$ = VARIABLE_EOX_NOOP; }
+
/*****************************************************************************
*
* ALTER EXTENSION name UPDATE [ TO version ]
@@ -6340,6 +6394,7 @@ drop_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name_list */
@@ -6609,6 +6664,7 @@ comment_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -6747,6 +6803,7 @@ security_label_type_any_name:
| TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -7168,6 +7225,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| ALL TABLES IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7208,6 +7273,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
;
@@ -7368,6 +7441,7 @@ defacl_privilege_target:
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
;
@@ -8964,6 +9038,25 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newname = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
opt_column: COLUMN { $$ = COLUMN; }
@@ -9282,6 +9375,25 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newschema = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
/*****************************************************************************
@@ -9517,6 +9629,14 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
@@ -10698,6 +10818,7 @@ ExplainableStmt:
| CreateMatViewStmt
| RefreshMatViewStmt
| ExecuteStmt /* by default all are $$=$1 */
+ | LetStmt
;
explain_option_list:
@@ -10755,6 +10876,7 @@ PreparableStmt:
| InsertStmt
| UpdateStmt
| DeleteStmt /* by default all are $$=$1 */
+ | LetStmt
;
/*****************************************************************************
@@ -11153,6 +11275,44 @@ opt_hold: /* EMPTY */ { $$ = 0; }
| WITHOUT HOLD { $$ = 0; }
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * LET STATEMENTS
+ *
+ *****************************************************************************/
+LetStmt: LET let_target '=' a_expr
+ {
+ LetStmt *n = makeNode(LetStmt);
+ SelectStmt *select = makeNode(SelectStmt);
+ ResTarget *res = makeNode(ResTarget);
+
+ n->target = $2;
+
+ /* Create target list for implicit query */
+ res->name = NULL;
+ res->indirection = NIL;
+ res->val = (Node *) $4;
+ res->location = @4;
+
+ select->targetList = list_make1(res);
+ n->selectStmt = (Node *) select;
+
+ n->location = @2;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+let_target:
+ ColId opt_indirection
+ {
+ $$ = list_make1(makeString($1));
+ if ($2)
+ $$ = list_concat($$,
+ check_indirection($2, yyscanner));
+ }
+
/*****************************************************************************
*
* QUERY:
@@ -15132,6 +15292,7 @@ unreserved_keyword:
| LARGE_P
| LAST_P
| LEAKPROOF
+ | LET
| LEVEL
| LISTEN
| LOAD
@@ -15280,6 +15441,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 61727e1d71..6823612fba 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -349,6 +349,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
Assert(false); /* can't happen */
break;
case EXPR_KIND_OTHER:
+ case EXPR_KIND_LET:
/*
* Accept aggregate/grouping here; caller must throw error if
@@ -465,6 +466,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
if (isAgg)
err = _("aggregate functions are not allowed in DEFAULT expressions");
@@ -879,6 +881,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("window functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -902,6 +905,8 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_CALL_ARGUMENT:
err = _("window functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("window functions are not allowed in LET statement");
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a9b6..cc614b3902 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -37,6 +38,7 @@
#include "utils/date.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
+#include "utils/typcache.h"
#include "utils/xml.h"
@@ -116,6 +118,9 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
+static Node *makeParamSchemaVariable(ParseState *pstate,
+ Oid varid, Oid typid, int32 typmod, Oid collid,
+ char *attrname, int location);
static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
int location);
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
@@ -512,6 +517,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
+ Oid varid = InvalidOid;
+ char *attrname = NULL;
+ bool not_unique;
+
RangeTblEntry *rte;
int levels_up;
enum
@@ -749,6 +758,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break;
}
+ varid = identify_variable(cref->fields, &attrname, ¬_unique);
+
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("schema variable reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+
/*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
@@ -773,6 +791,72 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
parser_errposition(pstate, cref->location)));
}
+ if (OidIsValid(varid))
+ {
+ Oid typid;
+ int32 typmod;
+ Oid collid;
+
+ get_schema_variable_type_typmod_collid(varid, &typid, &typmod, &collid);
+
+ if (node != NULL)
+ {
+ /*
+ * some collision can be solved simply here to reduce errors
+ * based on simply existence of some variables. Often error
+ * can be using alias same like variable name. In this case,
+ * when we found column reference, and we found reference to
+ * possible composite variable, but the variable is not composite,
+ * then we can ignore the variable as simply improper, and we
+ * use column reference only.
+ */
+ if (attrname)
+ {
+ if (type_is_rowtype(typid))
+ {
+ TupleDesc tupdesc;
+ bool found = false;
+ int i;
+
+ /* slow part, I hope it will not be to often */
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ if (namestrcmp(&(TupleDescAttr(tupdesc, i)->attname), attrname) == 0 &&
+ !TupleDescAttr(tupdesc, i)->attisdropped)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ FreeTupleDesc(tupdesc);
+
+ /* there are not composite variable with this field */
+ if (!found)
+ varid = InvalidOid;
+ }
+ else
+ /* there are not composite variable with this name */
+ varid = InvalidOid;
+ }
+
+ /* Raise error if varid is still valid. It should be really amigonuous */
+ if (OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ errdetail("The qualified identifier can be column reference or schema variable reference"),
+ parser_errposition(pstate, cref->location)));
+ }
+
+ if (OidIsValid(varid))
+ node = makeParamSchemaVariable(pstate,
+ varid, typid, typmod, collid,
+ attrname, cref->location);
+ }
+
/*
* Throw error if no translation found.
*/
@@ -807,6 +891,60 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return node;
}
+/*
+ * Generate param variable for reference to schema variable
+ */
+static Node *
+makeParamSchemaVariable(ParseState *pstate, Oid varid, Oid typid, int32 typmod, Oid collid, char *attrname, int location)
+{
+ Param *param;
+
+ param = makeNode(Param);
+
+ param->paramkind = PARAM_VARIABLE;
+ param->paramid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+ param->paramcollid = collid;
+
+ if (attrname != NULL)
+ {
+ TupleDesc tupdesc;
+ int i;
+
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+
+ if (strcmp(attrname, NameStr(att->attname)) == 0 &&
+ !att->attisdropped)
+ {
+ /* Success, so generate a FieldSelect expression */
+ FieldSelect *fselect = makeNode(FieldSelect);
+
+ fselect->arg = (Expr *) param;
+ fselect->fieldnum = i + 1;
+ fselect->resulttype = att->atttypid;
+ fselect->resulttypmod = att->atttypmod;
+ /* save attribute's collation for parse_collate.c */
+ fselect->resultcollid = att->attcollation;
+
+ ReleaseTupleDesc(tupdesc);
+ return (Node *) fselect;
+ }
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("could not identify column \"%s\" in variable", attrname),
+ parser_errposition(pstate, location)));
+ }
+
+ return (Node *) param;
+}
+
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
@@ -1818,6 +1956,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
case EXPR_KIND_VALUES_SINGLE:
+ case EXPR_KIND_LET:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
@@ -1826,6 +1965,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -3460,6 +3600,7 @@ ParseExprKindName(ParseExprKind exprKind)
return "CHECK";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
return "DEFAULT";
case EXPR_KIND_INDEX_EXPRESSION:
return "index expression";
@@ -3475,6 +3616,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "PARTITION BY";
case EXPR_KIND_CALL_ARGUMENT:
return "CALL";
+ case EXPR_KIND_LET:
+ return "LET";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 44257154b8..b2c9900e00 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2347,6 +2347,7 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("set-returning functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -2370,6 +2371,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_CALL_ARGUMENT:
err = _("set-returning functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("set-returning functions are not allowed in CALL arguments");
+ break;
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4932e58022..c60fe011f7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -35,16 +35,6 @@
static void markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
Var *var, int levelsup);
-static Node *transformAssignmentIndirection(ParseState *pstate,
- Node *basenode,
- const char *targetName,
- bool targetIsArray,
- Oid targetTypeId,
- int32 targetTypMod,
- Oid targetCollation,
- ListCell *indirection,
- Node *rhs,
- int location);
static Node *transformAssignmentSubscripts(ParseState *pstate,
Node *basenode,
const char *targetName,
@@ -672,7 +662,7 @@ updateTargetListEntry(ParseState *pstate,
* might want to decorate indirection cells with their own location info,
* in which case the location argument could probably be dropped.)
*/
-static Node *
+Node *
transformAssignmentIndirection(ParseState *pstate,
Node *basenode,
const char *targetName,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index d830569641..c27aecedb5 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3359,7 +3359,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
* get executed. Also, utilities aren't rewritten at all (do we still
* need that check?)
*/
- if (event != CMD_SELECT && event != CMD_UTILITY)
+ if (event != CMD_SELECT && event != CMD_UTILITY && event != CMD_PLAN_UTILITY)
{
int result_relation;
RangeTblEntry *rt_entry;
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 61ef396d8a..6a068af799 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -212,7 +212,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
}
/*
- * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
+ * For SELECT, LET, UPDATE and DELETE, add security quals to enforce the USING
* policies. These security quals control access to existing table rows.
* Restrictive policies are combined together using AND, and permissive
* policies are combined together using OR.
@@ -222,6 +222,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
&restrictive_policies);
if (commandType == CMD_SELECT ||
+ commandType == CMD_PLAN_UTILITY ||
commandType == CMD_UPDATE ||
commandType == CMD_DELETE)
add_security_quals(rt_index,
@@ -423,6 +424,7 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
switch (cmd)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
if (policy->polcmd == ACL_SELECT_CHR)
cmd_matches = true;
break;
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index c95a4d519d..47fb0f38b1 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -37,6 +37,7 @@
#include "executor/functions.h"
#include "executor/tqueue.h"
#include "executor/tstoreReceiver.h"
+#include "executor/svariableReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/portal.h"
@@ -143,6 +144,9 @@ CreateDestReceiver(CommandDest dest)
case DestTupleQueue:
return CreateTupleQueueDestReceiver(NULL);
+
+ case DestVariable:
+ return CreateVariableDestReceiver();
}
/* should never get here */
@@ -178,6 +182,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -222,6 +227,7 @@ NullCommand(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -268,6 +274,7 @@ ReadyForQuery(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b5804f64ad..35199fd0dc 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
@@ -344,7 +345,7 @@ ProcessUtility(PlannedStmt *pstmt,
char *completionTag)
{
Assert(IsA(pstmt, PlannedStmt));
- Assert(pstmt->commandType == CMD_UTILITY);
+ Assert(pstmt->commandType == CMD_UTILITY || pstmt->commandType == CMD_PLAN_UTILITY);
Assert(queryString != NULL); /* required as of 8.4 */
/*
@@ -915,6 +916,14 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
}
+ case T_LetStmt:
+ {
+ doLetStmt(pstmt, params, queryEnv, queryString);
+ if (completionTag)
+ strcpy(completionTag, "LET");
+ }
+ break;
+
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
@@ -1221,6 +1230,10 @@ ProcessUtilitySlow(ParseState *pstate,
}
break;
+ case T_CreateSchemaVarStmt:
+ address = DefineSchemaVariable(pstate, (CreateSchemaVarStmt *) parsetree);
+ break;
+
/*
* ************* object creation / destruction **************
*/
@@ -2055,6 +2068,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_STATISTIC_EXT:
tag = "ALTER STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "ALTER VARIABLE";
+ break;
default:
tag = "???";
break;
@@ -2104,6 +2120,10 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ case T_LetStmt:
+ tag = "LET";
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
@@ -2358,6 +2378,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "DROP VARIABLE";
+ break;
default:
tag = "???";
}
@@ -2639,6 +2662,9 @@ CreateCommandTag(Node *parsetree)
case DISCARD_SEQUENCES:
tag = "DISCARD SEQUENCES";
break;
+ case DISCARD_VARIABLES:
+ tag = "DISCARD VARIABLES";
+ break;
default:
tag = "???";
}
@@ -2844,6 +2870,7 @@ CreateCommandTag(Node *parsetree)
tag = "DELETE";
break;
case CMD_UTILITY:
+ case CMD_PLAN_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
default:
@@ -2915,6 +2942,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_CreateSchemaVarStmt:
+ tag = "CREATE VARIABLE";
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -2961,6 +2992,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ case T_LetStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a45e093de7..952c0d9628 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -315,6 +315,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_READ_CHR:
+ read = ACL_READ;
+ break;
+ case ACL_WRITE_CHR:
+ read = ACL_WRITE;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -808,6 +814,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -903,6 +913,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'T':
objtype = OBJECT_TYPE;
break;
+ case 'V':
+ objtype = OBJECT_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
}
@@ -1627,6 +1640,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CONNECT;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
+ if (pg_strcasecmp(priv_type, "READ") == 0)
+ return ACL_READ;
+ if (pg_strcasecmp(priv_type, "WRITE") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1721,6 +1738,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5cce3f1242..7cdb40952b 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -38,6 +38,7 @@
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
#include "common/keywords.h"
@@ -7395,6 +7396,14 @@ get_parameter(Param *param, deparse_context *context)
return;
}
+ /* translate paramid to original schema variable name */
+ if (param->paramkind == PARAM_VARIABLE)
+ {
+ appendStringInfo(context->buf, "%s",
+ schema_variable_get_name(param->paramid));
+ return;
+ }
+
/*
* Not PARAM_EXEC, or couldn't find referent: just print $N.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bba595ad1d..858a6dd4be 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1691,6 +1691,18 @@ get_relname_relid(const char *relname, Oid relnamespace)
ObjectIdGetDatum(relnamespace));
}
+/*
+ * get_varname_varid
+ * Given name and namespace of variable, look up the OID.
+ */
+Oid
+get_varname_varid(const char *varname, Oid varnamespace)
+{
+ return GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(varnamespace));
+}
+
#ifdef NOT_USED
/*
* get_relnatts
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b381782a3..35dc32f649 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -73,6 +73,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "utils/rel.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
@@ -968,6 +969,28 @@ static const struct cachedesc cacheinfo[] = {
0
},
2
+ },
+ {VariableRelationId, /* VARIABLENAMENSP */
+ VariableNameNspIndexId,
+ 2,
+ {
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ 0,
+ 0
+ },
+ 8
+ },
+ {VariableRelationId, /* VARIABLEOID */
+ VariableObjectIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
}
};
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 9b5869add8..c4e4d10c6a 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -296,6 +296,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading variables\n");
+ getVariables(fout);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 36e3383b85..58d15af7b1 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3465,6 +3465,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(type, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(type, "STATISTICS") == 0 ||
+ strcmp(type, "VARIABLE") == 0 ||
/* non-schema-specified objects */
strcmp(type, "DATABASE") == 0 ||
strcmp(type, "PROCEDURAL LANGUAGE") == 0 ||
@@ -3664,7 +3665,8 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
strcmp(te->desc, "SERVER") == 0 ||
strcmp(te->desc, "STATISTICS") == 0 ||
strcmp(te->desc, "PUBLICATION") == 0 ||
- strcmp(te->desc, "SUBSCRIPTION") == 0)
+ strcmp(te->desc, "SUBSCRIPTION") == 0 ||
+ strcmp(te->desc, "VARIABLE") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index f0ea83e6a9..dd5f38e327 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -258,6 +258,7 @@ static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
+static void dumpVariable(Archive *fout, VariableInfo *varinfo);
static void dumpDatabase(Archive *AH);
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
const char *dbname, Oid dboid);
@@ -4224,6 +4225,222 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+/*
+ * getVariables
+ * get information about variables
+ */
+void
+getVariables(Archive *fout)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer init_acl_subquery = createPQExpBuffer();
+ PQExpBuffer init_racl_subquery = createPQExpBuffer();
+ PGresult *res;
+ VariableInfo *varinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_varname;
+ int i_varnamespace;
+ int i_vartype;
+ int i_vartypname;
+ int i_vardefexpr;
+ int i_rolname;
+ int i_varacl;
+ int i_rvaracl;
+ int i_initvaracl;
+ int i_initrvaracl;
+ int i_vareoxaction;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion <= 110000)
+ return;
+
+ acl_subquery = createPQExpBuffer();
+ racl_subquery = createPQExpBuffer();
+ init_acl_subquery = createPQExpBuffer();
+ init_racl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
+ init_racl_subquery, "v.varacl", "v.varowner", "'V'",
+ dopt->binary_upgrade);
+
+ query = createPQExpBuffer();
+
+ resetPQExpBuffer(query);
+
+ /* Get the variables in current database. */
+ appendPQExpBuffer(query,
+ "SELECT v.tableoid, v.oid, v.varname, "
+ "v.vareoxaction, "
+ "v.varnamespace, "
+ "(%s varowner) AS rolname, "
+ "%s as varacl, "
+ "%s as rvaracl, "
+ "%s as initvaracl, "
+ "%s as initrvaracl, "
+ "v.vartype, "
+ "pg_catalog.format_type(v.vartype, v.vartypmod) as vartypname, "
+ "pg_catalog.pg_get_expr(v.vardefexpr,0) as vardefexpr "
+ "FROM pg_variable v "
+ "LEFT JOIN pg_init_privs pip "
+ "ON (v.oid = pip.objoid "
+ "AND pip.classoid = 'pg_variable'::regclass "
+ "AND pip.objsubid = 0)",
+ username_subquery,
+ acl_subquery->data,
+ racl_subquery->data,
+ init_acl_subquery->data,
+ init_racl_subquery->data);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(init_acl_subquery);
+ destroyPQExpBuffer(init_racl_subquery);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_varname = PQfnumber(res, "varname");
+ i_varnamespace = PQfnumber(res, "varnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_vartype = PQfnumber(res, "vartype");
+ i_vartypname = PQfnumber(res, "vartypname");
+ i_vareoxaction = PQfnumber(res, "vareoxaction");
+ i_vardefexpr = PQfnumber(res, "vardefexpr");
+ i_varacl = PQfnumber(res, "varacl");
+ i_rvaracl = PQfnumber(res, "rvaracl");
+ i_initvaracl = PQfnumber(res, "initvaracl");
+ i_initrvaracl = PQfnumber(res, "initrvaracl");
+
+ varinfo = pg_malloc(ntups * sizeof(VariableInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ TypeInfo *vtype;
+
+ varinfo[i].dobj.objType = DO_VARIABLE;
+ varinfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ varinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&varinfo[i].dobj);
+ varinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_varname));
+ varinfo[i].dobj.namespace =
+ findNamespace(fout,
+ atooid(PQgetvalue(res, i, i_varnamespace)));
+
+ varinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ varinfo[i].vartype = atooid(PQgetvalue(res, i, i_vartype));
+ varinfo[i].vartypname = pg_strdup(PQgetvalue(res, i, i_vartypname));
+
+ varinfo[i].vareoxaction = pg_strdup(PQgetvalue(res, i, i_vareoxaction));
+
+ varinfo[i].varacl = pg_strdup(PQgetvalue(res, i, i_varacl));
+ varinfo[i].rvaracl = pg_strdup(PQgetvalue(res, i, i_rvaracl));
+ varinfo[i].initvaracl = pg_strdup(PQgetvalue(res, i, i_initvaracl));
+ varinfo[i].initrvaracl = pg_strdup(PQgetvalue(res, i, i_initrvaracl));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ /* Do not try to dump ACL if no ACL exists. */
+ if (PQgetisnull(res, i, i_varacl) && PQgetisnull(res, i, i_rvaracl) &&
+ PQgetisnull(res, i, i_initvaracl) &&
+ PQgetisnull(res, i, i_initrvaracl))
+ varinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+
+ if (PQgetisnull(res, i, i_vardefexpr))
+ varinfo[i].vardefexpr = NULL;
+ else
+ varinfo[i].vardefexpr = pg_strdup(PQgetvalue(res, i, i_vardefexpr));
+
+ if (strlen(varinfo[i].rolname) == 0)
+ write_msg(NULL, "WARNING: owner of variable \"%s\" appears to be invalid\n",
+ varinfo[i].dobj.name);
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ vtype = findTypeByOid(varinfo[i].vartype);
+ addObjectDependency(&varinfo[i].dobj, vtype->dobj.dumpId);
+ }
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpVariable
+ * dump the definition of the given variables
+ */
+static void
+dumpVariable(Archive *fout, VariableInfo *varinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ const char *varname;
+ const char *vartypname;
+ const char *vardefexpr;
+ const char *vareoxaction;
+
+ /* Skip if not to be dumped */
+ if (!varinfo->dobj.dump || dopt->dataOnly)
+ return;
+
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ varname = fmtQualifiedDumpable(varinfo);
+ vartypname = varinfo->vartypname;
+ vardefexpr = varinfo->vardefexpr;
+ vareoxaction = varinfo->vareoxaction;
+
+ appendPQExpBuffer(delq, "DROP VARIABLE %s;\n",
+ varname);
+
+ appendPQExpBuffer(query, "CREATE VARIABLE %s AS %s",
+ varname, vartypname);
+
+ if (vardefexpr)
+ appendPQExpBuffer(query, " DEFAULT %s",
+ vardefexpr);
+
+ if (strcmp(vareoxaction, "d") == 0)
+ appendPQExpBuffer(query, " ON TRANSACTION END DROP");
+ else if (strcmp(vareoxaction, "r") == 0)
+ appendPQExpBuffer(query, " ON TRANSACTION END RESET");
+ if (strcmp(vareoxaction, "R") == 0)
+ appendPQExpBuffer(query, " ON ROLLBACK RESET");
+
+ appendPQExpBuffer(query, ";\n");
+
+ ArchiveEntry(fout, varinfo->dobj.catId, varinfo->dobj.dumpId,
+ varinfo->dobj.name,
+ NULL,
+ NULL,
+ varinfo->rolname, false,
+ "VARIABLE", SECTION_PRE_DATA,
+ query->data, delq->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ if (varinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, "VARIABLE", varname,
+ NULL, varinfo->rolname,
+ varinfo->dobj.catId, 0, varinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -9791,6 +10008,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_VARIABLE:
+ dumpVariable(fout, (VariableInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -17877,6 +18097,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_OPFAMILY:
case DO_COLLATION:
case DO_CONVERSION:
+ case DO_VARIABLE:
case DO_TABLE:
case DO_ATTRDEF:
case DO_PROCLANG:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 1448005f30..5471e667fc 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -84,7 +84,8 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_VARIABLE
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -625,6 +626,23 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/*
+ * The VariableInfo struct is used to represent schema variables
+ */
+typedef struct _VariableInfo
+{
+ DumpableObject dobj;
+ Oid vartype;
+ char *vartypname;
+ char *rolname; /* name of owner, or empty string */
+ char *vareoxaction;
+ char *vardefexpr;
+ char *varacl;
+ char *rvaracl;
+ char *initvaracl;
+ char *initrvaracl;
+} VariableInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -725,5 +743,6 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern void getVariables(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 6227a8fd26..969a021771 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -1477,6 +1477,10 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POST-DATA BOUNDARY (ID %d)",
obj->dumpId);
return;
+ case DO_VARIABLE:
+ snprintf(buf, bufsize,
+ "VARIABLE %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
}
/* shouldn't get here */
snprintf(buf, bufsize,
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index ec751a7c23..2a67766ed4 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2601,6 +2601,38 @@ my %tests = (
},
},
+ 'CREATE VARIABLE test_variable' => {
+ all_runs => 1,
+ catch_all => 'CREATE ... commands',
+ create_order => 61,
+ create_sql => 'CREATE VARIABLE dump_test.variable AS integer DEFAULT 0;',
+ regexp => qr/^
+ \QCREATE VARIABLE dump_test.variable AS integer DEFAULT 0;\E/xm,
+ like => {
+ binary_upgrade => 1,
+ clean => 1,
+ clean_if_exists => 1,
+ createdb => 1,
+ defaults => 1,
+ exclude_test_table => 1,
+ exclude_test_table_data => 1,
+ no_blobs => 1,
+ no_privs => 1,
+ no_owner => 1,
+ only_dump_test_schema => 1,
+ pg_dumpall_dbprivs => 1,
+ schema_only => 1,
+ section_pre_data => 1,
+ test_schema_plus_blobs => 1,
+ with_oids => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_test_table => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1,
+ role => 1,
+ section_post_data => 1, }, },
+
'CREATE VIEW test_view' => {
create_order => 61,
create_sql => 'CREATE VIEW dump_test.test_view
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..73a752fd7e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -853,6 +853,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
break;
}
break;
+ case 'V': /* Variables */
+ success = listVariables(pattern, show_verbose);
+ break;
case 'x': /* Extensions */
if (show_verbose)
success = listExtensionContents(pattern);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4ca0db1d0c..7a650af19b 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4198,6 +4198,85 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
return true;
}
+/*
+ * \dV
+ *
+ * listVariables()
+ */
+bool
+listVariables(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false, false, false, false, false};
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname as \"%s\",\n"
+ " v.varname as \"%s\",\n"
+ " pg_catalog.format_type(v.vartype, v.vartypmod) as \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(v.varowner) as \"%s\",\n"
+ " pg_catalog.pg_get_expr(v.vardefexpr, 0) as \"%s\",\n"
+ " CASE v.vareoxaction\n"
+ " WHEN 'd' THEN 'ON TRANSACTION END DROP'\n"
+ " WHEN 'r' THEN 'ON TRANSACTION END RESET'\n"
+ " WHEN 'R' THEN 'ON ROLLBACK RESET' END as \"%s\"",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Type"),
+ gettext_noop("Owner"),
+ gettext_noop("Default"),
+ gettext_noop("Transaction end action"));
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_variable v"
+ "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = v.varnamespace");
+
+ appendPQExpBufferStr(&buf, "\nWHERE true\n");
+ if (!pattern)
+ appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
+ " AND n.nspname <> 'information_schema'\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "v.varname", NULL,
+ "pg_catalog.pg_variable_is_visible(v.oid)");
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ /*
+ * Most functions in this file are content to print an empty table when
+ * there are no matching objects. We intentionally deviate from that
+ * here, but only in !quiet mode, for historical reasons.
+ */
+ if (PQntuples(res) == 0 && !pset.quiet)
+ {
+ if (pattern)
+ psql_error("Did not find any schema variable named \"%s\".\n",
+ pattern);
+ else
+ psql_error("Did not find any schema variables.\n");
+ }
+ else
+ {
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of variables");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+ }
+
+ PQclear(res);
+ return true;
+}
/*
* \dFp
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..ecc4e3a531 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
/* \dt, \di, \ds, \dS, etc. */
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
+/* \dV */
+extern bool listVariables(const char *pattern, bool varbose);
+
/* \dD */
extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 316030d358..adcc36cb6e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
@@ -257,6 +257,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[S+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dV [PATTERN] list variables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index bb696f8ee9..a7583810e8 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -805,6 +805,22 @@ static const SchemaQuery Query_for_list_of_statistics = {
NULL
};
+static const SchemaQuery Query_for_list_of_variables = {
+ /* min_server_version */
+ 0,
+ /* catname */
+ "pg_catalog.pg_variable v",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_variable_is_visible(v.oid)",
+ /* namespace */
+ "v.varnamespace",
+ /* result */
+ "pg_catalog.quote_ident(v.varname)",
+ /* qualresult */
+ NULL
+};
/*
* Queries to get lists of names of various kinds of things, possibly
@@ -1249,6 +1265,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
{"USER MAPPING FOR", NULL, NULL, NULL},
+ {"VARIABLE", NULL, NULL, &Query_for_list_of_variables},
{"VIEW", NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
@@ -1604,7 +1621,7 @@ psql_completion(const char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
- "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LET", "LISTEN", "LOAD", "LOCK",
"MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
"RESET", "REVOKE", "ROLLBACK",
@@ -1621,9 +1638,9 @@ psql_completion(const char *text, int start, int end)
"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
- "\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+ "\\dm", "\\dn", "\\do", "\\dO", "\\dp"
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+ "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", "\\dV",
"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
"\\endif", "\\errverbose", "\\ev",
"\\f",
@@ -1988,6 +2005,9 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
else if (Matches4("ALTER", "SYSTEM", "SET", MatchAny))
COMPLETE_WITH_CONST("TO");
+ /* ALTER VARIABLE <name> */
+ else if (Matches3("ALTER", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
/* ALTER VIEW <name> */
else if (Matches3("ALTER", "VIEW", MatchAny))
COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
@@ -2837,6 +2857,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
COMPLETE_WITH_LIST2("GROUP", "ROLE");
+/* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
+ /* Complete CREATE VARIABLE <name> with AS */
+ else if (TailMatches3("CREATE", "VARIABLE", MatchAny))
+ COMPLETE_WITH_CONST("AS");
+ /* Complete CREATE VARIABLE <name> with AS types*/
+ else if (TailMatches4("CREATE", "VARIABLE", MatchAny, "AS"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE VIEW <name> with AS */
else if (TailMatches3("CREATE", "VIEW", MatchAny))
@@ -2890,7 +2918,7 @@ psql_completion(const char *text, int start, int end)
/* DISCARD */
else if (Matches1("DISCARD"))
- COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP");
+ COMPLETE_WITH_LIST5("ALL", "PLANS", "SEQUENCES", "TEMP", "VARIABLES");
/* DO */
else if (Matches1("DO"))
@@ -2992,6 +3020,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP VARIABLE */
+ else if (Matches2("DROP", "VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ else if (Matches3("DROP", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3002,14 +3036,14 @@ psql_completion(const char *text, int start, int end)
* Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "ANALYZE", "VERBOSE");
+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "ANALYZE", "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "ANALYZE"))
- COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "VERBOSE");
+ COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "VERBOSE") ||
Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
- COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
+ COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "LET");
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
@@ -3118,6 +3152,7 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'ALL ROUTINES IN SCHEMA'"
" UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
" UNION SELECT 'ALL TABLES IN SCHEMA'"
+ " UNION SELECT 'ALL VARIABLES IN SCHEMA'"
" UNION SELECT 'DATABASE'"
" UNION SELECT 'DOMAIN'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
@@ -3131,14 +3166,16 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'SEQUENCE'"
" UNION SELECT 'TABLE'"
" UNION SELECT 'TABLESPACE'"
- " UNION SELECT 'TYPE'");
+ " UNION SELECT 'TYPE'"
+ " UNION SELECT 'VARIABLE'");
}
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
- COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA",
+ COMPLETE_WITH_LIST6("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
- "TABLES IN SCHEMA");
+ "TABLES IN SCHEMA",
+ "VARIABLES IN SCHEMA");
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
@@ -3172,6 +3209,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches1("TYPE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+ else if (TailMatches1("VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
COMPLETE_WITH_CONST("TO");
else
@@ -3324,7 +3363,7 @@ psql_completion(const char *text, int start, int end)
/* PREPARE xx AS */
else if (Matches3("PREPARE", MatchAny, "AS"))
- COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
+ COMPLETE_WITH_LIST5("SELECT", "UPDATE", "INSERT", "DELETE FROM", "LET");
/*
* PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -3547,6 +3586,14 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
COMPLETE_WITH_CONST("=");
+/* LET --- can be inside EXPLAIN, PREPARE etc */
+ /* If prev. word is LET suggest a list of variables */
+ else if (TailMatches1("LET"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ /* Complete LET <variable> with "=" */
+ else if (TailMatches2("LET", MatchAny))
+ COMPLETE_WITH_CONST("=");
+
/* USER MAPPING */
else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 46c271a46c..3e38a05e55 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -180,7 +180,8 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_VARIABLE /* pg_variable */
} ObjectClass;
#define LAST_OCLASS OCLASS_TRANSFORM
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 254fbef1f7..67ed04f351 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -360,4 +360,10 @@ DECLARE_UNIQUE_INDEX(pg_subscription_subname_index, 6115, on pg_subscription usi
DECLARE_UNIQUE_INDEX(pg_subscription_rel_srrelid_srsubid_index, 6117, on pg_subscription_rel using btree(srrelid oid_ops, srsubid oid_ops));
#define SubscriptionRelSrrelidSrsubidIndexId 6117
+DECLARE_UNIQUE_INDEX(pg_variable_oid_index, 4288, on pg_variable using btree(oid oid_ops));
+#define VariableObjectIndexId 4288
+
+DECLARE_UNIQUE_INDEX(pg_variable_varname_nsp_index, 4289, on pg_variable using btree(varname name_ops, varnamespace oid_ops));
+#define VariableNameNspIndexId 4289
+
#endif /* INDEXING_H */
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 0e202372d5..8812075b2e 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -75,10 +75,13 @@ extern Oid RangeVarGetAndCheckCreationNamespace(RangeVar *newRelation,
extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
+extern bool VariableIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
+extern bool VariableIsVisible(Oid varid);
+
extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
bool expand_variadic,
@@ -146,6 +149,10 @@ extern void SetTempNamespaceState(Oid tempNamespaceId,
Oid tempToastNamespaceId);
extern void ResetTempTableNamespace(void);
+extern List *NamesFromList(List *names);
+extern Oid lookup_variable(const char *nspname, const char *varname, bool missing_ok);
+extern Oid identify_variable(List *names, char **attrname, bool *not_uniq);
+
extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
extern OverrideSearchPath *CopyOverrideSearchPath(OverrideSearchPath *path);
extern bool OverrideSearchPathMatchesCurrent(OverrideSearchPath *path);
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index aee49fdb6d..f84ea21c68 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -57,6 +57,7 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a14651010f..61cbe65805 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5961,6 +5961,9 @@
proname => 'pg_collation_is_visible', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid',
prosrc => 'pg_collation_is_visible' },
+{ oid => '4187', descr => 'is schema variable visible in search path?',
+ proname => 'pg_variable_is_visible', procost => '10', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid', prosrc => 'pg_variable_is_visible' },
{ oid => '2854', descr => 'get OID of current session\'s temp schema, if any',
proname => 'pg_my_temp_schema', provolatile => 's', proparallel => 'r',
diff --git a/src/include/catalog/pg_variable.h b/src/include/catalog/pg_variable.h
new file mode 100644
index 0000000000..1e0ae6da53
--- /dev/null
+++ b/src/include/catalog/pg_variable.h
@@ -0,0 +1,102 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.h
+ * definition of schema variables system catalog (pg_variables)
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_variable.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VARIABLE_H
+#define PG_VARIABLE_H
+
+#include "catalog/genbki.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable_d.h"
+#include "utils/acl.h"
+
+/* ----------------
+ * pg_variable definition. cpp turns this into
+ * typedef struct FormData_pg_variable
+ * ----------------
+ */
+CATALOG(pg_variable,4287,VariableRelationId)
+{
+ NameData varname; /* variable name */
+ Oid varnamespace; /* OID of namespace containing variable class */
+ Oid vartype; /* OID of entry in pg_type for variable's type */
+ int32 vartypmod; /* typmode for variable's type */
+ Oid varowner; /* class owner */
+ Oid varcollation; /* variable collation */
+ char vareoxaction; /* action on transaction end */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+
+ /* list of expression trees for variable default (NULL if none) */
+ pg_node_tree vardefexpr BKI_DEFAULT(_null_);
+
+ aclitem varacl[1] BKI_DEFAULT(_null_); /* access permissions */
+
+#endif
+} FormData_pg_variable;
+
+typedef enum VariableEOXActionCodes
+{
+ VARIABLE_EOX_CODE_NOOP = 'n', /* NOOP */
+ VARIABLE_EOX_CODE_DROP = 'd', /* ON TRANSACTION END DROP */
+ VARIABLE_EOX_CODE_RESET = 'r', /* ON TRANSACTION END RESET */
+ VARIABLE_EOX_CODE_ROLLBACK_RESET = 'R' /* ON ROLLBACK RESET */
+} VariableEOXActionCodes;
+
+/* ----------------
+ * Form_pg_variable corresponds to a pointer to a tuple with
+ * the format of pg_variable relation.
+ * ----------------
+ */
+typedef FormData_pg_variable *Form_pg_variable;
+
+typedef struct Variable
+{
+ Oid oid;
+ char *name;
+ Oid namespace;
+ Oid typid;
+ int32 typmod;
+ Oid owner;
+ Oid collation;
+ VariableEOXAction eoxaction;
+ Node *defexpr;
+ Acl *acl;
+} Variable;
+
+/* returns fields from pg_variable table */
+extern char *get_schema_variable_name(Oid varid);
+extern void get_schema_variable_type_typmod_collid(Oid varid,
+ Oid *typid,
+ int32 *typmod,
+ Oid *collid);
+
+/* returns name of variable based on current search path */
+extern char *schema_variable_get_name(Oid varid);
+
+extern Variable *GetVariable(Oid varid, bool missing_ok);
+extern ObjectAddress VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Oid varCollation,
+ Node *varDefexpr,
+ VariableEOXAction eoxaction,
+ bool if_not_exists);
+
+
+#endif /* PG_VARIABLE_H */
diff --git a/src/include/commands/schemavariable.h b/src/include/commands/schemavariable.h
new file mode 100644
index 0000000000..b7e8221967
--- /dev/null
+++ b/src/include/commands/schemavariable.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavariable.h
+ * prototypes for schemavariable.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/schemavariable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SCHEMAVARIABLE_H
+#define SCHEMAVARIABLE_H
+
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable.h"
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "utils/queryenvironment.h"
+
+extern void ResetSchemaVariableCache(void);
+
+extern void RemoveVariableById(Oid varid);
+extern ObjectAddress DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt);
+
+extern Datum GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid, bool copy);
+extern void SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod);
+
+extern void doLetStmt(PlannedStmt *pstmt, ParamListInfo params, QueryEnvironment *queryEnv, const char *queryString);
+
+extern void register_variable_on_commit_action(Oid varid, VariableEOXAction action);
+extern void SchemaVariablePreCommit_on_commit_actions(void);
+extern void AtEOXact_SchemaVariables_on_commit_actions(bool isCommit);
+
+#endif
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77616..4fdceb6cee 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -344,13 +345,22 @@ typedef struct ExprEvalStep
TupleDesc argdesc;
} nulltest_row;
- /* for EEOP_PARAM_EXEC/EXTERN */
+ /* for EEOP_PARAM_EXEC/EXTERN/VARIABLE */
struct
{
- int paramid; /* numeric ID for parameter */
- Oid paramtype; /* OID of parameter's datatype */
+ int paramid; /* numeric ID for parameter */
+ Oid paramtype; /* OID of parameter's datatype */
} param;
+ /* for EEOP_PARAM_VARIABLE */
+ struct
+ {
+ int paramid; /* numeric ID for parameter */
+ Oid varoid; /* OID of assigned variable */
+ Oid paramtype; /* OID of parameter's datatype */
+ } vparam;
+
+
/* for EEOP_PARAM_CALLBACK */
struct
{
@@ -700,6 +710,8 @@ extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
extern void ExecEvalParamExecParams(Bitmapset *params, EState *estate);
extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern void ExecEvalParamVariable(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext);
extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op);
diff --git a/src/include/executor/svariableReceiver.h b/src/include/executor/svariableReceiver.h
new file mode 100644
index 0000000000..8c8117701f
--- /dev/null
+++ b/src/include/executor/svariableReceiver.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.h
+ * prototypes for svariableReceiver.c
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/svariableReceiver.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SVARIABLE_RECEIVER_H
+#define SVARIABLE_RECEIVER_H
+
+#include "tcop/dest.h"
+
+
+extern DestReceiver *CreateVariableDestReceiver(void);
+
+extern void SetVariableDestReceiverParams(DestReceiver *self, Oid varid);
+
+#endif /* SVARIABLE_RECEIVER_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index c830f141b1..efdb51cbfe 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -100,6 +100,8 @@ typedef struct ExprState
int steps_len; /* number of steps currently */
int steps_alloc; /* allocated length of steps array */
+ int nvariables; /* number of used variables */
+
struct PlanState *parent; /* parent PlanState node, if any */
ParamListInfo ext_params; /* for compiling PARAM_EXTERN nodes */
@@ -473,6 +475,7 @@ typedef struct ResultRelInfo
typedef struct EState
{
NodeTag type;
+ bool es_shared; /* plpgsql uses share estate */
/* Basic state for all query types: */
ScanDirection es_direction; /* current scan direction */
@@ -565,6 +568,14 @@ typedef struct EState
/* The per-query shared memory area to use for parallel execution. */
struct dsa_area *es_query_dsa;
+ int es_result_variable; /* Oid of target variable */
+
+ /* query schema variable cache */
+ int es_nvariables;
+ bool *es_varnulls;
+ Oid *es_vartypes;
+ Datum *es_varvalues;
+
/*
* JIT information. es_jit_flags indicates whether JIT should be performed
* and with which options. es_jit is created on-demand when JITing is
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 697d3d7a5f..dd7fd8ed42 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -348,6 +348,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSchemaVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
@@ -419,6 +420,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
+ T_LetStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -663,6 +665,7 @@ typedef enum CmdType
CMD_DELETE,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
+ CMD_PLAN_UTILITY, /* only let stmt now, requires planning */
CMD_NOTHING /* dummy command for instead nothing rules
* with qual */
} CmdType;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07ab1a3dde..28a818337d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -84,7 +84,9 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
-#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_READ (1<<12) /* for variables */
+#define ACL_WRITE (1<<13) /* for variables */
+#define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -121,6 +123,7 @@ typedef struct Query
int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */
+ int resultVariable; /* Oid of target variable or 0 */
bool hasAggs; /* has aggregates in tlist or havingQual */
bool hasWindowFuncs; /* has window functions in tlist */
@@ -1505,6 +1508,18 @@ typedef struct UpdateStmt
WithClause *withClause; /* WITH clause */
} UpdateStmt;
+/* ----------------------
+ * Let Statement
+ * ----------------------
+ */
+typedef struct LetStmt
+{
+ NodeTag type;
+ List *target; /* target variable */
+ Node *selectStmt; /* source expression */
+ int location;
+} LetStmt;
+
/* ----------------------
* Select Statement
*
@@ -1682,6 +1697,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -2497,6 +2513,21 @@ typedef struct AlterSeqStmt
bool missing_ok; /* skip error if a role is missing? */
} AlterSeqStmt;
+/* ----------------------
+ * {Create|Alter} VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSchemaVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the type of variable */
+ CollateClause *collClause;
+ Node *defexpr; /* default expression */
+ VariableEOXAction eoxaction; /* on commit action */
+ bool if_not_exists; /* do nothing if it already exists */
+} CreateSchemaVarStmt;
+
/* ----------------------
* Create {Aggregate|Operator|Type} Statement
* ----------------------
@@ -3238,7 +3269,8 @@ typedef enum DiscardMode
DISCARD_ALL,
DISCARD_PLANS,
DISCARD_SEQUENCES,
- DISCARD_TEMP
+ DISCARD_TEMP,
+ DISCARD_VARIABLES
} DiscardMode;
typedef struct DiscardStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd03a..2588f1455f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -43,7 +43,7 @@ typedef struct PlannedStmt
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete|utility */
+ CmdType commandType; /* select|let|insert|update|delete|utility */
uint64 queryId; /* query identifier (copied from Query) */
@@ -81,6 +81,9 @@ typedef struct PlannedStmt
*/
List *rootResultRelations;
+ /* Oid of target variable for LET command */
+ Oid resultVariable;
+
List *subplans; /* Plan trees for SubPlan expressions; note
* that some could be NULL */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..d3bdebac98 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -43,15 +43,26 @@ typedef struct Alias
List *colnames; /* optional list of column aliases */
} Alias;
-/* What to do at commit time for temporary relations */
+/*
+ * What to do at commit time for temporary relations or
+ * persistent/temporary variable.
+ */
typedef enum OnCommitAction
{
ONCOMMIT_NOOP, /* No ON COMMIT clause (do nothing) */
ONCOMMIT_PRESERVE_ROWS, /* ON COMMIT PRESERVE ROWS (do nothing) */
ONCOMMIT_DELETE_ROWS, /* ON COMMIT DELETE ROWS */
- ONCOMMIT_DROP /* ON COMMIT DROP */
+ ONCOMMIT_DROP, /* ON COMMIT DROP */
} OnCommitAction;
+typedef enum VariableEOXAction
+{
+ VARIABLE_EOX_NOOP, /* Do nothing */
+ VARIABLE_EOX_DROP, /* ON TRANSACTION END DROP */
+ VARIABLE_EOX_RESET, /* ON TRANSACTION END RESET */
+ VARIABLE_EOX_ROLLBACK_RESET /* ON ROLLBACK RESET */
+} VariableEOXAction;
+
/*
* RangeVar - range variable, used in FROM clauses
*
@@ -229,13 +240,17 @@ typedef struct Const
* of the `paramid' field contain the SubLink's subLinkId, and
* the low-order 16 bits contain the column number. (This type
* of Param is also converted to PARAM_EXEC during planning.)
+ *
+ * PARAM_VARIABLE: The parameter is a access to schema variable
+ * paramid holds varid.
*/
typedef enum ParamKind
{
PARAM_EXTERN,
PARAM_EXEC,
PARAM_SUBLINK,
- PARAM_MULTIEXPR
+ PARAM_MULTIEXPR,
+ PARAM_VARIABLE
} ParamKind;
typedef struct Param
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db40147b..d3ed3f4d0f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -231,6 +231,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("let", LET, UNRESERVED_KEYWORD)
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -434,6 +435,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 0230543810..f7c2e67f33 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -69,7 +69,9 @@ typedef enum ParseExprKind
EXPR_KIND_TRIGGER_WHEN, /* WHEN condition in CREATE TRIGGER */
EXPR_KIND_POLICY, /* USING or WITH CHECK expr in policy */
EXPR_KIND_PARTITION_EXPRESSION, /* PARTITION BY expression */
- EXPR_KIND_CALL_ARGUMENT /* procedure argument in CALL */
+ EXPR_KIND_CALL_ARGUMENT, /* procedure argument in CALL */
+ EXPR_KIND_VARIABLE_DEFAULT, /* default value for schema variable */
+ EXPR_KIND_LET /* LET assignment (should be same like UPDATE) */
} ParseExprKind;
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index ec6e0c102f..1ee199ed8f 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -32,6 +32,16 @@ extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
int attrno,
List *indirection,
int location);
+extern Node *transformAssignmentIndirection(ParseState *pstate,
+ Node *basenode,
+ const char *targetName,
+ bool targetIsArray,
+ Oid targetTypeId,
+ int32 targetTypMod,
+ Oid targetCollation,
+ ListCell *indirection,
+ Node *rhs,
+ int location);
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
char *colname, int attrno,
List *indirection,
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 82f0f2e741..c49b653555 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -96,7 +96,8 @@ typedef enum
DestCopyOut, /* results sent to COPY TO code */
DestSQLFunction, /* results sent to SQL-language func mgr */
DestTransientRel, /* results sent to transient relation */
- DestTupleQueue /* results sent to tuple queue */
+ DestTupleQueue, /* results sent to tuple queue */
+ DestVariable /* results sents to schema variable */
} CommandDest;
/* ----------------
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f4d4be8d0d..c624d8dd0b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -147,9 +147,11 @@ typedef ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
+#define ACL_READ_CHR 'S' /* 'R' is occupated by old RULE priv */
+#define ACL_WRITE_CHR 'W'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcSW"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -166,6 +168,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_READ|ACL_WRITE)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -253,6 +256,8 @@ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_variable_aclmask(Oid var_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode);
@@ -269,6 +274,7 @@ extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
+extern AclResult pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, ObjectType objtype,
const char *objectname);
@@ -305,6 +311,7 @@ extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool pg_publication_ownercheck(Oid pub_oid, Oid roleid);
extern bool pg_subscription_ownercheck(Oid sub_oid, Oid roleid);
extern bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid);
+extern bool pg_variable_ownercheck(Oid stat_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index e55ea4035b..cb3f4aaca9 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -122,6 +122,7 @@ extern bool get_func_leakproof(Oid funcid);
extern float4 get_func_cost(Oid funcid);
extern float4 get_func_rows(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
+extern Oid get_varname_varid(const char *varname, Oid varnamespace);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4f333586ee..453699be3c 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -107,9 +107,11 @@ enum SysCacheIdentifier
TYPENAMENSP,
TYPEOID,
USERMAPPINGOID,
- USERMAPPINGUSERSERVER
+ USERMAPPINGUSERSERVER,
+ VARIABLENAMENSP,
+ VARIABLEOID
-#define SysCacheSize (USERMAPPINGUSERSERVER + 1)
+#define SysCacheSize (VARIABLEOID + 1)
};
extern void InitCatalogCache(void);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 380d1de8f4..ac71dd7d7a 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8049,6 +8049,7 @@ plpgsql_create_econtext(PLpgSQL_execstate *estate)
{
oldcontext = MemoryContextSwitchTo(TopTransactionContext);
shared_simple_eval_estate = CreateExecutorState();
+ shared_simple_eval_estate->es_shared = true;
MemoryContextSwitchTo(oldcontext);
}
estate->simple_eval_estate = shared_simple_eval_estate;
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 7d3647a12d..7f183d4f1b 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -332,6 +332,7 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
/* Create a private EState for simple-expression execution */
simple_eval_estate = CreateExecutorState();
+ simple_eval_estate->es_shared = true;
/* And run the function */
PG_TRY();
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b500..48286f8e1a 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -105,5 +105,7 @@ ORDER BY 1, 2;
pg_index | indpred | pg_node_tree
pg_largeobject | data | bytea
pg_largeobject_metadata | lomacl | aclitem[]
-(11 rows)
+ pg_variable | varacl | aclitem[]
+ pg_variable | vardefexpr | pg_node_tree
+(13 rows)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 0aa5357917..848b041a4b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -163,6 +163,7 @@ pg_ts_parser|t
pg_ts_template|t
pg_type|t
pg_user_mapping|t
+pg_variable|t
point_tbl|t
polygon_tbl|t
quad_box_tbl|t
diff --git a/src/test/regress/expected/schema_variables.out b/src/test/regress/expected/schema_variables.out
new file mode 100644
index 0000000000..d38b0a686f
--- /dev/null
+++ b/src/test/regress/expected/schema_variables.out
@@ -0,0 +1,428 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+DROP VARIABLE var1, var2;
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT var1;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT READ ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT var1;
+ var1
+------
+
+(1 row)
+
+SET ROLE TO DEFAULT;
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to work
+LET var1 = 333;
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var1;
+ERROR: permission denied for schema variable var1
+-- should to work;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: ((g.v)::numeric = var1)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var1;
+SELECT * FROM schema_var_view;
+ var1
+------
+ 333
+(1 row)
+
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ var1
+------
+
+(1 row)
+
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+----------------------------
+ Result
+ Output: 3.14159265358979
+(2 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+SELECT inc(1);
+ inc
+-----
+ 1
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 2
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 3
+(1 row)
+
+SELECT inc(1) FROM generate_series(1,10);
+ inc
+-----
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+(10 rows)
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+-- composite variables
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+\d v1
+\d v2
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+SELECT v1;
+ v1
+------------
+ (1,2,3.14)
+(1 row)
+
+SELECT v2;
+ v2
+---------------
+ (10,20,31.40)
+(1 row)
+
+SELECT (v1).*;
+ x | y | z
+---+---+------
+ 1 | 2 | 3.14
+(1 row)
+
+SELECT (v2).*;
+ x | y | z
+----+----+-------
+ 10 | 20 | 31.40
+(1 row)
+
+SELECT v1.x + v1.z;
+ ?column?
+----------
+ 4.14
+(1 row)
+
+SELECT v2.x + v2.z;
+ ?column?
+----------
+ 41.40
+(1 row)
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+SELECT v2.x;
+ERROR: permission denied for schema variable v2
+SET ROLE TO DEFAULT;
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+DROP ROLE var_test_role;
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+ relname
+----------
+ pg_class
+(1 row)
+
+-- should to fail
+SELECT varx.xxx;
+ERROR: type text is not composite
+-- variables can be updated under RO transaction
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+SELECT varx;
+ varx
+-------
+ hello
+(1 row)
+
+DROP VARIABLE varx;
+CREATE TYPE t1 AS (a int, b numeric, c text);
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+ v1
+----------------------------
+ (1,3.14159265358979,hello)
+(1 row)
+
+LET v1.b = 10.2222;
+SELECT v1;
+ v1
+-------------------
+ (1,10.2222,hello)
+(1 row)
+
+-- should to fail
+LET v1.x = 10;
+ERROR: cannot assign to field "x" of column "x" because there is no such column in data type t1
+LINE 1: LET v1.x = 10;
+ ^
+DROP VARIABLE v1;
+DROP TYPE t1;
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+ va1
+------------
+ {10.1,2.1}
+(1 row)
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+ va2
+--------------------
+ (10.2,"{0.0,0.0}")
+(1 row)
+
+LET va2.b[1] = 10.3;
+SELECT va2;
+ va2
+---------------------
+ (10.2,"{10.3,0.0}")
+(1 row)
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+ v1
+------------------
+ 6.28318530717958
+(1 row)
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+ v2
+--------------------------
+ (3.14159265358979,Hello)
+(1 row)
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+ERROR: cannot drop type t2 because other objects depend on it
+DETAIL: schema variable v2 depends on type t2
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+ var1
+------
+ 1000
+(1 row)
+
+CREATE ROLE var_test_role;
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+DROP VARIABLE var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+SET ROLE TO DEFAULT;
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+SET ROLE TO var_test_role;
+SELECT public.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+SELECT public.var1_renamed;
+ var1_renamed
+--------------
+ 1000
+(1 row)
+
+DROP VARIABLE public.var1_renamed;
+SET ROLE TO DEFAULt;
+DROP ROLE var_test_role;
+CREATE VARIABLE xx AS text DEFAULT 'hello';
+SELECT xx, upper(xx);
+ xx | upper
+-------+-------
+ hello | HELLO
+(1 row)
+
+LET xx = 'Hi';
+SELECT xx;
+ xx
+----
+ Hi
+(1 row)
+
+DROP VARIABLE xx;
+-- using special behave that depends on transactions
+CREATE VARIABLE t1 AS int DEFAULT -1 ON TRANSACTION END RESET;
+BEGIN;
+ SELECT t1;
+ t1
+----
+ -1
+(1 row)
+
+ LET t1 = 100;
+ SELECT t1;
+ t1
+-----
+ 100
+(1 row)
+
+COMMIT;
+SELECT t1;
+ t1
+----
+ -1
+(1 row)
+
+DROP VARIABLE t1;
+CREATE VARIABLE t1 AS int DEFAULT -1 ON ROLLBACK RESET;
+BEGIN;
+ SELECT t1;
+ t1
+----
+ -1
+(1 row)
+
+ LET t1 = 100;
+ SELECT t1;
+ t1
+-----
+ 100
+(1 row)
+
+COMMIT;
+SELECT t1;
+ t1
+-----
+ 100
+(1 row)
+
+BEGIN;
+ LET t1 = 1000;
+ SELECT t1;
+ t1
+------
+ 1000
+(1 row)
+
+ROLLBACK;
+SELECT t1;
+ t1
+----
+ -1
+(1 row)
+
+DROP VARIABLE t1;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 16f979c8d9..9bf379b87b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml schema_variables
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 42632be675..42bf4ecb3f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -191,3 +191,4 @@ test: partition_aggregate
test: event_trigger
test: fast_default
test: stats
+test: schema_variables
diff --git a/src/test/regress/sql/schema_variables.sql b/src/test/regress/sql/schema_variables.sql
new file mode 100644
index 0000000000..4350ef893c
--- /dev/null
+++ b/src/test/regress/sql/schema_variables.sql
@@ -0,0 +1,290 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+
+DROP VARIABLE var1, var2;
+
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+
+CREATE ROLE var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT READ ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+-- should to work
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to work
+LET var1 = 333;
+
+SET ROLE TO DEFAULT;
+
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+
+SELECT secure_var();
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT public.var1;
+
+-- should to work;
+SELECT secure_var();
+
+SET ROLE TO DEFAULT;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+
+CREATE VIEW schema_var_view AS SELECT var1;
+
+SELECT * FROM schema_var_view;
+
+\c -
+
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+
+LET var1 = pi();
+
+SELECT var1;
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+
+EXECUTE var_pp(100, 1.23456);
+
+SELECT var1;
+
+CREATE VARIABLE var3 AS int;
+
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT inc(1);
+SELECT inc(1);
+SELECT inc(1);
+
+SELECT inc(1) FROM generate_series(1,10);
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+LET var3 = 0;
+
+SET ROLE TO DEFAULT;
+
+DROP VIEW schema_var_view;
+
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+
+-- composite variables
+
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+
+\d v1
+\d v2
+
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+
+SELECT v1;
+SELECT v2;
+SELECT (v1).*;
+SELECT (v2).*;
+
+SELECT v1.x + v1.z;
+SELECT v2.x + v2.z;
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+
+SELECT v2.x;
+
+SET ROLE TO DEFAULT;
+
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+DROP ROLE var_test_role;
+
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+
+-- should to fail
+SELECT varx.xxx;
+
+-- variables can be updated under RO transaction
+
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+
+SELECT varx;
+
+DROP VARIABLE varx;
+
+CREATE TYPE t1 AS (a int, b numeric, c text);
+
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+LET v1.b = 10.2222;
+SELECT v1;
+
+-- should to fail
+LET v1.x = 10;
+
+DROP VARIABLE v1;
+DROP TYPE t1;
+
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+LET va2.b[1] = 10.3;
+SELECT va2;
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+
+CREATE ROLE var_test_role;
+
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+DROP VARIABLE var_schema2.var1;
+
+SET ROLE TO DEFAULT;
+
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+
+SET ROLE TO var_test_role;
+SELECT public.var1;
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+
+SELECT public.var1_renamed;
+
+DROP VARIABLE public.var1_renamed;
+
+SET ROLE TO DEFAULt;
+
+DROP ROLE var_test_role;
+
+CREATE VARIABLE xx AS text DEFAULT 'hello';
+
+SELECT xx, upper(xx);
+
+LET xx = 'Hi';
+
+SELECT xx;
+
+DROP VARIABLE xx;
+
+-- using special behave that depends on transactions
+
+CREATE VARIABLE t1 AS int DEFAULT -1 ON TRANSACTION END RESET;
+
+BEGIN;
+ SELECT t1;
+ LET t1 = 100;
+ SELECT t1;
+COMMIT;
+
+SELECT t1;
+
+DROP VARIABLE t1;
+
+CREATE VARIABLE t1 AS int DEFAULT -1 ON ROLLBACK RESET;
+
+BEGIN;
+ SELECT t1;
+ LET t1 = 100;
+ SELECT t1;
+COMMIT;
+
+SELECT t1;
+
+BEGIN;
+ LET t1 = 1000;
+ SELECT t1;
+ROLLBACK;
+
+SELECT t1;
+
+DROP VARIABLE t1;
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-04 13:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-06 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-07 12:34 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-09-07 14:28 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Fabien COELHO @ 2018-09-07 12:34 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hello Pavel,
> here is updated patch - I wrote some transactional support
>
> I am not sure how these new features are understandable and if these
> features does it better or not.
> There are possibility to reset to default value when
>
> a) any transaction is finished - the scope of value is limited by
> transaction
>
> CREATE VARIABLE foo int ON TRANSACTION END RESET;
With this option I understand that it is a "within a transactionnal"
variable, i.e. when the transaction ends, whether commit or rollback, the
variable is reset to a default variable. It is not really a "session"
variable anymore, each transaction has its own value.
-- begin session
-- foo has default value, eg NULL
BEGIN;
LET foo = 1;
COMMIT/ROLLBACK;
-- foo has default value again, NULL
> b) when transaction finished by rollback
>
> CREATE VARIABLE foo int ON ROLLBACK RESET
That is a little bit safer and you are back to a SESSION-scope variable,
which is reset to the default value if the (any) transaction fails?
-- begin session
-- foo has default value, eg NULL
BEGIN;
LET foo = 1;
COMMIT;
-- foo has value 1
BEGIN;
-- foo has value 1...
ROLLBACK;
-- foo has value NULL
c) A more logical (from a transactional point of view - but not necessary
simple to implement, I do not know) feature/variant would be to reset the
value to the one it had at the beginning of the transaction, which is not
necessarily the default.
-- begin session
-- foo has default value, eg NULL
BEGIN;
LET foo = 1;
COMMIT;
-- foo has value 1
BEGIN;
LET foo = 2; (*)
-- foo has value 2
ROLLBACK;
-- foo has value 1 back, change (*) has been reverted
> Now, when I am thinking about it, the @b is simple, but not too practical -
> when some fails, then we lost a value (any transaction inside session can
> fails).
Indeed.
> The @a has sense - the behave is global value (what is not possible
> in Postgres now), but this value is destroyed by any unhandled exceptions,
> and it cleaned on transaction end. The @b is just for information and for
> discussion, but I'll remove it - because it is obscure.
Indeed.
> The open question is syntax. PostgreSQL has already ON COMMIT xxx . It is
> little bit unclean, because it has semantic "on transaction end", but if I
> didn't implement @b, then ON COMMIT syntax can be used.
I was more arguing on the third (c) option, i.e. on rollback the value is
reverted to its value at the beginning of the rollbacked transaction.
At the minimum, ISTM that option (b) is enough to implement the audit
pattern, but it would mean that any session which has a rollback, for any
reason (deadlock, serialization...), would have to be reinitialized, which
would be a drawback.
The to options could be non-transactional session variables "ON ROLLBACK
DO NOT RESET/DO NOTHING", and somehow transactional session variables "ON
ROLLBACK RESET TO DEFAULT" (b) or "ON ROLLBACK RESET TO INITIAL" (c).
--
Fabien.
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-04 13:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-06 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-07 12:34 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
@ 2018-09-07 14:28 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-09-07 14:28 UTC (permalink / raw)
To: Fabien COELHO <coelho@cri.ensmp.fr>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
2018-09-07 14:34 GMT+02:00 Fabien COELHO <coelho@cri.ensmp.fr>:
>
> Hello Pavel,
>
> here is updated patch - I wrote some transactional support
>>
>> I am not sure how these new features are understandable and if these
>> features does it better or not.
>>
>
> There are possibility to reset to default value when
>>
>> a) any transaction is finished - the scope of value is limited by
>> transaction
>>
>> CREATE VARIABLE foo int ON TRANSACTION END RESET;
>>
>
> With this option I understand that it is a "within a transactionnal"
> variable, i.e. when the transaction ends, whether commit or rollback, the
> variable is reset to a default variable. It is not really a "session"
> variable anymore, each transaction has its own value.
>
yes, the correct name should be "schema variable with transaction scope". I
think it can be useful like short life global variable. These variables can
works like transaction caches.
> -- begin session
> -- foo has default value, eg NULL
> BEGIN;
> LET foo = 1;
> COMMIT/ROLLBACK;
> -- foo has default value again, NULL
>
> b) when transaction finished by rollback
>>
>> CREATE VARIABLE foo int ON ROLLBACK RESET
>>
>
> That is a little bit safer and you are back to a SESSION-scope variable,
> which is reset to the default value if the (any) transaction fails?
>
> -- begin session
> -- foo has default value, eg NULL
> BEGIN;
> LET foo = 1;
> COMMIT;
> -- foo has value 1
> BEGIN;
> -- foo has value 1...
> ROLLBACK;
> -- foo has value NULL
>
> c) A more logical (from a transactional point of view - but not necessary
> simple to implement, I do not know) feature/variant would be to reset the
> value to the one it had at the beginning of the transaction, which is not
> necessarily the default.
>
> -- begin session
> -- foo has default value, eg NULL
> BEGIN;
> LET foo = 1;
> COMMIT;
> -- foo has value 1
> BEGIN;
> LET foo = 2; (*)
> -- foo has value 2
> ROLLBACK;
> -- foo has value 1 back, change (*) has been reverted
>
> Now, when I am thinking about it, the @b is simple, but not too practical -
>> when some fails, then we lost a value (any transaction inside session can
>> fails).
>>
>
> Indeed.
>
> The @a has sense - the behave is global value (what is not possible
>> in Postgres now), but this value is destroyed by any unhandled exceptions,
>> and it cleaned on transaction end. The @b is just for information and for
>> discussion, but I'll remove it - because it is obscure.
>>
>
> Indeed.
>
> The open question is syntax. PostgreSQL has already ON COMMIT xxx . It is
>> little bit unclean, because it has semantic "on transaction end", but if I
>> didn't implement @b, then ON COMMIT syntax can be used.
>>
>
> I was more arguing on the third (c) option, i.e. on rollback the value is
> reverted to its value at the beginning of the rollbacked transaction.
>
> At the minimum, ISTM that option (b) is enough to implement the audit
> pattern, but it would mean that any session which has a rollback, for any
> reason (deadlock, serialization...), would have to be reinitialized, which
> would be a drawback.
>
> The to options could be non-transactional session variables "ON ROLLBACK
> DO NOT RESET/DO NOTHING", and somehow transactional session variables "ON
> ROLLBACK RESET TO DEFAULT" (b) or "ON ROLLBACK RESET TO INITIAL" (c).
>
@b is hardly understandable for not trained people, because any rollback in
session does reset. But people expecting @c, or some near @c.
I understand so you talked about @c. Now I think so it is possible to
implement, but it is not trivial. The transactional behave have to
calculate not only with transactions, but with SAVEPOINTS and ROLLBACK TO
savepoints. On second hand, the implementation will be relatively compact.
I'll hold it in my memory, but there are harder issues (support for
parallelism).
Regards
Pavel
> --
> Fabien.
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
@ 2018-09-14 21:31 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-14 21:31 UTC (permalink / raw)
To: Dean Rasheed <dean.a.rasheed@gmail.com>; +Cc: Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
út 4. 9. 2018 v 9:21 odesílatel Dean Rasheed <dean.a.rasheed@gmail.com>
napsal:
> AFAICS this patch does nothing to consider parallel safety -- that is,
> as things stand, a variable is allowed in a query that may be
> parallelised, but its value is not copied to workers, leading to
> incorrect results. For example:
>
> create table foo(a int);
> insert into foo select * from generate_series(1,1000000);
> create variable zero int;
> let zero = 0;
>
> explain (costs off) select count(*) from foo where a%10 = zero;
>
> QUERY PLAN
> -----------------------------------------------
> Finalize Aggregate
> -> Gather
> Workers Planned: 2
> -> Partial Aggregate
> -> Parallel Seq Scan on foo
> Filter: ((a % 10) = zero)
> (6 rows)
>
> select count(*) from foo where a%10 = zero;
>
> count
> -------
> 38037 -- Different random result each time, should be 100,000
> (1 row)
>
> Thoughts?
>
This issue should be fixed in attached patch (and more others).
The code is more cleaner now, there are more tests, and documentation is
mostly complete. I am sorry - my English is not good.
New features:
o ON COMMIT DROP and ON TRANSACTION END RESET -- remove temp variable on
commit, reset variable on transaction end (commit, rollback)
o LET var = DEFAULT -- reset specified variable
Regards
Pavel
> Regards,
> Dean
>
Attachments:
[text/x-patch] schema-variables-20180914-01.patch (235.6K, ../../CAFj8pRDREykg0hnMBkryBmSTtqEs9QUKUp_447_F5P1j8bkH1w@mail.gmail.com/3-schema-variables-20180914-01.patch)
download | inline diff:
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 0179deea2e..13043987ff 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -359,6 +359,11 @@
<entry><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link></entry>
<entry>mappings of users to foreign servers</entry>
</row>
+
+ <row>
+ <entry><link linkend="catalog-pg-variable"><structname>pg_variable</structname></link></entry>
+ <entry>schema variables</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -11303,4 +11308,124 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
+ <sect1 id="catalog-pg-variable">
+ <title><structname>pg_variable</structname></title>
+
+ <indexterm zone="catalog-pg-variable">
+ <primary>pg_variable</primary>
+ </indexterm>
+
+ <para>
+ The table <structname>pg_variable</structname> holds metadata
+ of schema variables.
+ </para>
+
+ <table>
+ <title><structname>pg_views</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><structfield>oid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry></entry>
+ <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Name of the schema variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the namespace that contains this variable
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartype</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the data type of this variable.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vartypmod</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ <structfield>vartypmod</structfield> records type-specific data
+ supplied at table creation time (for example, the maximum
+ length of a <type>varchar</type> column). It is passed to
+ type-specific input functions and length coercion functions.
+ The value will generally be -1 for types that do not need <structfield>vartypmod</structfield>.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>varowner</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>Owner of the variable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varcollation</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+ <entry>
+ The defined collation of the variable, or zero if the variable is
+ not of a collatable data type.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vareoxaction</structfield></entry>
+ <entry><type>char</type></entry>
+ <entry></entry>
+ <entry>
+ <literal>n</literal> = no action, <literal>d</literal> = drop variable,
+ <literal>r</literal> = reset variable
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>vardefexpr</structfield></entry>
+ <entry><type>pg_node_tree</type></entry>
+ <entry></entry>
+ <entry>The internal representation of the variable default value</entry>
+ </row>
+
+ <row>
+ <entry><structfield>varacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>
+ Access privileges; see
+ <xref linkend="sql-grant"/> and
+ <xref linkend="sql-revoke"/>
+ for details
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..0631c9ed56 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -47,6 +47,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterType SYSTEM "alter_type.sgml">
<!ENTITY alterUser SYSTEM "alter_user.sgml">
<!ENTITY alterUserMapping SYSTEM "alter_user_mapping.sgml">
+<!ENTITY alterVariable SYSTEM "alter_variable.sgml">
<!ENTITY alterView SYSTEM "alter_view.sgml">
<!ENTITY analyze SYSTEM "analyze.sgml">
<!ENTITY begin SYSTEM "begin.sgml">
@@ -99,6 +100,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createType SYSTEM "create_type.sgml">
<!ENTITY createUser SYSTEM "create_user.sgml">
<!ENTITY createUserMapping SYSTEM "create_user_mapping.sgml">
+<!ENTITY createVariable SYSTEM "create_variable.sgml">
<!ENTITY createView SYSTEM "create_view.sgml">
<!ENTITY deallocate SYSTEM "deallocate.sgml">
<!ENTITY declare SYSTEM "declare.sgml">
@@ -148,6 +150,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropUser SYSTEM "drop_user.sgml">
<!ENTITY dropUserMapping SYSTEM "drop_user_mapping.sgml">
<!ENTITY dropView SYSTEM "drop_view.sgml">
+<!ENTITY dropVariable SYSTEM "drop_variable.sgml">
<!ENTITY end SYSTEM "end.sgml">
<!ENTITY execute SYSTEM "execute.sgml">
<!ENTITY explain SYSTEM "explain.sgml">
@@ -155,6 +158,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY grant SYSTEM "grant.sgml">
<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
<!ENTITY insert SYSTEM "insert.sgml">
+<!ENTITY let SYSTEM "let.sgml">
<!ENTITY listen SYSTEM "listen.sgml">
<!ENTITY load SYSTEM "load.sgml">
<!ENTITY lock SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/alter_variable.sgml b/doc/src/sgml/ref/alter_variable.sgml
new file mode 100644
index 0000000000..6376ac716b
--- /dev/null
+++ b/doc/src/sgml/ref/alter_variable.sgml
@@ -0,0 +1,170 @@
+<!--
+doc/src/sgml/ref/alter_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-altervariable">
+ <indexterm zone="sql-altervariable">
+ <primary>ALTER VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>ALTER VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ALTER VARIABLE</refname>
+ <refpurpose>
+ change the definition of a variable
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="parameter">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
+ALTER VARIABLE <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>ALTER VARIABLE</command> changes the definition of an existing variable.
+ There are several subforms:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>OWNER</literal></term>
+ <listitem>
+ <para>
+ This form changes the owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RENAME</literal></term>
+ <listitem>
+ <para>
+ This form changes the name of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET SCHEMA</literal></term>
+ <listitem>
+ <para>
+ This form moves the variable into another schema.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </para>
+
+ <para>
+ You must own the variable to use <command>ALTER VARIABLE</command>.
+ To change the schema of a variable, you must also have
+ <literal>CREATE</literal> privilege on the new schema.
+ To alter the owner, you must also be a direct or indirect member of the new
+ owning role, and that role must have <literal>CREATE</literal> privilege on
+ the variable's schema. (These restrictions enforce that altering the owner
+ doesn't do anything you couldn't do by dropping and recreating the variable.
+ However, a superuser can alter ownership of any type anyway.)
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (possibly schema-qualified) of an existing variable to
+ alter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_name</replaceable></term>
+ <listitem>
+ <para>
+ The new name for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_owner</replaceable></term>
+ <listitem>
+ <para>
+ The user name of the new owner of the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_schema</replaceable></term>
+ <listitem>
+ <para>
+ The new schema for the variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To rename a variable:
+<programlisting>
+ALTER VARIABLE foo RENAME TO boo;
+</programlisting>
+ </para>
+
+ <para>
+ To change the owner of the variable <literal>boo</literal>
+ to <literal>joe</literal>:
+<programlisting>
+ALTER VARIABLE boo OWNER TO joe;
+</programlisting>
+ </para>
+
+ <para>
+ To change the schema of the variable <literal>boo</literal>
+ to <literal>private</literal>:
+<programlisting>
+ALTER VARIABLE boo SET SCHEMA private;
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ This comman is a PostgreSQL extension.
+ </para>
+ </refsect1>
+
+ <refsect1 id="sql-altervariable-see-also">
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml
new file mode 100644
index 0000000000..bcb6824711
--- /dev/null
+++ b/doc/src/sgml/ref/create_variable.sgml
@@ -0,0 +1,170 @@
+<!--
+doc/src/sgml/ref/create_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-createvariable">
+ <indexterm zone="sql-createvariable">
+ <primary>CREATE VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE VARIABLE</refname>
+ <refpurpose>define a new permissioned typed schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE { TEMPORARY | TEMP } VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ] [ COLLATE <replaceable class="parameter">collation</replaceable> ]
+ [ DEFAULT <replaceable class="parameter">default_expr</replaceable> ] [ { ON COMMIT DROP | ON TRANSACTION END RESET } ]
+</synopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> creates a new schema variable.
+ These variables are scalar typed, non-transactional, and, like relations,
+ exist within a schema with access controlled via
+ <command>GRANT</command> and <command>REVOKE</command>.
+ </para>
+
+ <para>
+ The value of a schema variable is session-local. Retrieving
+ a variable's value will return NULL unless its value has been set
+ to something else in the current session.
+ </para>
+
+ <para>
+ Retrieval is done via the <function>get_schema_variable</function>dunxrion or the SQL
+ command <command>SELECT</command>. Setting of values is done via the
+ <function>set_schema_variable</function> function or the SQL command
+ <command>LET</command>.
+ Notably, while schema variables are in many ways a kind of table you cannot use
+ <command>UPDATE</command> on them.
+ </para>
+
+ <para>
+ For purposes of name uniqueness relation-like objects (e.g., tables, indexes)
+ within the same schema are considered. i.e., you cannot give a table and a
+ schema variable the same name. This is a consequence of them being treated
+ like relations for purposes of <command>SELECT</command>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF NOT EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the name already exists. A notice is issued in this case.
+ Note that type of the variable is not considered, nor could it be since the namespace
+ searched contains non-variable objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the data type of the variable to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>COLLATE <replaceable>collation</replaceable></literal></term>
+ <listitem>
+ <para>
+ The <literal>COLLATE</literal> clause assigns a collation to
+ the variable (which must be of a collatable data type).
+ If not specified, the variable data type's default collation is used.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>DEFAULT <replaceable>default_expr</replaceable></literal></term>
+ <listitem>
+ <para>
+ The <literal>DEFAULT</literal> clause assigns a default data for
+ schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON COMMIT DROP</literal>, <literal>ON TRANSACTION END RESET</literal></term>
+ <listitem>
+ <para>
+ The <literal>ON COMMIT DROP</literal> clause specify the bahaviour of
+ temporary schema variable at commit of transaction. It is allowed only
+ for temporal variables, and enforce drop variable at commit time. The
+ <literal>ON TRANSACTION END RESET</literal> enforce reset to default
+ value at transaction end (<literal>COMMIT</literal>, <literal>ROLLBACK</literal>).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Use <command>DROP VARIABLE</command> to remove a variable.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Create an integer variable <literal>var1</literal>:
+<programlisting>
+CREATE VARIABLE var1 AS integer;
+SELECT var1;
+</programlisting>
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE VARIABLE</command> is a PostgreSQL feature.
+ <!-- The choice of wording here seems to be left to personal preference... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/discard.sgml b/doc/src/sgml/ref/discard.sgml
index 6b909b7232..d83ad811fd 100644
--- a/doc/src/sgml/ref/discard.sgml
+++ b/doc/src/sgml/ref/discard.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
+DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP | VARIABLES }
</synopsis>
</refsynopsisdiv>
@@ -75,6 +75,17 @@ DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>VARIABLES</literal></term>
+ <listitem>
+ <para>
+ Resets the value of all schema variables. When variables
+ will be used later, then will be initialized again to
+ NULL or default value.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml
new file mode 100644
index 0000000000..c1c1a2bd67
--- /dev/null
+++ b/doc/src/sgml/ref/drop_variable.sgml
@@ -0,0 +1,93 @@
+<!--
+doc/src/sgml/ref/drop_variable.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropvariable">
+ <indexterm zone="sql-dropvariable">
+ <primary>DROP VARIABLE</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP VARIABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP VARIABLE</refname>
+ <refpurpose>remove a schema variable</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP VARIABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP VARIABLE</command> removes a schema variable.
+ A variable can only be dropped by its owner or a superuser.
+ <!-- this would suggest that we need an alter variable owner to command -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the variable does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To remove the schema variable <literal>var1</literal>:
+
+<programlisting>
+DROP VARIABLE var1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP VARIABLE</command> is proprietary PostgreSQL command.
+ <!-- create variable is a "PostgreSQL feature",
+ this is a "proprietary PostgreSQL command" ... -->
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altervariable"/></member>
+ <member><xref linkend="sql-createvariable"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index ff64c7a3ba..a83920a7a1 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -79,6 +79,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE <replaceable>type_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
[ GROUP ] <replaceable class="parameter">role_name</replaceable>
@@ -167,6 +171,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
foreign servers,
large objects,
schemas,
+ schema variable
or tablespaces.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
@@ -385,6 +390,24 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>READ</literal></term>
+ <listitem>
+ <para>
+ Allows to read a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WRITE</literal></term>
+ <listitem>
+ <para>
+ Allows to set a schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ALL PRIVILEGES</literal></term>
<listitem>
@@ -550,6 +573,8 @@ rolename=xxxx -- privileges granted to a role
C -- CREATE
c -- CONNECT
T -- TEMPORARY
+ S -- READ
+ w -- WRITE
arwdDxt -- ALL PRIVILEGES (for tables, varies for other objects)
* -- grant option for preceding privilege
diff --git a/doc/src/sgml/ref/let.sgml b/doc/src/sgml/ref/let.sgml
new file mode 100644
index 0000000000..299cfdf413
--- /dev/null
+++ b/doc/src/sgml/ref/let.sgml
@@ -0,0 +1,104 @@
+<!--
+doc/src/sgml/ref/let.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-let">
+ <indexterm zone="sql-let">
+ <primary>LET</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>LET</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>LET</refname>
+ <refpurpose>change a schema variable's value</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+LET <replaceable class="parameter">schema_variable</replaceable> = <replaceable class="parameter">sql_expression</replaceable>
+LET <replaceable class="parameter">schema_variable</replaceable> = DEFAULT
+
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ The <command>LET</command> command updates the specified schema variable' value.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>schema_variable</literal></term>
+ <listitem>
+ <para>
+ The name of schema variable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sql expression</literal></term>
+ <listitem>
+ <para>
+ An SQL expression, the result is cast to the schema variable's type.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>DEFAULT</literal></term>
+ <listitem>
+ <para>
+ Ensure reset schema variable to default value if it is defined.
+ When there are not assigned default value, then value of schema
+ variable will be null.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Example:
+<programlisting>
+CREATE VARIABLE myvar AS integer;
+LET myvar = 10;
+LET myvar = (SELECT sum(val) FROM tab);
+LET myvar = DEFAULT;
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <!-- this feels like it needs to be more specific,
+ but I don't know enough to make it so -->
+ <literal>LET</literal> extends syntax defined in the SQL
+ standard. The standard knows <literal>SET</literal> command,
+ that is used for different purpouse in PostgreSQL.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createvariable"/></member>
+ <member><xref linkend="sql-dropvariable"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 5317f8ccba..8435e05957 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -108,6 +108,12 @@ REVOKE [ GRANT OPTION FOR ]
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_name</replaceable> [, ...]
[ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { READ | WRITE } [, ...] | ALL [ PRIVILEGES ] }
+ ON VARIABLE <replaceable>variable_name</replaceable> [, ...]
+ FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..5fb82df51e 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -75,6 +75,7 @@
&alterType;
&alterUser;
&alterUserMapping;
+ &alterVariable;
&alterView;
&analyze;
&begin;
@@ -127,6 +128,7 @@
&createType;
&createUser;
&createUserMapping;
+ &createVariable;
&createView;
&deallocate;
&declare;
@@ -175,6 +177,7 @@
&dropType;
&dropUser;
&dropUserMapping;
+ &dropVariable;
&dropView;
&end;
&execute;
@@ -183,6 +186,7 @@
&grant;
&importForeignSchema;
&insert;
+ &let;
&listen;
&load;
&lock;
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 875be180fe..adc91a4238 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -33,6 +33,7 @@
#include "catalog/namespace.h"
#include "catalog/storage.h"
#include "commands/async.h"
+#include "commands/schemavariable.h"
#include "commands/tablecmds.h"
#include "commands/trigger.h"
#include "executor/spi.h"
@@ -1978,6 +1979,9 @@ CommitTransaction(void)
*/
PreCommit_on_commit_actions();
+ /* Let ON COMMIT DROP or ON TRANSACTION END */
+ AtPreEOXact_SchemaVariable_on_commit_actions(true);
+
/* close large objects before lower-level cleanup */
AtEOXact_LargeObject(true);
@@ -2102,6 +2106,7 @@ CommitTransaction(void)
AtEOXact_GUC(true, 1);
AtEOXact_SPI(true);
AtEOXact_on_commit_actions(true);
+ AtEOXact_SchemaVariable_on_commit_actions(true);
AtEOXact_Namespace(true, is_parallel_worker);
AtEOXact_SMgr();
AtEOXact_Files(true);
@@ -2519,6 +2524,9 @@ AbortTransaction(void)
AfterTriggerEndXact(false); /* 'false' means it's abort */
AtAbort_Portals();
AtEOXact_LargeObject(false);
+
+ /* 'false' means it's abort */
+ AtPreEOXact_SchemaVariable_on_commit_actions(false);
AtAbort_Notify();
AtEOXact_RelationMap(false, is_parallel_worker);
AtAbort_Twophase();
@@ -2582,6 +2590,7 @@ AbortTransaction(void)
AtEOXact_GUC(false, 1);
AtEOXact_SPI(false);
AtEOXact_on_commit_actions(false);
+ AtEOXact_SchemaVariable_on_commit_actions(false);
AtEOXact_Namespace(false, is_parallel_worker);
AtEOXact_SMgr();
AtEOXact_Files(false);
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 0865240f11..1f7c4d1223 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -19,7 +19,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_publication.o pg_range.o \
pg_db_role_setting.o pg_shdepend.o pg_subscription.o pg_type.o \
- storage.o toasting.o
+ pg_variable.o storage.o toasting.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
@@ -46,7 +46,7 @@ CATALOG_HEADERS := \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
- pg_subscription_rel.h
+ pg_subscription_rel.h pg_variable.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 578e4c6592..86917e15a8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -57,6 +57,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_transform.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
@@ -112,6 +113,7 @@ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
+static void ExecGrant_Variable(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
@@ -284,6 +286,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -507,6 +512,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -609,6 +618,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_VARIABLE:
+ ExecGrant_Variable(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -768,6 +780,16 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_VARIABLE:
+ foreach(cell, objnames)
+ {
+ RangeVar *varvar = (RangeVar *) lfirst(cell);
+ Oid relOid;
+
+ relOid = lookup_variable(varvar->schemaname, varvar->relname, false);
+ objects = lappend_oid(objects, relOid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -855,6 +877,31 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
heap_close(rel, AccessShareLock);
}
break;
+ case OBJECT_VARIABLE:
+ {
+ ScanKeyData key;
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&key,
+ Anum_pg_variable_varnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(namespaceId));
+
+ rel = heap_open(VariableRelationId, AccessShareLock);
+ scan = heap_beginscan_catalog(rel, 1, &key);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ }
+ break;
+
default:
/* should not happen */
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
@@ -1018,6 +1065,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for schema variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1215,6 +1266,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1441,6 +1498,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -3266,6 +3326,129 @@ ExecGrant_Type(InternalGrant *istmt)
heap_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Variable(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_VARIABLE;
+
+ relation = heap_open(VariableRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid varId = lfirst_oid(cell);
+ Form_pg_variable pg_variable_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_variable];
+ bool nulls[Natts_pg_variable];
+ bool replaces[Natts_pg_variable];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for schema variables %u", varId);
+
+ pg_variable_tuple = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_variable_tuple->varowner;
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple, Anum_pg_variable_varacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_VARIABLE, ownerId);
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ oldmembers = NULL;
+ }
+ else
+ {
+ old_acl = DatumGetAclPCopy(aclDatum);
+ /* Get the roles mentioned in the existing ACL */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+ }
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ varId, grantorId, OBJECT_VARIABLE,
+ NameStr(pg_variable_tuple->varname),
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ */
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_variable_varacl - 1] = true;
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(varId, VariableRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(VariableRelationId, varId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3298,6 +3481,10 @@ string_to_privilege(const char *privname)
return ACL_CONNECT;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
+ if (strcmp(privname, "read") == 0)
+ return ACL_READ;
+ if (strcmp(privname, "write") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized privilege type \"%s\"", privname)));
@@ -3333,6 +3520,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3456,6 +3647,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3566,6 +3760,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of schema variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3710,6 +3907,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
+ case OBJECT_VARIABLE:
+ return pg_variable_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objtype: %d",
(int) objtype);
@@ -4499,6 +4698,67 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a variable.
+ */
+AclMode
+pg_variable_aclmask(Oid var_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_variable varForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the type's tuple from pg_type
+ */
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable with OID %u does not exist",
+ var_oid)));
+ varForm = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Now get the type's owner and ACL from the tuple
+ */
+ ownerId = varForm->varowner;
+
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple,
+ Anum_pg_variable_varacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_VARIABLE, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+
/*
* Exported routine for checking a user's access privileges to a column
*
@@ -4744,6 +5004,18 @@ pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a variable
+ */
+AclResult
+pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
+{
+ if (pg_variable_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Ownership check for a relation (specified by OID).
*/
@@ -5361,6 +5633,33 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a schema variables (specified by OID).
+ */
+bool
+pg_variable_ownercheck(Oid db_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(db_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("variable with OID %u does not exist", db_oid)));
+
+ ownerId = ((Form_pg_variable) GETSTRUCT(tuple))->varowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+
/*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
@@ -5486,6 +5785,10 @@ get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
+ case OBJECT_VARIABLE:
+ defaclobjtype = DEFACLOBJ_VARIABLE;
+ break;
+
default:
return NULL;
}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 4f1d365357..782ddb1655 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -59,6 +59,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -67,6 +68,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
@@ -1280,6 +1282,10 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_VARIABLE:
+ RemoveVariableById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2537,6 +2543,9 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case VariableRelationId:
+ return OCLASS_VARIABLE;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 5d13e6a3d7..e47caa356d 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -39,6 +39,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
@@ -755,6 +756,69 @@ RelationIsVisible(Oid relid)
return visible;
}
+/*
+ * VariableIsVisible
+ * Determine whether a variable (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified variable name".
+ */
+bool
+VariableIsVisible(Oid varid)
+{
+ HeapTuple vartup;
+ Form_pg_variable varform;
+ Oid varnamespace;
+ bool visible;
+
+ vartup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(vartup))
+ elog(ERROR, "cache lookup failed for schema variable %u", varid);
+ varform = (Form_pg_variable) GETSTRUCT(vartup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ varnamespace = varform->varnamespace;
+ if (varnamespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, varnamespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another relation of the same name earlier in the path. So
+ * we must do a slow check for conflicting relations.
+ */
+ char *varname = NameStr(varform->varname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == varnamespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (OidIsValid(get_varname_varid(varname, namespaceId)))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(vartup);
+
+ return visible;
+}
/*
* TypenameGetTypid
@@ -2776,6 +2840,202 @@ TSConfigIsVisible(Oid cfgid)
return visible;
}
+/*
+ * When we know a variable name, then we can find variable simply
+ */
+Oid
+lookup_variable(const char *nspname, const char *varname, bool missing_ok)
+{
+ Oid namespaceId;
+ Oid varoid = InvalidOid;
+ ListCell *l;
+
+ if (nspname)
+ {
+ namespaceId = LookupExplicitNamespace(nspname, missing_ok);
+ if (!OidIsValid(namespaceId))
+ return InvalidOid;
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+
+ if (OidIsValid(varoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(varoid) && !missing_ok)
+ {
+ if (nspname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\".\"%s\" does not exist",
+ nspname, varname)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable \"%s\" does not exist",
+ varname)));
+ }
+
+ return varoid;
+}
+
+List *
+NamesFromList(List *names)
+{
+ ListCell *l;
+ List *result = NIL;
+
+ foreach(l, names)
+ {
+ Node *n = lfirst(l);
+
+ if (IsA(n, String))
+ {
+ result = lappend(result, n);
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * identify_variable
+ *
+ * Returns oid of not ambigonuous variable specified by qualified path
+ * or InvalidOid. When the path is ambigonuous, then not_uniq flag is
+ * is true.
+ */
+Oid
+identify_variable(List *names, char **attrname, bool *not_uniq)
+{
+ char *a = NULL;
+ char *b = NULL;
+ char *c = NULL;
+ char *d = NULL;
+ Oid varoid_without_attr;
+ Oid varoid_with_attr;
+
+ *not_uniq = false;
+
+ switch (list_length(names))
+ {
+ case 1:
+ a = strVal(linitial(names));
+ return lookup_variable(NULL, a, true);
+
+ case 2:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+
+ /*
+ * a.b can mean "schema"."variable" or "variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(a, b, true);
+ varoid_with_attr = lookup_variable(NULL, a, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = b;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 3:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+
+ /*
+ * a.b.c can mean "catalog"."schema"."variable" or "schema"."variable"."field",
+ * Check both variants, and returns InvalidOid with not_uniq
+ * flag, when both interpretations are possible.
+ */
+ varoid_without_attr = lookup_variable(b, c, true);
+ varoid_with_attr = lookup_variable(a, b, true);
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_uniq = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ return varoid_without_attr;
+ }
+ else
+ {
+ *attrname = c;
+ return varoid_with_attr;
+ }
+ break;
+
+ case 4:
+ a = strVal(linitial(names));
+ b = strVal(lsecond(names));
+ c = strVal(lthird(names));
+ d = strVal(lfourth(names));
+
+ /*
+ * We in this case a "a" is used as catalog name, check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ *attrname = d;
+ return lookup_variable(b, c, true);
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(names))));
+ break;
+ }
+}
/*
* DeconstructQualifiedName
@@ -4490,3 +4750,14 @@ pg_is_other_temp_schema(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(isOtherTempNamespace(oid));
}
+
+Datum
+pg_variable_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ if (!SearchSysCacheExists1(VARIABLEOID, ObjectIdGetDatum(oid)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_BOOL(VariableIsVisible(oid));
+}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7db942dcba..cc3d415e61 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -58,6 +58,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -489,6 +490,18 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
OBJECT_STATISTIC_EXT,
true
+ },
+ {
+ VariableRelationId,
+ VariableObjectIndexId,
+ VARIABLEOID,
+ VARIABLENAMENSP,
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ Anum_pg_variable_varowner,
+ Anum_pg_variable_varacl,
+ OBJECT_VARIABLE,
+ true
}
};
@@ -714,6 +727,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_VARIABLE */
+ {
+ "schema variable", OBJECT_VARIABLE
}
};
@@ -739,6 +756,7 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
bool missing_ok);
static ObjectAddress get_object_address_type(ObjectType objtype,
TypeName *typename, bool missing_ok);
+static ObjectAddress get_object_address_variable(List *object, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object,
bool missing_ok);
static ObjectAddress get_object_address_opf_member(ObjectType objtype,
@@ -996,6 +1014,10 @@ get_object_address(ObjectType objtype, Node *object,
missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_VARIABLE:
+ address = get_object_address_variable(castNode(List, object), missing_ok);
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
@@ -1848,16 +1870,20 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type \"%c\"", objtype),
- errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
+ errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
DEFACLOBJ_RELATION,
DEFACLOBJ_SEQUENCE,
DEFACLOBJ_FUNCTION,
DEFACLOBJ_TYPE,
- DEFACLOBJ_NAMESPACE)));
+ DEFACLOBJ_NAMESPACE,
+ DEFACLOBJ_VARIABLE)));
}
/*
@@ -1942,6 +1968,24 @@ textarray_to_strvaluelist(ArrayType *arr)
return list;
}
+/*
+ * Find the ObjectAddress for a type or domain
+ */
+static ObjectAddress
+get_object_address_variable(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ char *nspname = NULL;
+ char *varname = NULL;
+
+ ObjectAddressSet(address, VariableRelationId, InvalidOid);
+
+ DeconstructQualifiedName(object, &nspname, &varname);
+ address.objectId = lookup_variable(nspname, varname, missing_ok);
+
+ return address;
+}
+
/*
* SQL-callable version of get_object_address
*/
@@ -2131,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_TABCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
+ case OBJECT_VARIABLE:
objnode = (Node *) name;
break;
case OBJECT_ACCESS_METHOD:
@@ -2415,6 +2460,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
break;
+ case OBJECT_VARIABLE:
+ if (!pg_variable_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
default:
elog(ERROR, "unrecognized object type: %d",
(int) objtype);
@@ -3157,6 +3207,32 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_VARIABLE:
+ {
+ char *nspname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ if (VariableIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ appendStringInfo(&buffer, _("schema variable %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(varform->varname)));
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_TSPARSER:
{
HeapTuple tup;
@@ -3422,6 +3498,16 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new schemas belonging to role %s"),
rolename);
break;
+ case DEFACLOBJ_VARIABLE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new variables belonging to role %s"),
+ rolename);
+ break;
default:
/* shouldn't get here */
if (nspname)
@@ -4070,6 +4156,10 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_VARIABLE:
+ appendStringInfoString(&buffer, "schema variable");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4962,6 +5052,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on variables");
+ break;
}
if (objname)
@@ -5121,6 +5215,33 @@ getObjectIdentityParts(const ObjectAddress *object,
}
break;
+ case OCLASS_VARIABLE:
+ {
+ char *schema;
+ char *varname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for schema variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ schema = get_namespace_name_or_temp(varform->varnamespace);
+ varname = NameStr(varform->varname);
+
+ appendStringInfo(&buffer, "%s",
+ quote_qualified_identifier(schema, varname));
+
+ if (objname)
+ *objname = list_make2(schema, varname);
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
diff --git a/src/backend/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c
new file mode 100644
index 0000000000..5372f30f31
--- /dev/null
+++ b/src/backend/catalog/pg_variable.c
@@ -0,0 +1,352 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.c
+ * schema variables
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_variable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
+#include "nodes/makefuncs.h"
+#include "nodes/primnodes.h"
+#include "storage/lmgr.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+static VariableEOXActionCodes
+to_eoxaction_code(VariableEOXAction action)
+{
+ switch (action)
+ {
+ case VARIABLE_EOX_NOOP:
+ return VARIABLE_EOX_CODE_NOOP;
+
+ case VARIABLE_EOX_DROP:
+ return VARIABLE_EOX_CODE_DROP;
+
+ case VARIABLE_EOX_RESET:
+ return VARIABLE_EOX_CODE_RESET;
+
+ default:
+ elog(ERROR, "unexpected action");
+ }
+
+}
+
+static VariableEOXAction
+to_eoxaction(VariableEOXActionCodes code)
+{
+ switch (code)
+ {
+ case VARIABLE_EOX_CODE_NOOP:
+ return VARIABLE_EOX_NOOP;
+
+ case VARIABLE_EOX_CODE_DROP:
+ return VARIABLE_EOX_DROP;
+
+ case VARIABLE_EOX_CODE_RESET:
+ return VARIABLE_EOX_RESET;
+
+ default:
+ elog(ERROR, "unexpected code");
+ }
+}
+
+/*
+ * Returns name of schema variable. When variable is not on path,
+ * then the name is qualified.
+ */
+char *
+schema_variable_get_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+ char *nspname;
+ char *result;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ if (VariableIsVisible(varid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ result = quote_qualified_identifier(nspname, varname);
+
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/*
+ * Returns varname field of pg_variable
+ */
+char *
+get_schema_variable_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = NameStr(varform->varname);
+
+ ReleaseSysCache(tup);
+
+ return varname;
+}
+
+/*
+ * Returns type, typmod of schema variable
+ */
+void
+get_schema_variable_type_typmod_collid(Oid varid, Oid *typid, int32 *typmod, Oid *collid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ *typid = varform->vartype;
+ *typmod = varform->vartypmod;
+ *collid = varform->varcollation;
+
+ ReleaseSysCache(tup);
+
+ return;
+}
+
+/*
+ * Fetch all fields of schema variable from the syscache.
+ */
+Variable *
+GetVariable(Oid varid, bool missing_ok)
+{
+ HeapTuple tup;
+ Variable *var;
+ Form_pg_variable varform;
+ Datum aclDatum;
+ Datum defexprDatum;
+ bool isnull;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (missing_ok)
+ return NULL;
+
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+ }
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ var = (Variable *) palloc(sizeof(Variable));
+ var->oid = varid;
+ var->name = pstrdup(NameStr(varform->varname));
+ var->namespace = varform->varnamespace;
+ var->typid = varform->vartype;
+ var->typmod = varform->vartypmod;
+ var->owner = varform->varowner;
+ var->collation = varform->varcollation;
+ var->eoxaction = to_eoxaction(varform->vareoxaction);
+
+ /* Get defexpr */
+ defexprDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_vardefexpr,
+ &isnull);
+
+ if (!isnull)
+ var->defexpr = stringToNode(TextDatumGetCString(defexprDatum));
+ else
+ var->defexpr = NULL;
+
+ /* Get varacl */
+ aclDatum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_varacl,
+ &isnull);
+ if (!isnull)
+ var->acl = DatumGetAclPCopy(aclDatum);
+ else
+ var->acl = NULL;
+
+ ReleaseSysCache(tup);
+
+ return var;
+}
+
+ObjectAddress
+VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Oid varCollation,
+ Node *varDefexpr,
+ VariableEOXAction eoxaction,
+ bool if_not_exists)
+{
+ Acl *varacl;
+ NameData varname;
+ bool nulls[Natts_pg_variable];
+ Datum values[Natts_pg_variable];
+ Relation rel;
+ HeapTuple tup,
+ oldtup;
+ TupleDesc tupdesc;
+ ObjectAddress myself,
+ referenced;
+ Oid retval;
+ int i;
+
+ for (i = 0; i < Natts_pg_variable; i++)
+ {
+ nulls[i] = false;
+ values[i] = (Datum) 0;
+ }
+
+ namestrcpy(&varname, varName);
+ values[Anum_pg_variable_varname - 1] = NameGetDatum(&varname);
+ values[Anum_pg_variable_varnamespace - 1] = ObjectIdGetDatum(varNamespace);
+ values[Anum_pg_variable_vartype - 1] = ObjectIdGetDatum(varType);
+ values[Anum_pg_variable_vartypmod - 1] = Int32GetDatum(varTypmod);
+ values[Anum_pg_variable_varowner - 1] = ObjectIdGetDatum(varOwner);
+ values[Anum_pg_variable_varcollation - 1] = ObjectIdGetDatum(varCollation);
+ values[Anum_pg_variable_vareoxaction - 1] = CharGetDatum((char) to_eoxaction_code(eoxaction));
+ /* proacl will be determined later */
+
+ if (varDefexpr)
+ values[Anum_pg_variable_vardefexpr - 1] = CStringGetTextDatum(nodeToString(varDefexpr));
+ else
+ nulls[Anum_pg_variable_vardefexpr - 1] = true;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+ tupdesc = RelationGetDescr(rel);
+
+ oldtup = SearchSysCache2(VARIABLENAMENSP,
+ PointerGetDatum(varName),
+ ObjectIdGetDatum(varNamespace));
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ if (if_not_exists)
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists, skipping",
+ varName)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("schema variable \"%s\" already exists",
+ varName)));
+
+ heap_freetuple(oldtup);
+ heap_close(rel, RowExclusiveLock);
+
+ return InvalidObjectAddress;
+ }
+
+ varacl = get_user_default_acl(OBJECT_VARIABLE, varOwner,
+ varNamespace);
+
+ if (varacl != NULL)
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(varacl);
+ else
+ nulls[Anum_pg_variable_varacl - 1] = true;
+
+ tup = heap_form_tuple(tupdesc, values, nulls);
+ CatalogTupleInsert(rel, tup);
+
+ retval = HeapTupleGetOid(tup);
+
+ myself.classId = VariableRelationId;
+ myself.objectId = retval;
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = varNamespace;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on used type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = varType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on any roles mentioned in ACL */
+ if (varacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(varacl, &newmembers);
+ updateAclDependencies(VariableRelationId, retval, 0,
+ varOwner,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ /* register on commit action if it is necessary */
+ register_variable_on_commit_action(myself.objectId, eoxaction);
+
+ heap_freetuple(tup);
+
+ /* Post creation hook for new function */
+ InvokeObjectPostCreateHook(VariableRelationId, retval, 0);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..2cb5b1172d 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,7 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
- schemacmds.o seclabel.o sequence.o statscmds.o subscriptioncmds.o \
+ schemacmds.o seclabel.o sequence.o schemavariable.o statscmds.o subscriptioncmds.o \
tablecmds.o tablespace.o trigger.o tsearchcmds.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index eff325cc7d..a9d5e5e0ad 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -387,6 +387,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_VARIABLE:
{
ObjectAddress address;
Relation catalog;
@@ -504,6 +505,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
@@ -594,6 +596,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_TSDICT:
case OCLASS_TSTEMPLATE:
case OCLASS_TSCONFIG:
+ case OCLASS_VARIABLE:
{
Relation catalog;
@@ -852,6 +855,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_TABLESPACE:
case OBJECT_TSDICTIONARY:
case OBJECT_TSCONFIGURATION:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 01a999c2ac..fec2495e93 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -19,6 +19,7 @@
#include "commands/discard.h"
#include "commands/prepare.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "utils/guc.h"
#include "utils/portal.h"
@@ -48,6 +49,10 @@ DiscardCommand(DiscardStmt *stmt, bool isTopLevel)
ResetTempTableNamespace();
break;
+ case DISCARD_VARIABLES:
+ ResetSchemaVariableCache();
+ break;
+
default:
elog(ERROR, "unrecognized DISCARD target: %d", stmt->target);
}
@@ -75,4 +80,5 @@ DiscardAll(bool isTopLevel)
ResetPlanCache();
ResetTempTableNamespace();
ResetSequenceCaches();
+ ResetSchemaVariableCache();
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e..426df246b3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -126,6 +126,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"TEXT SEARCH TEMPLATE", true},
{"TYPE", true},
{"USER MAPPING", true},
+ {"VARIABLE", true},
{"VIEW", true},
{NULL, false}
};
@@ -297,7 +298,8 @@ check_ddl_tag(const char *tag)
pg_strcasecmp(tag, "REVOKE") == 0 ||
pg_strcasecmp(tag, "DROP OWNED") == 0 ||
pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
+ pg_strcasecmp(tag, "SECURITY LABEL") == 0 ||
+ pg_strcasecmp(tag, "CREATE VARIABLE") == 0)
return EVENT_TRIGGER_COMMAND_TAG_OK;
/*
@@ -1146,6 +1148,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -1209,6 +1212,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
return true;
/*
@@ -2244,6 +2248,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2326,6 +2332,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b1556a..eb8c08baf3 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
+ case CMD_PLAN_UTILITY:
/* OK */
break;
default:
diff --git a/src/backend/commands/schemavariable.c b/src/backend/commands/schemavariable.c
new file mode 100644
index 0000000000..3d21dddbf8
--- /dev/null
+++ b/src/backend/commands/schemavariable.c
@@ -0,0 +1,753 @@
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
+#include "executor/executor.h"
+#include "executor/svariableReceiver.h"
+#include "nodes/execnodes.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/inval.h"
+#include "utils/memutils.h"
+#include "utils/lsyscache.h"
+#include "utils/snapmgr.h"
+#include "utils/syscache.h"
+
+/*
+ * ON COMMIT action list
+ */
+typedef struct OnCommitItem
+{
+ Oid varid; /* relid of relation */
+ VariableEOXAction eoxaction; /* what to do at end of xact */
+ TransactionId creating_xid;
+
+ /*
+ * If this entry was created during the current transaction,
+ * creating_subid is the ID of the creating subxact; if created in a prior
+ * transaction, creating_subid is zero. If deleted during the current
+ * transaction, deleting_subid is the ID of the deleting subxact; if no
+ * deletion request is pending, deleting_subid is zero.
+ */
+ SubTransactionId creating_subid;
+ SubTransactionId deleting_subid;
+} OnCommitItem;
+
+static List *on_commits = NIL;
+
+/*
+ * The content of variables is not transactional. Due this fact the
+ * implementation of DROP can be simple, because although DROP VARIABLE
+ * can be reverted, the content of variable can be lost. In this example,
+ * DROP VARIABLE is same like reset variable.
+ */
+
+typedef struct SchemaVariableData
+{
+ Oid varid; /* pg_variable OID of this sequence (hash key) */
+ Oid typid; /* OID of the data type */
+ int32 typmod;
+ int16 typlen;
+ bool typbyval;
+ bool isnull;
+ bool freeval;
+ Datum value;
+ bool is_rowtype; /* true when variable is composite */
+ bool is_valid; /* true when variable was successfuly initialized */
+} SchemaVariableData;
+
+typedef SchemaVariableData *SchemaVariable;
+
+static HTAB *schemavarhashtab = NULL; /* hash table for session variables */
+static MemoryContext SchemaVariableMemoryContext = NULL;
+
+static bool first_time = true;
+static void create_schemavar_hashtable(void);
+static bool clean_cache_req = false;
+
+static void clean_cache(void);
+static void force_clean_cache(XactEvent event, void *arg);
+static void remove_variable_on_commit_actions(Oid varid);
+
+
+/*
+ * Save info about ncessity to clean hash table, because some
+ * schema variable was dropped. Don't do here more, recheck
+ * needs to be in transaction state.
+ */
+static void
+InvalidateSchemaVarCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ if (cacheid != VARIABLEOID)
+ return;
+
+ clean_cache_req = true;
+}
+
+static void
+force_clean_cache(XactEvent event, void *arg)
+{
+ /*
+ * should continue only in transaction time, when
+ * syscache is available.
+ */
+ if (clean_cache_req && IsTransactionState())
+ {
+ clean_cache();
+ clean_cache_req = false;
+ }
+}
+
+static void
+clean_cache(void)
+{
+ HASH_SEQ_STATUS status;
+ SchemaVariable var;
+
+ if (!schemavarhashtab)
+ return;
+
+ hash_seq_init(&status, schemavarhashtab);
+
+ /*
+ * Every valid variable have to have entry in system
+ * catalog. Removed if there is nothing.
+ */
+ while ((var = (SchemaVariable) hash_seq_search(&status)) != NULL)
+ {
+ HeapTuple tp = InvalidOid;
+
+ tp = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var->varid));
+ if (!HeapTupleIsValid(tp))
+ {
+ elog(DEBUG1, "variable %d is removed from cache", var->varid);
+
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
+ if (hash_search(schemavarhashtab,
+ (void *) &var->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(DEBUG1, "hash table corrupted");
+ }
+ else
+ ReleaseSysCache(tp);
+ }
+}
+
+/*
+ * Clean variable defined by varid
+ */
+static void
+clean_cache_varid(Oid varid)
+{
+ SchemaVariable svar;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ return;
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_FIND, &found);
+ if (found)
+ {
+ /* clean content, if it is necessary */
+ if (svar->freeval)
+ pfree(DatumGetPointer(svar->value));
+
+ if (hash_search(schemavarhashtab,
+ (void *) &svar->varid,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(DEBUG1, "hash table corrupted");
+
+ remove_variable_on_commit_actions(varid);
+ }
+}
+
+/*
+ * Create the hash table for storing schema variables
+ */
+static void
+create_schemavar_hashtable(void)
+{
+ HASHCTL ctl;
+
+ /* set callbacks */
+ if (first_time)
+ {
+ CacheRegisterSyscacheCallback(VARIABLEOID,
+ InvalidateSchemaVarCacheCallback,
+ (Datum) 0);
+
+ RegisterXactCallback(force_clean_cache, NULL);
+
+ first_time = false;
+ }
+
+ /* needs own long life memory context */
+ if (SchemaVariableMemoryContext == NULL)
+ {
+ SchemaVariableMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "schema variables",
+ ALLOCSET_START_SMALL_SIZES);
+ }
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(SchemaVariableData);
+ ctl.hcxt = SchemaVariableMemoryContext;
+
+ schemavarhashtab = hash_create("Schema variables", 64, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/*
+ * Fast drop complete content of schema variables
+ */
+void
+ResetSchemaVariableCache(void)
+{
+ if (schemavarhashtab)
+ {
+ hash_destroy(schemavarhashtab);
+ schemavarhashtab = NULL;
+ }
+
+ if (SchemaVariableMemoryContext != NULL)
+ {
+ MemoryContextReset(SchemaVariableMemoryContext);
+ }
+}
+
+/*
+ * Drop variable by OID
+ */
+void
+RemoveVariableById(Oid varid)
+{
+ Relation rel;
+ HeapTuple tup;
+
+ rel = heap_open(VariableRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ CatalogTupleDelete(rel, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(rel, RowExclusiveLock);
+
+ /* remove variable from on_commits list */
+ remove_variable_on_commit_actions(varid);
+}
+
+/*
+ * Creates new variable - entry in pg_catalog.pg_variable table
+ */
+ObjectAddress
+DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt)
+{
+ Oid namespaceid;
+ AclResult aclresult;
+ Oid typid;
+ int32 typmod;
+ Oid varowner = GetUserId();
+ Oid collation;
+ Oid typcollation;
+ ObjectAddress variable;
+
+ Node *cooked_default = NULL;
+
+ /*
+ * Check consistency of arguments
+ */
+ if (stmt->eoxaction == VARIABLE_EOX_DROP
+ && stmt->variable->relpersistence != RELPERSISTENCE_TEMP)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("ON COMMIT DROP can only be used on temporary variables")));
+
+ namespaceid =
+ RangeVarGetAndCheckCreationNamespace(stmt->variable, NoLock, NULL);
+
+ typenameTypeIdAndMod(pstate, stmt->typeName, &typid, &typmod);
+ typcollation = get_typcollation(typid);
+
+ aclresult = pg_type_aclcheck(typid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error_type(aclresult, typid);
+
+ if (stmt->collClause)
+ collation = LookupCollation(pstate,
+ stmt->collClause->collname,
+ stmt->collClause->location);
+ else
+ collation = typcollation;;
+
+ /* Complain if COLLATE is applied to an uncollatable type */
+ if (OidIsValid(collation) && !OidIsValid(typcollation))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("collations are not supported by type %s",
+ format_type_be(typid)),
+ parser_errposition(pstate, stmt->collClause->location)));
+
+ if (stmt->defexpr)
+ {
+ cooked_default = transformExpr(pstate, stmt->defexpr,
+ EXPR_KIND_VARIABLE_DEFAULT);
+
+ cooked_default = coerce_to_specific_type(pstate,
+ cooked_default, typid, "DEFAULT");
+ assign_expr_collations(pstate, cooked_default);
+ }
+
+ variable = VariableCreate(stmt->variable->relname,
+ namespaceid,
+ typid,
+ typmod,
+ varowner,
+ collation,
+ cooked_default,
+ stmt->eoxaction,
+ stmt->if_not_exists);
+
+ /*
+ * We must bump the command counter to make the newly-created variable
+ * tuple visible for any other operations.
+ */
+ CommandCounterIncrement();
+
+ return variable;
+}
+
+/*
+ * Try to search value in hash table. If doesn't
+ * exists insert it (and calculate defexpr if exists.
+ */
+static SchemaVariable
+PrepareSchemaVariableForReading(Oid varid)
+{
+ SchemaVariable svar;
+ Variable *var;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+ if (!found)
+ {
+ var = GetVariable(varid, false);
+ get_typlenbyval(var->typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = var->typid;
+ svar->typmod = var->typmod;
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+ svar->is_rowtype = type_is_rowtype(var->typid);
+
+ /* when we don't need calculate defexpr, value is valid already */
+ svar->is_valid = var->defexpr ? false : true;
+
+ if (var->eoxaction != VARIABLE_EOX_NOOP)
+ register_variable_on_commit_action(varid, var->eoxaction);
+ }
+ else if (!svar->is_valid)
+ {
+ /* we need var to recalculate defexpr */
+ var = GetVariable(varid, false);
+ }
+ else
+ /* we don't need to go to sys cache */
+ var = NULL;
+
+ /*
+ * Initialize variable when it is necessary. It is fresh
+ * or last initialization was not successfull.
+ */
+ if (var != NULL && var->defexpr && !svar->is_valid)
+ {
+ MemoryContext oldcontext = NULL;
+
+ Datum value = (Datum) 0;
+ bool null;
+ EState *estate = NULL;
+ Expr *defexpr;
+ ExprState *defexprs;
+
+ /* Prepare default expr */
+ estate = CreateExecutorState();
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ defexpr = expression_planner((Expr *) var->defexpr);
+ defexprs = ExecInitExpr(defexpr, NULL);
+ value = ExecEvalExprSwitchContext(defexprs, GetPerTupleExprContext(estate), &null);
+
+ MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!null)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ FreeExecutorState(estate);
+ }
+
+ if (!svar->is_valid)
+ elog(ERROR, "the content of variable is not valid");
+
+ return svar;
+}
+
+/*
+ * Returns content of variable. We expext secured access now.
+ * Secure check should be done before.
+ */
+Datum
+GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid, bool copy)
+{
+ SchemaVariable svar;
+ Datum value;
+ bool isnull;
+
+ svar = PrepareSchemaVariableForReading(varid);
+ Assert(svar != NULL);
+
+ if (expected_typid != svar->typid)
+ elog(ERROR, "type of variable \"%s\" is different than expected",
+ schema_variable_get_name(varid));
+
+ value = svar->value;
+ isnull = svar->isnull;
+
+ *isNull = isnull;
+
+ if (!isnull && copy)
+ return datumCopy(value, svar->typbyval, svar->typlen);
+
+ return value;
+}
+
+/*
+ * Returns copy of value specified schema variable
+ */
+Datum
+CopySchemaVariable(Oid varid, bool *isNull, Oid *typid)
+{
+ SchemaVariable svar;
+
+ svar = PrepareSchemaVariableForReading(varid);
+ Assert(svar != NULL);
+
+ *isNull = svar->isnull;
+ *typid = svar->typid;
+
+ if (!svar->isnull)
+ return datumCopy(svar->value, svar->typbyval, svar->typlen);
+
+ return (Datum) 0;
+}
+
+/*
+ * Write value to variable. We expect secured access in this moment.
+ * In this time, we recheck syschache about used type.
+ */
+void
+SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod)
+{
+ MemoryContext oldcontext = NULL;
+
+ SchemaVariable svar;
+ Oid var_typid;
+ int32 var_typmod;
+ Oid var_collid;
+ bool found;
+
+ if (schemavarhashtab == NULL)
+ create_schemavar_hashtable();
+
+ svar = (SchemaVariable) hash_search(schemavarhashtab, &varid,
+ HASH_ENTER, &found);
+
+ get_schema_variable_type_typmod_collid(varid,
+ &var_typid,
+ &var_typmod,
+ &var_collid);
+
+ /* check types first */
+ if (var_typid != typid)
+ elog(ERROR, "type of expression is different than schema variable type");
+
+ if (found)
+ {
+ /* release current content first */
+ if (svar->freeval)
+ {
+ pfree(DatumGetPointer(svar->value));
+ svar->value = (Datum) 0;
+ svar->isnull = true;
+ svar->freeval = false;
+ }
+ }
+ else
+ {
+ Variable *var = GetVariable(varid, false);
+
+ register_variable_on_commit_action(varid, var->eoxaction);
+ }
+
+ get_typlenbyval(typid, &svar->typlen, &svar->typbyval);
+
+ svar->varid = varid;
+ svar->typid = typid;
+ svar->typmod = typmod;
+
+ svar->isnull = true;
+ svar->freeval = false;
+ svar->value = (Datum) 0;
+
+ svar->is_rowtype = type_is_rowtype(typid);
+ svar->is_valid = false;
+
+ oldcontext = MemoryContextSwitchTo(SchemaVariableMemoryContext);
+
+ if (!isNull)
+ {
+ svar->value = datumCopy(value, svar->typbyval, svar->typlen);
+ svar->freeval = svar->value != value;
+ svar->isnull = false;
+ svar->is_valid = true;
+ }
+ else
+ {
+ svar->isnull = true;
+ svar->is_valid = true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+}
+
+/*
+ * Reset to default specified schema variable
+ */
+void
+doLetStmtReset(PlannedStmt *pstmt)
+{
+ clean_cache_varid(pstmt->resultVariable);
+}
+
+/*
+ * Assign result of evaluated expression to schema variable
+ */
+void
+doLetStmtEval(PlannedStmt *pstmt,
+ ParamListInfo params,
+ QueryEnvironment *queryEnv,
+ const char *queryString)
+{
+ QueryDesc *queryDesc;
+ DestReceiver *dest;
+
+ PushCopiedSnapshot(GetActiveSnapshot());
+ UpdateActiveSnapshotCommandId();
+
+ /* Create dest receiver for LET */
+ dest = CreateDestReceiver(DestVariable);
+
+ SetVariableDestReceiverParams(dest, pstmt->resultVariable);
+
+ /* Create a QueryDesc requesting no output */
+ queryDesc = CreateQueryDesc(pstmt, queryString,
+ GetActiveSnapshot(),
+ InvalidSnapshot,
+ dest, params, queryEnv, 0);
+
+ ExecutorStart(queryDesc, 0);
+ ExecutorRun(queryDesc, ForwardScanDirection, 2L, true);
+ ExecutorFinish(queryDesc);
+ ExecutorEnd(queryDesc);
+
+ FreeQueryDesc(queryDesc);
+
+ PopActiveSnapshot();
+}
+
+/*
+ * Register a newly-created relation's ON COMMIT action.
+ */
+void
+register_variable_on_commit_action(Oid varid, VariableEOXAction action)
+{
+ OnCommitItem *oc;
+ MemoryContext oldcxt;
+
+ /*
+ * We needn't bother registering the relation unless there is an ON COMMIT
+ * action we need to take.
+ */
+ if (action == VARIABLE_EOX_NOOP)
+ return;
+
+ oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+ oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
+ oc->varid = varid;
+ oc->eoxaction = action;
+ oc->creating_subid = GetCurrentSubTransactionId();
+ oc->deleting_subid = InvalidSubTransactionId;
+
+ on_commits = lcons(oc, on_commits);
+
+ MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * Remove variable from on_commits action
+ */
+static void
+remove_variable_on_commit_actions(Oid varid)
+{
+ ListCell *l;
+
+ foreach(l, on_commits)
+ {
+ OnCommitItem *oc = (OnCommitItem *) lfirst(l);
+
+ if (oc->varid == varid)
+ {
+ oc->deleting_subid = GetCurrentSubTransactionId();
+ }
+ }
+}
+
+/*
+ * Perform ON TRANSACTION END RESET or ON COMMIT DROP
+ */
+void
+AtPreEOXact_SchemaVariable_on_commit_actions(bool isCommit)
+{
+ ListCell *l;
+
+ foreach(l, on_commits)
+ {
+ OnCommitItem *oc = (OnCommitItem *) lfirst(l);
+
+ /* Ignore entry if already dropped in this xact */
+ if (oc->deleting_subid != InvalidSubTransactionId)
+ continue;
+
+ switch (oc->eoxaction)
+ {
+ case VARIABLE_EOX_NOOP:
+ /* Do nothing */
+ break;
+ case VARIABLE_EOX_RESET:
+ clean_cache_varid(oc->varid);
+ break;
+ case VARIABLE_EOX_DROP:
+ {
+ /*
+ * ON COMMIT DROP is allowed only for temp schema variables.
+ * So we should explicit delete only when current transaction
+ * was committed. When is rollback, then schema variable is
+ * removed automatically.
+ */
+ if (isCommit)
+ {
+ ObjectAddress object;
+
+ object.classId = VariableRelationId;
+ object.objectId = oc->varid;
+ object.objectSubId = 0;
+
+ /*
+ * Since this is an automatic drop, rather than one
+ * directly initiated by the user, we pass the
+ * PERFORM_DELETION_INTERNAL flag.
+ */
+ performDeletion(&object,
+ DROP_CASCADE, PERFORM_DELETION_INTERNAL);
+ }
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Post-commit or post-abort cleanup for ON COMMIT management.
+ *
+ * All we do here is remove no-longer-needed OnCommitItem entries.
+ *
+ * During commit, remove entries that were deleted during this transaction;
+ * during abort, remove those created during this transaction.
+ */
+void
+AtEOXact_SchemaVariable_on_commit_actions(bool isCommit)
+{
+ ListCell *cur_item;
+ ListCell *prev_item;
+
+ prev_item = NULL;
+ cur_item = list_head(on_commits);
+
+ while (cur_item != NULL)
+ {
+ OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
+
+ if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
+ oc->creating_subid != InvalidSubTransactionId)
+ {
+ /* cur_item must be removed */
+ on_commits = list_delete_cell(on_commits, cur_item, prev_item);
+ pfree(oc);
+ if (prev_item)
+ cur_item = lnext(prev_item);
+ else
+ cur_item = list_head(on_commits);
+ }
+ else
+ {
+ oc->creating_subid = InvalidSubTransactionId;
+ oc->deleting_subid = InvalidSubTransactionId;
+ prev_item = cur_item;
+ cur_item = lnext(prev_item);
+ }
+ }
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index e96512e051..0bd9b240b4 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9655,6 +9655,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
/*
* We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index cc09895fa5..ee8ff7da9e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -29,6 +29,6 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
nodeCtescan.o nodeNamedtuplestorescan.o nodeWorktablescan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o \
- nodeTableFuncscan.o
+ nodeTableFuncscan.o svariableReceiver.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d7..6ad95028cf 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -33,6 +33,7 @@
#include "access/nbtree.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -736,6 +737,56 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
break;
+
+ case PARAM_VARIABLE:
+ {
+ int es_num_schema_variables = 0;
+ SchemaVariableValue *es_schema_variables = NULL;
+
+ if (state->parent && state->parent->state)
+ {
+ es_schema_variables = state->parent->state->es_schema_variables;
+ es_num_schema_variables = state->parent->state->es_num_schema_variables;
+ }
+
+ /*
+ * We should to use schema variable buffer, when it is
+ * available.
+ */
+ if (es_schema_variables)
+ {
+ SchemaVariableValue *var;
+
+ /* check params, unexpected */
+ if (param->paramid >= es_num_schema_variables)
+ elog(ERROR, "paramid of PARAM_VARIABLE param is out of range");
+
+ var = &es_schema_variables[param->paramid];
+
+ /* unexpected */
+ if (var->typid != param->paramtype)
+ elog(ERROR, "type of buffered value is different than PARAM_VARIABLE type");
+
+ /* In this case, the parameter is like a constant */
+ scratch.opcode = EEOP_CONST;
+ scratch.d.constval.value = var->value;
+ scratch.d.constval.isnull = var->isnull;
+ ExprEvalPushStep(state, &scratch);
+ }
+ else
+ {
+ /*
+ * When we have not a full PlanState (plpgsql simple
+ * expr evaluation, then we should to use direct access.
+ */
+ scratch.opcode = EEOP_PARAM_VARIABLE;
+ scratch.d.vparam.varid = param->paramvarid;
+ scratch.d.vparam.vartype = param->paramtype;
+ ExprEvalPushStep(state, &scratch);
+ }
+ }
+ break;
+
case PARAM_EXTERN:
/*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25aae5..5dc22ee1c7 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/schemavariable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -351,6 +352,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1007,6 +1009,16 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_VARIABLE)
+ {
+ /* direct access to schema variable (without buffering) */
+ *op->resvalue = GetSchemaVariable(op->d.vparam.varid,
+ op->resnull,
+ op->d.vparam.vartype,
+ true);
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_CASE_TESTVAL)
{
/*
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index c583e020a0..70d1fcdaa7 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,9 +43,12 @@
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_publication.h"
+#include "catalog/pg_variable.h"
#include "commands/matview.h"
#include "commands/trigger.h"
+#include "commands/schemavariable.h"
#include "executor/execdebug.h"
+#include "executor/svariableReceiver.h"
#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -199,17 +202,65 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
estate->es_sourceText = queryDesc->sourceText;
+ /*
+ * Prepare schema variables, if are not prepared in queryDesc
+ */
+ if (queryDesc->num_schema_variables > 0)
+ {
+ /* When buffer of used schema variables loaded from shared memory */
+ estate->es_schema_variables = queryDesc->schema_variables;
+ estate->es_num_schema_variables = queryDesc->num_schema_variables;
+ }
+ else if (queryDesc->plannedstmt->schemaVariables)
+ {
+ ListCell *lc;
+ int nSchemaVariables;
+ int i = 0;
+
+ nSchemaVariables = list_length(queryDesc->plannedstmt->schemaVariables);
+
+ /* Create buffer for used schema variables */
+ estate->es_schema_variables = (SchemaVariableValue *)
+ palloc(nSchemaVariables * sizeof(SchemaVariableValue));
+
+ foreach(lc, queryDesc->plannedstmt->schemaVariables)
+ {
+ AclResult aclresult;
+ Oid varid = lfirst_oid(lc);
+
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_READ);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE,
+ schema_variable_get_name(varid));
+
+ estate->es_schema_variables[i].varid = varid;
+ estate->es_schema_variables[i].value = CopySchemaVariable(varid,
+ &estate->es_schema_variables[i].isnull,
+ &estate->es_schema_variables[i].typid);
+
+ i++;
+ }
+
+ estate->es_num_schema_variables = nSchemaVariables;
+ }
+
/*
* Fill in the query environment, if any, from queryDesc.
*/
estate->es_queryEnv = queryDesc->queryEnv;
+ /*
+ * Result can be stored in schema variable.
+ */
+ estate->es_result_variable = queryDesc->plannedstmt->resultVariable;
+
/*
* If non-read-only query, set the command ID to mark output tuples with
*/
switch (queryDesc->operation)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
@@ -345,6 +396,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate->es_lastoid = InvalidOid;
sendTuples = (operation == CMD_SELECT ||
+ OidIsValid(estate->es_result_variable) ||
queryDesc->plannedstmt->hasReturning);
if (sendTuples)
@@ -924,6 +976,17 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_num_root_result_relations = 0;
}
+ if (OidIsValid(estate->es_result_variable))
+ {
+ AclResult aclresult;
+ Oid varid = estate->es_result_variable;
+
+ /* Ensure this variable is writeable */
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, schema_variable_get_name(varid));
+ }
+
/*
* Similarly, we have to lock relations selected FOR [KEY] UPDATE/SHARE
* before we initialize the plan tree, else we'd be risking lock upgrades.
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index ee0f07a81e..be36fbb011 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -12,8 +12,9 @@
* workers and ensuring that their state generally matches that of the
* leader; see src/backend/access/transam/README.parallel for details.
* However, we must save and restore relevant executor state, such as
- * any ParamListInfo associated with the query, buffer usage info, and
- * the actual plan to be passed down to the worker.
+ * any ParamListInfo associated with the query, buffer usage info, used
+ * schema variables buffer, and the actual plan to be passed down to the
+ * worker.
*
* IDENTIFICATION
* src/backend/executor/execParallel.c
@@ -62,6 +63,7 @@
#define PARALLEL_KEY_INSTRUMENTATION UINT64CONST(0xE000000000000006)
#define PARALLEL_KEY_DSA UINT64CONST(0xE000000000000007)
#define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xE000000000000008)
+#define PARALLEL_KEY_SCHEMA_VARIABLES UINT64CONST(0xE000000000000009)
#define PARALLEL_TUPLE_QUEUE_SIZE 65536
@@ -136,6 +138,12 @@ static bool ExecParallelRetrieveInstrumentation(PlanState *planstate,
/* Helper function that runs in the parallel worker. */
static DestReceiver *ExecParallelGetReceiver(dsm_segment *seg, shm_toc *toc);
+/* Helper functions that can pass values of used schema variables */
+static Size EstimateSchemaVariables(EState *estate);
+static void SerializeSchemaVariables(EState *estate, char **start_address);
+static SchemaVariableValue *RestoreSchemaVariables(char **start_address,
+ int *num_schema_variables);
+
/*
* Create a serialized representation of the plan to be sent to each worker.
*/
@@ -571,12 +579,14 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
char *pstmt_data;
char *pstmt_space;
char *paramlistinfo_space;
+ char *schema_variables_space;
BufferUsage *bufusage_space;
SharedExecutorInstrumentation *instrumentation = NULL;
int pstmt_len;
int paramlistinfo_len;
int instrumentation_len = 0;
int instrument_offset = 0;
+ int schema_variables_len = 0;
Size dsa_minsize = dsa_minimum_size();
char *query_string;
int query_len;
@@ -622,6 +632,11 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
shm_toc_estimate_chunk(&pcxt->estimator, paramlistinfo_len);
shm_toc_estimate_keys(&pcxt->estimator, 1);
+ /* Estimate space for serialized schema variables. */
+ schema_variables_len = EstimateSchemaVariables(estate);
+ shm_toc_estimate_chunk(&pcxt->estimator, schema_variables_len);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
/*
* Estimate space for BufferUsage.
*
@@ -699,6 +714,11 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
shm_toc_insert(pcxt->toc, PARALLEL_KEY_PARAMLISTINFO, paramlistinfo_space);
SerializeParamList(estate->es_param_list_info, ¶mlistinfo_space);
+ /* Store serialized schema variables. */
+ schema_variables_space = shm_toc_allocate(pcxt->toc, schema_variables_len);
+ shm_toc_insert(pcxt->toc, PARALLEL_KEY_SCHEMA_VARIABLES, schema_variables_space);
+ SerializeSchemaVariables(estate, &schema_variables_space);
+
/* Allocate space for each worker's BufferUsage; no need to initialize. */
bufusage_space = shm_toc_allocate(pcxt->toc,
mul_size(sizeof(BufferUsage), pcxt->nworkers));
@@ -1099,6 +1119,7 @@ ExecParallelGetQueryDesc(shm_toc *toc, DestReceiver *receiver,
{
char *pstmtspace;
char *paramspace;
+ char *schemavariablespace;
PlannedStmt *pstmt;
ParamListInfo paramLI;
char *queryString;
@@ -1262,6 +1283,7 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
SharedExecutorInstrumentation *instrumentation;
int instrument_options = 0;
void *area_space;
+ char *schemavariable_space;
dsa_area *area;
ParallelWorkerContext pwcxt;
@@ -1285,6 +1307,14 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
area_space = shm_toc_lookup(toc, PARALLEL_KEY_DSA, false);
area = dsa_attach_in_place(area_space, seg);
+ /* Reconstruct schema variables. */
+ schemavariable_space = shm_toc_lookup(toc,
+ PARALLEL_KEY_SCHEMA_VARIABLES,
+ false);
+ queryDesc->schema_variables =
+ RestoreSchemaVariables(&schemavariable_space,
+ &queryDesc->num_schema_variables);
+
/* Start up the executor */
queryDesc->plannedstmt->jitFlags = fpes->jit_flags;
ExecutorStart(queryDesc, fpes->eflags);
@@ -1344,3 +1374,118 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
FreeQueryDesc(queryDesc);
receiver->rDestroy(receiver);
}
+
+/*
+ * Estimate the amount of space required to serialize a used
+ * schema variables.
+ */
+static Size
+EstimateSchemaVariables(EState *estate)
+{
+ int i;
+ Size sz = sizeof(int);
+
+ if (estate->es_schema_variables == NULL)
+ return sz;
+
+ for (i = 0; i < estate->es_num_schema_variables; i++)
+ {
+ SchemaVariableValue *svarval;
+ Oid typeOid;
+ int16 typLen;
+ bool typByVal;
+
+ svarval = &estate->es_schema_variables[i];
+
+ typeOid = svarval->typid;
+
+ sz = add_size(sz, sizeof(Oid)); /* space for type OID */
+
+ /* space for datum/isnull */
+ Assert(OidIsValid(typeOid));
+ get_typlenbyval(typeOid, &typLen, &typByVal);
+
+ sz = add_size(sz,
+ datumEstimateSpace(svarval->value, svarval->isnull, typByVal, typLen));
+ }
+
+ return sz;
+}
+
+/*
+ * Serialize a schema variables buffer into caller-provided storage.
+ *
+ * We write the number of parameters first, as a 4-byte integer, and then
+ * write details for each parameter in turn. The details for each parameter
+ * consist of a 4-byte type OID, and then the datum as serialized by
+ * datumSerialize(). The caller is responsible for ensuring that there is
+ * enough storage to store the number of bytes that will be written; use
+ * EstimateSchemaVariables to find out how many will be needed.
+ * *start_address is updated to point to the byte immediately following those
+ * written.
+ *
+ * RestoreSchemaVariables can be used to recreate a schema variable buffer
+ * based on the serialized representation;
+ */
+static void
+SerializeSchemaVariables(EState *estate, char **start_address)
+{
+ int nparams;
+ int i;
+
+ /* Write number of parameters. */
+ nparams = estate->es_num_schema_variables;
+ memcpy(*start_address, &nparams, sizeof(int));
+ *start_address += sizeof(int);
+
+ /* Write each parameter in turn. */
+ for (i = 0; i < nparams; i++)
+ {
+ SchemaVariableValue *svarval;
+ Oid typeOid;
+ int16 typLen;
+ bool typByVal;
+
+ svarval = &estate->es_schema_variables[i];
+ typeOid = svarval->typid;
+
+ /* Write type OID. */
+ memcpy(*start_address, &typeOid, sizeof(Oid));
+ *start_address += sizeof(Oid);
+
+ Assert(OidIsValid(typeOid));
+ get_typlenbyval(typeOid, &typLen, &typByVal);
+
+ datumSerialize(svarval->value, svarval->isnull, typByVal, typLen,
+ start_address);
+ }
+}
+
+static SchemaVariableValue *
+RestoreSchemaVariables(char **start_address, int *num_schema_variables)
+{
+ SchemaVariableValue *schema_variables;
+ int i;
+ int nparams;
+
+ memcpy(&nparams, *start_address, sizeof(int));
+ *start_address += sizeof(int);
+
+ *num_schema_variables = nparams;
+ schema_variables = (SchemaVariableValue *)
+ palloc(nparams * sizeof(SchemaVariableValue));
+
+ for (i = 0; i < nparams; i++)
+ {
+ SchemaVariableValue *svarval = &schema_variables[i];
+
+ /* Read type OID. */
+ memcpy(&svarval->typid, *start_address, sizeof(Oid));
+ *start_address += sizeof(Oid);
+
+ /* Read datum/isnull. */
+ svarval->value = datumRestore(start_address, &svarval->isnull);
+ }
+
+ return schema_variables;
+}
diff --git a/src/backend/executor/svariableReceiver.c b/src/backend/executor/svariableReceiver.c
new file mode 100644
index 0000000000..0eac4b5d0c
--- /dev/null
+++ b/src/backend/executor/svariableReceiver.c
@@ -0,0 +1,145 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.c
+ * An implementation of DestReceiver that stores the result value in
+ * a schema variable.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/svariableReceiver.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/tuptoaster.h"
+#include "executor/svariableReceiver.h"
+#include "commands/schemavariable.h"
+
+typedef struct
+{
+ DestReceiver pub;
+ Oid varid;
+ Oid typid;
+ int32 typmod;
+ int typlen;
+ int slot_offset;
+ int rows;
+} svariableState;
+
+
+/*
+ * Prepare to receive tuples from executor.
+ */
+static void
+svariableStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+ svariableState *myState = (svariableState *) self;
+ int natts = typeinfo->natts;
+ int outcols = 0;
+ int i;
+
+ for (i = 0; i < natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+
+ if (attr->attisdropped)
+ continue;
+
+ if (++outcols > 1)
+ elog(ERROR, "svariable DestReceiver can take only one attribute");
+
+ myState->typid = attr->atttypid;
+ myState->typmod = attr->atttypmod;
+ myState->typlen = attr->attlen;
+ myState->slot_offset = i;
+ }
+
+ myState->rows = 0;
+}
+
+/*
+ * Receive a tuple from the executor and store it in schema variable.
+ */
+static bool
+svariableReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
+{
+ svariableState *myState = (svariableState *) self;
+ Datum value;
+ bool isnull;
+ bool freeval = false;
+
+ /* Make sure the tuple is fully deconstructed */
+ slot_getallattrs(slot);
+
+ value = slot->tts_values[myState->slot_offset];
+ isnull = slot->tts_isnull[myState->slot_offset];
+
+ if (myState->typlen == -1 && !isnull && VARATT_IS_EXTERNAL(DatumGetPointer(value)))
+ {
+ value = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+ DatumGetPointer(value)));
+ freeval = true;
+ }
+
+ SetSchemaVariable(myState->varid, value, isnull, myState->typid, myState->typmod);
+
+ if (freeval)
+ pfree(DatumGetPointer(value));
+
+ return true;
+}
+
+/*
+ * Clean up at end of an executor run
+ */
+static void
+svariableShutdownReceiver(DestReceiver *self)
+{
+ /* Do nothing */
+}
+
+/*
+ * Destroy receiver when done with it
+ */
+static void
+svariableDestroyReceiver(DestReceiver *self)
+{
+ pfree(self);
+}
+
+/*
+ * Initially create a DestReceiver object.
+ */
+DestReceiver *
+CreateVariableDestReceiver(void)
+{
+ svariableState *self = (svariableState *) palloc0(sizeof(svariableState));
+
+ self->pub.receiveSlot = svariableReceiveSlot;
+ self->pub.rStartup = svariableStartupReceiver;
+ self->pub.rShutdown = svariableShutdownReceiver;
+ self->pub.rDestroy = svariableDestroyReceiver;
+ self->pub.mydest = DestVariable;
+
+ /* private fields will be set by SetVariableDestReceiverParams */
+
+ return (DestReceiver *) self;
+}
+
+/*
+ * Set parameters for a VariableDestReceiver
+ */
+void
+SetVariableDestReceiverParams(DestReceiver *self, Oid varid)
+{
+ svariableState *myState = (svariableState *) self;
+
+ Assert(myState->pub.mydest == DestVariable);
+ Assert(OidIsValid(varid));
+
+ myState->varid = varid;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c8220cf65..523db538aa 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(nonleafResultRelations);
COPY_NODE_FIELD(rootResultRelations);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks);
@@ -100,6 +101,7 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(invalItems);
COPY_NODE_FIELD(paramExecTypes);
COPY_NODE_FIELD(utilityStmt);
+ COPY_NODE_FIELD(schemaVariables);
COPY_LOCATION_FIELD(stmt_location);
COPY_LOCATION_FIELD(stmt_len);
@@ -1415,6 +1417,7 @@ _copyParam(const Param *from)
COPY_SCALAR_FIELD(paramtype);
COPY_SCALAR_FIELD(paramtypmod);
COPY_SCALAR_FIELD(paramcollid);
+ COPY_SCALAR_FIELD(paramvarid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -3000,6 +3003,7 @@ _copyQuery(const Query *from)
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
+ COPY_SCALAR_FIELD(resultVariable);
COPY_SCALAR_FIELD(hasAggs);
COPY_SCALAR_FIELD(hasWindowFuncs);
COPY_SCALAR_FIELD(hasTargetSRFs);
@@ -3009,6 +3013,7 @@ _copyQuery(const Query *from)
COPY_SCALAR_FIELD(hasModifyingCTE);
COPY_SCALAR_FIELD(hasForUpdate);
COPY_SCALAR_FIELD(hasRowSecurity);
+ COPY_SCALAR_FIELD(hasSchemaVariable);
COPY_NODE_FIELD(cteList);
COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(jointree);
@@ -3118,6 +3123,18 @@ _copySelectStmt(const SelectStmt *from)
return newnode;
}
+static LetStmt *
+_copyLetStmt(const LetStmt *from)
+{
+ LetStmt *newnode = makeNode(LetStmt);
+
+ COPY_NODE_FIELD(target);
+ COPY_NODE_FIELD(selectStmt);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
static SetOperationStmt *
_copySetOperationStmt(const SetOperationStmt *from)
{
@@ -5166,6 +5183,9 @@ copyObjectImpl(const void *from)
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
+ case T_LetStmt:
+ retval = _copyLetStmt(from);
+ break;
case T_SetOperationStmt:
retval = _copySetOperationStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 378f2facb8..2ea5e18b1a 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -202,6 +202,7 @@ _equalParam(const Param *a, const Param *b)
COMPARE_SCALAR_FIELD(paramtype);
COMPARE_SCALAR_FIELD(paramtypmod);
COMPARE_SCALAR_FIELD(paramcollid);
+ COMPARE_SCALAR_FIELD(paramvarid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -949,6 +950,7 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_SCALAR_FIELD(canSetTag);
COMPARE_NODE_FIELD(utilityStmt);
COMPARE_SCALAR_FIELD(resultRelation);
+ COMPARE_SCALAR_FIELD(resultVariable);
COMPARE_SCALAR_FIELD(hasAggs);
COMPARE_SCALAR_FIELD(hasWindowFuncs);
COMPARE_SCALAR_FIELD(hasTargetSRFs);
@@ -958,6 +960,7 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_SCALAR_FIELD(hasModifyingCTE);
COMPARE_SCALAR_FIELD(hasForUpdate);
COMPARE_SCALAR_FIELD(hasRowSecurity);
+ COMPARE_SCALAR_FIELD(hasSchemaVariable);
COMPARE_NODE_FIELD(cteList);
COMPARE_NODE_FIELD(rtable);
COMPARE_NODE_FIELD(jointree);
@@ -1057,6 +1060,16 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
return true;
}
+static bool
+_equalLetStmt(const LetStmt *a, const LetStmt *b)
+{
+ COMPARE_NODE_FIELD(target);
+ COMPARE_NODE_FIELD(selectStmt);
+
+ return true;
+}
+
+
static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
@@ -3225,6 +3238,9 @@ equal(const void *a, const void *b)
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
break;
+ case T_LetStmt:
+ retval = _equalLetStmt(a, b);
+ break;
case T_SetOperationStmt:
retval = _equalSetOperationStmt(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b5af904c18..9eb3e3f3bb 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -278,6 +278,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(nonleafResultRelations);
WRITE_NODE_FIELD(rootResultRelations);
+ WRITE_OID_FIELD(resultVariable);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks);
@@ -285,6 +286,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(invalItems);
WRITE_NODE_FIELD(paramExecTypes);
WRITE_NODE_FIELD(utilityStmt);
+ WRITE_NODE_FIELD(schemaVariables);
WRITE_LOCATION_FIELD(stmt_location);
WRITE_LOCATION_FIELD(stmt_len);
}
@@ -1194,6 +1196,7 @@ _outParam(StringInfo str, const Param *node)
WRITE_OID_FIELD(paramtype);
WRITE_INT_FIELD(paramtypmod);
WRITE_OID_FIELD(paramcollid);
+ WRITE_OID_FIELD(paramvarid);
WRITE_LOCATION_FIELD(location);
}
@@ -2260,6 +2263,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node)
WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems);
WRITE_NODE_FIELD(paramExecTypes);
+ WRITE_NODE_FIELD(schemaVariables);
WRITE_UINT_FIELD(lastPHId);
WRITE_UINT_FIELD(lastRowMarkId);
WRITE_INT_FIELD(lastPlanNodeId);
@@ -2316,6 +2320,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
WRITE_BOOL_FIELD(hasHavingQual);
WRITE_BOOL_FIELD(hasPseudoConstantQuals);
WRITE_BOOL_FIELD(hasRecursion);
+ WRITE_BOOL_FIELD(hasSchemaVariable);
WRITE_INT_FIELD(wt_param_id);
WRITE_BITMAPSET_FIELD(curOuterRels);
WRITE_NODE_FIELD(curOuterParams);
@@ -2794,6 +2799,16 @@ _outSelectStmt(StringInfo str, const SelectStmt *node)
WRITE_NODE_FIELD(rarg);
}
+static void
+_outLetStmt(StringInfo str, const LetStmt *node)
+{
+ WRITE_NODE_TYPE("LET");
+
+ WRITE_NODE_FIELD(target);
+ WRITE_NODE_FIELD(selectStmt);
+ WRITE_LOCATION_FIELD(location);
+}
+
static void
_outFuncCall(StringInfo str, const FuncCall *node)
{
@@ -2972,6 +2987,7 @@ _outQuery(StringInfo str, const Query *node)
appendStringInfoString(str, " :utilityStmt <>");
WRITE_INT_FIELD(resultRelation);
+ WRITE_INT_FIELD(resultVariable);
WRITE_BOOL_FIELD(hasAggs);
WRITE_BOOL_FIELD(hasWindowFuncs);
WRITE_BOOL_FIELD(hasTargetSRFs);
@@ -2981,6 +2997,7 @@ _outQuery(StringInfo str, const Query *node)
WRITE_BOOL_FIELD(hasModifyingCTE);
WRITE_BOOL_FIELD(hasForUpdate);
WRITE_BOOL_FIELD(hasRowSecurity);
+ WRITE_BOOL_FIELD(hasSchemaVariable);
WRITE_NODE_FIELD(cteList);
WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(jointree);
@@ -4192,6 +4209,9 @@ outNode(StringInfo str, const void *obj)
case T_SelectStmt:
_outSelectStmt(str, obj);
break;
+ case T_LetStmt:
+ _outLetStmt(str, obj);
+ break;
case T_ColumnDef:
_outColumnDef(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3254524223..3ea7e47387 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -242,6 +242,7 @@ _readQuery(void)
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
+ READ_INT_FIELD(resultVariable);
READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasWindowFuncs);
READ_BOOL_FIELD(hasTargetSRFs);
@@ -251,6 +252,7 @@ _readQuery(void)
READ_BOOL_FIELD(hasModifyingCTE);
READ_BOOL_FIELD(hasForUpdate);
READ_BOOL_FIELD(hasRowSecurity);
+ READ_BOOL_FIELD(hasSchemaVariable);
READ_NODE_FIELD(cteList);
READ_NODE_FIELD(rtable);
READ_NODE_FIELD(jointree);
@@ -571,6 +573,7 @@ _readParam(void)
READ_OID_FIELD(paramtype);
READ_INT_FIELD(paramtypmod);
READ_OID_FIELD(paramcollid);
+ READ_OID_FIELD(paramvarid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -1485,6 +1488,7 @@ _readPlannedStmt(void)
READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(nonleafResultRelations);
READ_NODE_FIELD(rootResultRelations);
+ READ_OID_FIELD(resultVariable);
READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks);
@@ -1492,6 +1496,7 @@ _readPlannedStmt(void)
READ_NODE_FIELD(invalItems);
READ_NODE_FIELD(paramExecTypes);
READ_NODE_FIELD(utilityStmt);
+ READ_NODE_FIELD(schemaVariables);
READ_LOCATION_FIELD(stmt_location);
READ_LOCATION_FIELD(stmt_len);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index e589471fee..c0eb222296 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -301,6 +301,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->lastPlanNodeId = 0;
glob->transientPlan = false;
glob->dependsOnRole = false;
+ glob->schemaVariables = NIL;
/*
* Assess whether it's feasible to use parallel mode for this query. We
@@ -334,7 +335,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
*/
if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
IsUnderPostmaster &&
- parse->commandType == CMD_SELECT &&
+ (parse->commandType == CMD_SELECT ||
+ parse->commandType == CMD_PLAN_UTILITY) &&
!parse->hasModifyingCTE &&
max_parallel_workers_per_gather > 0 &&
!IsParallelWorker() &&
@@ -520,6 +522,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->resultRelations = glob->resultRelations;
result->nonleafResultRelations = glob->nonleafResultRelations;
result->rootResultRelations = glob->rootResultRelations;
+ result->resultVariable = parse->resultVariable;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks;
@@ -528,6 +531,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->paramExecTypes = glob->paramExecTypes;
/* utilityStmt should be null, but we might as well copy it */
result->utilityStmt = parse->utilityStmt;
+ result->schemaVariables = glob->schemaVariables;
result->stmt_location = parse->stmt_location;
result->stmt_len = parse->stmt_len;
@@ -659,6 +663,12 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
*/
pull_up_subqueries(root);
+ /*
+ * Check if some subquery uses schema variable. Flag hasSchemaVariable
+ * should be true if query or some subquery uses any schema variable.
+ */
+ pull_up_has_schema_variable(root);
+
/*
* If this is a simple UNION ALL query, flatten it into an appendrel. We
* do this now because it requires applying pull_up_subqueries to the leaf
@@ -2172,7 +2182,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
* inheritance_planner, add the ModifyTable node.
*/
- if (parse->commandType != CMD_SELECT && !inheritance_update)
+ if (parse->commandType != CMD_SELECT && parse->commandType != CMD_PLAN_UTILITY && !inheritance_update)
{
List *withCheckOptionLists;
List *returningLists;
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index f66f39d8c6..8db0ae0501 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -140,6 +140,7 @@ static List *set_returning_clause_references(PlannerInfo *root,
int rtoffset);
static bool extract_query_dependencies_walker(Node *node,
PlannerInfo *context);
+static bool pull_up_has_schema_variable_walker(Node *node, PlannerInfo *root);
/*****************************************************************************
*
@@ -1004,6 +1005,50 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
return plan;
}
+/*
+ * Search usage of schema variables in subqueries
+ */
+void
+pull_up_has_schema_variable(PlannerInfo *root)
+{
+ Query *query = root->parse;
+
+ if (query->hasSchemaVariable)
+ {
+ root->hasSchemaVariable = true;
+ }
+ else
+ {
+ (void) query_tree_walker(query,
+ pull_up_has_schema_variable_walker,
+ (void *) root, 0);
+ }
+}
+
+static bool
+pull_up_has_schema_variable_walker(Node *node, PlannerInfo *root)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Query))
+ {
+ Query *query = (Query *) node;
+
+ if (query->hasSchemaVariable)
+ {
+ root->hasSchemaVariable = true;
+ return false;
+ }
+
+ /* Recurse into subselects */
+ return query_tree_walker((Query *) node,
+ pull_up_has_schema_variable_walker,
+ (void *) root, 0);
+ }
+ return expression_tree_walker(node, pull_up_has_schema_variable_walker,
+ (void *) root);
+}
+
/*
* set_indexonlyscan_references
* Do set_plan_references processing on an IndexOnlyScan
@@ -1439,10 +1484,14 @@ fix_expr_common(PlannerInfo *root, Node *node)
/*
* fix_param_node
* Do set_plan_references processing on a Param
+ * Collect schema variables list and replace variable oid by
+ * index to collected list.
*
* If it's a PARAM_MULTIEXPR, replace it with the appropriate Param from
* root->multiexpr_params; otherwise no change is needed.
* Just for paranoia's sake, we make a copy of the node in either case.
+ *
+ * If it's a PARAM_VARIABLE, then we should to calculate paramid.
*/
static Node *
fix_param_node(PlannerInfo *root, Param *p)
@@ -1461,6 +1510,52 @@ fix_param_node(PlannerInfo *root, Param *p)
elog(ERROR, "unexpected PARAM_MULTIEXPR ID: %d", p->paramid);
return copyObject(list_nth(params, colno - 1));
}
+
+ if (p->paramkind == PARAM_VARIABLE)
+ {
+ ListCell *lc;
+ int n = 0;
+ bool found = false;
+
+ /* We will modify object */
+ p = (Param *) copyObject(p);
+
+ /*
+ * Now, we can actualize list of schema variables, and we can
+ * complete paramid parameter.
+ */
+ foreach(lc, root->glob->schemaVariables)
+ {
+ if (lfirst_oid(lc) == p->paramvarid)
+ {
+ p->paramid = n;
+ found = true;
+ break;
+ }
+ n += 1;
+ }
+
+ if (!found)
+ {
+ PlanInvalItem *inval_item = makeNode(PlanInvalItem);
+
+ /* paramid is still schema variable id */
+ inval_item->cacheId = VARIABLEOID;
+ inval_item->hashValue = GetSysCacheHashValue1(VARIABLEOID,
+ ObjectIdGetDatum(p->paramvarid));
+
+ /* Append this variable to global, register dependency */
+ root->glob->invalItems = lappend(root->glob->invalItems,
+ inval_item);
+ root->glob->schemaVariables = lappend_oid(root->glob->schemaVariables,
+ p->paramvarid);
+
+ p->paramid = n;
+ }
+
+ return (Node *) p;
+ }
+
return (Node *) copyObject(p);
}
@@ -1472,7 +1567,9 @@ fix_param_node(PlannerInfo *root, Param *p)
* replacing PARAM_MULTIEXPR Params, expanding PlaceHolderVars,
* replacing Aggref nodes that should be replaced by initplan output Params,
* looking up operator opcode info for OpExpr and related nodes,
- * and adding OIDs from regclass Const nodes into root->glob->relationOids.
+ * adding OIDs from regclass Const nodes into root->glob->relationOids,
+ * and replacing PARAM_VARIABLE paramid, that is oid of schema variable
+ * to offset to array of by query used schema variables.
*/
static Node *
fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset)
@@ -1485,7 +1582,8 @@ fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset)
if (rtoffset != 0 ||
root->multiexpr_params != NIL ||
root->glob->lastPHId != 0 ||
- root->minmax_aggs != NIL)
+ root->minmax_aggs != NIL ||
+ root->hasSchemaVariable)
{
return fix_scan_expr_mutator(node, &context);
}
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 8603feef2b..2923e3fcc7 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -71,6 +71,7 @@ preprocess_targetlist(PlannerInfo *root)
{
Query *parse = root->parse;
int result_relation = parse->resultRelation;
+ int result_variable = parse->resultVariable;
List *range_table = parse->rtable;
CmdType command_type = parse->commandType;
RangeTblEntry *target_rte = NULL;
@@ -96,6 +97,10 @@ preprocess_targetlist(PlannerInfo *root)
target_relation = heap_open(target_rte->relid, NoLock);
}
+ else if (result_variable)
+ {
+ Assert(command_type == CMD_PLAN_UTILITY);
+ }
else
Assert(command_type == CMD_SELECT);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index ee6f4cdf4d..f232c6cfd1 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1268,7 +1268,8 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
{
Param *param = (Param *) node;
- if (param->paramkind == PARAM_EXTERN)
+ if (param->paramkind == PARAM_EXTERN ||
+ param->paramkind == PARAM_VARIABLE)
return false;
if (param->paramkind != PARAM_EXEC ||
@@ -4813,7 +4814,7 @@ substitute_actual_parameters_mutator(Node *node,
{
if (node == NULL)
return NULL;
- if (IsA(node, Param))
+ if (IsA(node, Param) && ((Param *) node)->paramkind != PARAM_VARIABLE)
{
Param *param = (Param *) node;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8369e3ad62..fc0cf34c7d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1272,7 +1272,7 @@ get_relation_constraints(PlannerInfo *root,
* descriptor, instead of constraint exclusion which is driven by the
* individual partition's partition constraint.
*/
- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
+ if (enable_partition_pruning && root->parse->commandType != CMD_SELECT && root->parse->commandType != CMD_PLAN_UTILITY)
{
List *pcqual = RelationGetPartitionQual(relation);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c601b6d40d..86e21d519b 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,7 +25,10 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/namespace.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/schemavariable.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -44,6 +47,8 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -78,6 +83,8 @@ static Query *transformCreateTableAsStmt(ParseState *pstate,
CreateTableAsStmt *stmt);
static Query *transformCallStmt(ParseState *pstate,
CallStmt *stmt);
+static Query *transformLetStmt(ParseState *pstate,
+ LetStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown);
#ifdef RAW_EXPRESSION_COVERAGE_TEST
@@ -267,6 +274,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
+ case T_LetStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
@@ -327,6 +335,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
(CallStmt *) parseTree);
break;
+ case T_LetStmt:
+ result = transformLetStmt(pstate,
+ (LetStmt *) parseTree);
+ break;
+
default:
/*
@@ -367,6 +380,7 @@ analyze_requires_snapshot(RawStmt *parseTree)
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
+ case T_LetStmt:
result = true;
break;
@@ -454,6 +468,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
if (pstate->p_hasAggs)
parseCheckAggregates(pstate, qry);
+ qry->hasSchemaVariable = pstate->p_hasSchemaVariable;
+
assign_query_collations(pstate, qry);
return qry;
@@ -880,6 +896,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
qry->hasSubLinks = pstate->p_hasSubLinks;
+ qry->hasSchemaVariable = pstate->p_hasSchemaVariable;
assign_query_collations(pstate, qry);
@@ -1327,6 +1344,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
(LockingClause *) lfirst(l), false);
}
+ qry->hasSchemaVariable = pstate->p_hasSchemaVariable;
+
assign_query_collations(pstate, qry);
return qry;
@@ -1561,12 +1580,229 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
+ qry->hasSchemaVariable = pstate->p_hasSchemaVariable;
assign_query_collations(pstate, qry);
return qry;
}
+/*
+ * transformLetStmt -
+ * transform an Let Statement
+ */
+static Query *
+transformLetStmt(ParseState *pstate, LetStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ List *exprList = NIL;
+ List *exprListCoer = NIL;
+ List *indirection = NIL;
+ ListCell *lc;
+ Query *selectQuery;
+ int i = 0;
+
+ Oid varid;
+
+ ParseExprKind sv_expr_kind;
+ char *attrname = NULL;
+ bool not_unique;
+ bool is_rowtype;
+ Oid typid;
+ int32 typmod;
+ Oid collid;
+
+ AclResult aclresult;
+ List *names = NULL;
+ int indirection_start;
+
+ sv_expr_kind = pstate->p_expr_kind;
+ pstate->p_expr_kind = EXPR_KIND_LET;
+
+ /* There can't be any outer WITH to worry about */
+ Assert(pstate->p_ctenamespace == NIL);
+
+ names = NamesFromList(stmt->target);
+
+ varid = identify_variable(names, &attrname, ¬_unique);
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("target \"%s\" of LET command is ambiguous",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+ if (!OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("schema variable \"%s\" doesn't exists",
+ NameListToString(names)),
+ parser_errposition(pstate, stmt->location)));
+
+
+ qry->resultVariable = varid;
+
+ /* Simple behave, when LET xx = DEFAULT was used */
+ if (stmt->selectStmt == NULL)
+ {
+ if (attrname != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("only complete variable can be set to default"),
+ parser_errposition(pstate, stmt->location)));
+
+ qry->commandType = CMD_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ return qry;
+ }
+
+ /* Exec this command as utility */
+ qry->commandType = CMD_PLAN_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ get_schema_variable_type_typmod_collid(varid, &typid, &typmod, &collid);
+
+ is_rowtype = type_is_rowtype(typid);
+
+ if (attrname && !is_rowtype)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("target variable \"%s\" is not row type",
+ schema_variable_get_name(varid)),
+ parser_errposition(pstate, stmt->location)));
+
+ aclresult = pg_variable_aclcheck(varid, GetUserId(), ACL_WRITE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE, NameListToString(names));
+
+ selectQuery = transformStmt(pstate, stmt->selectStmt);
+
+ /* The grammar should have produced a SELECT */
+ if (!IsA(selectQuery, Query) ||
+ selectQuery->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in LET ... SELECT");
+
+ /*----------
+ * Generate an expression list for the LET that selects all the
+ * non-resjunk columns from the subquery.
+ *----------
+ */
+ exprList = NIL;
+ foreach(lc, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->resjunk)
+ continue;
+
+ exprList = lappend(exprList, tle->expr);
+ }
+
+ /*
+ * Because doesn't support pattern matching, don't allow multicolumn result
+ */
+ if (list_length(exprList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expression is not scalar value"),
+ parser_errposition(pstate,
+ exprLocation((Node *) exprList))));
+
+ indirection_start = list_length(names) - (attrname ? 1 : 0);
+ indirection = list_copy_tail(stmt->target, indirection_start);
+
+ exprListCoer = NIL;
+ foreach(lc, exprList)
+ {
+ Node *orig_expr = (Node*) lfirst(lc);
+ Oid exprtypid = exprType((Node *) orig_expr);
+ Param *param = makeNode(Param);
+ Expr *expr = NULL;
+
+ param->paramkind = PARAM_VARIABLE;
+ param->paramvarid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+
+ if (indirection != NULL)
+ {
+ bool targetIsArray;
+ char *targetName;
+
+ targetName = attrname != NULL ? attrname : get_schema_variable_name(varid);
+ targetIsArray = OidIsValid(get_element_type(typid));
+
+ pstate->p_hasSchemaVariable = true;
+
+ expr = (Expr *)
+ transformAssignmentIndirection(pstate,
+ (Node *) param,
+ targetName,
+ targetIsArray,
+ typid,
+ typmod,
+ InvalidOid,
+ list_head(indirection),
+ (Node *) orig_expr,
+ stmt->location);
+ }
+ else
+ expr = (Expr *)
+ coerce_to_target_type(pstate,
+ (Node *) orig_expr,
+ exprtypid,
+ typid, typmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ stmt->location);
+
+ if (expr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variable \"%s\" is of type %s,"
+ " but expression is of type %s",
+ schema_variable_get_name(varid),
+ format_type_be(typid),
+ format_type_be(exprtypid)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *) orig_expr))));
+
+ exprListCoer = lappend(exprListCoer, expr);
+ }
+
+ /*
+ * Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
+ */
+ qry->targetList = NIL;
+ foreach(lc, exprListCoer)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ tle = makeTargetEntry(expr,
+ i + 1,
+ FigureColname((Node *)expr),
+ false);
+ qry->targetList = lappend(qry->targetList, tle);
+ }
+
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+ qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+ qry->hasSchemaVariable = pstate->p_hasSchemaVariable;
+
+ assign_query_collations(pstate, qry);
+
+ pstate->p_expr_kind = sv_expr_kind;
+
+ return qry;
+}
+
/*
* transformSetOperationStmt -
* transforms a set-operations tree
@@ -1799,6 +2035,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
(LockingClause *) lfirst(l), false);
}
+ qry->hasSchemaVariable = pstate->p_hasSchemaVariable;
+
assign_query_collations(pstate, qry);
return qry;
@@ -2278,6 +2516,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
qry->hasSubLinks = pstate->p_hasSubLinks;
+ qry->hasSchemaVariable = pstate->p_hasSchemaVariable;
assign_query_collations(pstate, qry);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4bd2223f26..9f5229914b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -211,6 +211,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
JoinType jtype;
DropBehavior dbehavior;
OnCommitAction oncommit;
+ VariableEOXAction oneoxaction;
List *list;
Node *node;
Value *value;
@@ -257,8 +258,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSchemaVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -268,7 +269,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DropTransformStmt
DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
- ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
+ LetStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -400,6 +401,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
TriggerTransitions TriggerReferencing
publication_name_list
vacuum_relation_list opt_vacuum_relation_list
+ let_target
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -422,6 +424,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <ival> OptTemp
%type <ival> OptNoLog
%type <oncommit> OnCommitOption
+%type <oneoxaction> OnEOXActionOption
%type <ival> for_locking_strength
%type <node> for_locking_item
@@ -584,6 +587,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> optSchemaVarDefExpr
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -649,7 +653,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+ LEADING LEAKPROOF LEAST LEFT LET LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -687,8 +691,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -878,6 +882,7 @@ stmt :
| CreatePolicyStmt
| CreatePLangStmt
| CreateSchemaStmt
+ | CreateSchemaVarStmt
| CreateSeqStmt
| CreateStmt
| CreateSubscriptionStmt
@@ -917,6 +922,7 @@ stmt :
| ImportForeignSchemaStmt
| IndexStmt
| InsertStmt
+ | LetStmt
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
@@ -1808,7 +1814,12 @@ DiscardStmt:
n->target = DISCARD_SEQUENCES;
$$ = (Node *) n;
}
-
+ | DISCARD VARIABLES
+ {
+ DiscardStmt *n = makeNode(DiscardStmt);
+ n->target = DISCARD_VARIABLES;
+ $$ = (Node *) n;
+ }
;
@@ -4479,6 +4490,48 @@ create_extension_opt_item:
}
;
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE VARIABLE varname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSchemaVarStmt:
+ CREATE OptTemp VARIABLE qualified_name opt_as Typename opt_collate_clause optSchemaVarDefExpr OnEOXActionOption
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $4->relpersistence = $2;
+ n->variable = $4;
+ n->typeName = $6;
+ n->collClause = (CollateClause *) $7;
+ n->defexpr = $8;
+ n->eoxaction = $9;
+ n->if_not_exists = false;
+ $$ = (Node *) n;
+ }
+ | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename opt_collate_clause optSchemaVarDefExpr OnEOXActionOption
+ {
+ CreateSchemaVarStmt *n = makeNode(CreateSchemaVarStmt);
+ $7->relpersistence = $2;
+ n->variable = $7;
+ n->typeName = $9;
+ n->collClause = (CollateClause *) $10;
+ n->defexpr = $11;
+ n->eoxaction = $12;
+ n->if_not_exists = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+optSchemaVarDefExpr: DEFAULT b_expr { $$ = $2; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+OnEOXActionOption: ON COMMIT DROP { $$ = VARIABLE_EOX_DROP; }
+ | ON TRANSACTION END_P RESET { $$ = VARIABLE_EOX_RESET; }
+ | /*EMPTY*/ { $$ = VARIABLE_EOX_NOOP; }
+
/*****************************************************************************
*
* ALTER EXTENSION name UPDATE [ TO version ]
@@ -6340,6 +6393,7 @@ drop_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name_list */
@@ -6609,6 +6663,7 @@ comment_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -6747,6 +6802,7 @@ security_label_type_any_name:
| TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/* object types taking name */
@@ -7168,6 +7224,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| ALL TABLES IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7208,6 +7272,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
;
@@ -7368,6 +7440,7 @@ defacl_privilege_target:
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
;
@@ -8964,6 +9037,25 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newname = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
opt_column: COLUMN { $$ = COLUMN; }
@@ -9282,6 +9374,25 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newschema = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
/*****************************************************************************
@@ -9517,6 +9628,14 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
@@ -10698,6 +10817,7 @@ ExplainableStmt:
| CreateMatViewStmt
| RefreshMatViewStmt
| ExecuteStmt /* by default all are $$=$1 */
+ | LetStmt
;
explain_option_list:
@@ -10755,6 +10875,7 @@ PreparableStmt:
| InsertStmt
| UpdateStmt
| DeleteStmt /* by default all are $$=$1 */
+ | LetStmt
;
/*****************************************************************************
@@ -11153,6 +11274,50 @@ opt_hold: /* EMPTY */ { $$ = 0; }
| WITHOUT HOLD { $$ = 0; }
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * LET STATEMENTS
+ *
+ *****************************************************************************/
+LetStmt: LET let_target '=' a_expr
+ {
+ LetStmt *n = makeNode(LetStmt);
+
+ n->target = $2;
+
+ if (!IsA((Node *) $4, SetToDefault))
+ {
+ SelectStmt *select = makeNode(SelectStmt);
+ ResTarget *res = makeNode(ResTarget);
+
+ /* Create target list for implicit query */
+ res->name = NULL;
+ res->indirection = NIL;
+ res->val = (Node *) $4;
+ res->location = @4;
+
+ select->targetList = list_make1(res);
+ n->selectStmt = (Node *) select;
+ }
+ else
+ n->selectStmt = NULL;
+
+ n->location = @2;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+let_target:
+ ColId opt_indirection
+ {
+ $$ = list_make1(makeString($1));
+ if ($2)
+ $$ = list_concat($$,
+ check_indirection($2, yyscanner));
+ }
+
/*****************************************************************************
*
* QUERY:
@@ -15132,6 +15297,7 @@ unreserved_keyword:
| LARGE_P
| LAST_P
| LEAKPROOF
+ | LET
| LEVEL
| LISTEN
| LOAD
@@ -15280,6 +15446,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 61727e1d71..6823612fba 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -349,6 +349,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
Assert(false); /* can't happen */
break;
case EXPR_KIND_OTHER:
+ case EXPR_KIND_LET:
/*
* Accept aggregate/grouping here; caller must throw error if
@@ -465,6 +466,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
if (isAgg)
err = _("aggregate functions are not allowed in DEFAULT expressions");
@@ -879,6 +881,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("window functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -902,6 +905,8 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_CALL_ARGUMENT:
err = _("window functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("window functions are not allowed in LET statement");
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a9b6..54565834fd 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -37,6 +38,7 @@
#include "utils/date.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
+#include "utils/typcache.h"
#include "utils/xml.h"
@@ -116,6 +118,9 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
+static Node *makeParamSchemaVariable(ParseState *pstate,
+ Oid varid, Oid typid, int32 typmod, Oid collid,
+ char *attrname, int location);
static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
int location);
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
@@ -512,6 +517,9 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
+ Oid varid = InvalidOid;
+ char *attrname = NULL;
+ bool not_unique;
RangeTblEntry *rte;
int levels_up;
enum
@@ -749,6 +757,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break;
}
+ varid = identify_variable(cref->fields, &attrname, ¬_unique);
+
+ if (not_unique)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("schema variable reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+
/*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
@@ -773,6 +790,72 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
parser_errposition(pstate, cref->location)));
}
+ if (OidIsValid(varid))
+ {
+ Oid typid;
+ int32 typmod;
+ Oid collid;
+
+ get_schema_variable_type_typmod_collid(varid, &typid, &typmod, &collid);
+
+ if (node != NULL)
+ {
+ /*
+ * some collision can be solved simply here to reduce errors
+ * based on simply existence of some variables. Often error
+ * can be using alias same like variable name. In this case,
+ * when we found column reference, and we found reference to
+ * possible composite variable, but the variable is not composite,
+ * then we can ignore the variable as simply improper, and we
+ * use column reference only.
+ */
+ if (attrname)
+ {
+ if (type_is_rowtype(typid))
+ {
+ TupleDesc tupdesc;
+ bool found = false;
+ int i;
+
+ /* slow part, I hope it will not be to often */
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ if (namestrcmp(&(TupleDescAttr(tupdesc, i)->attname), attrname) == 0 &&
+ !TupleDescAttr(tupdesc, i)->attisdropped)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ FreeTupleDesc(tupdesc);
+
+ /* there are not composite variable with this field */
+ if (!found)
+ varid = InvalidOid;
+ }
+ else
+ /* there are not composite variable with this name */
+ varid = InvalidOid;
+ }
+
+ /* Raise error if varid is still valid. It should be really amigonuous */
+ if (OidIsValid(varid))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ errdetail("The qualified identifier can be column reference or schema variable reference"),
+ parser_errposition(pstate, cref->location)));
+ }
+
+ if (OidIsValid(varid))
+ node = makeParamSchemaVariable(pstate,
+ varid, typid, typmod, collid,
+ attrname, cref->location);
+ }
+
/*
* Throw error if no translation found.
*/
@@ -807,6 +890,74 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return node;
}
+/*
+ * Generate param variable for reference to schema variable
+ */
+static Node *
+makeParamSchemaVariable(ParseState *pstate,
+ Oid varid, Oid typid, int32 typmod, Oid collid,
+ char *attrname, int location)
+{
+ Param *param;
+
+ param = makeNode(Param);
+
+ param->paramkind = PARAM_VARIABLE;
+ param->paramvarid = varid;
+ param->paramtype = typid;
+ param->paramtypmod = typmod;
+ param->paramcollid = collid;
+
+ /*
+ * There are two access to schema variables - direct, used by simple
+ * plpgsql expressions, where there are not necessary to emulate stability.
+ * Buffered access is used elsewhere. We should to ensure stable values,
+ * and because schema variables are global, then we should to work with
+ * copied values instead direct access to variables. For direct access
+ * the varid is best for access. For buffered access we need to assign
+ * index to buffer - later, when we will know what variables are used.
+ * Now, we just remember, so we use schema variables.
+ */
+ pstate->p_hasSchemaVariable = true;
+
+ if (attrname != NULL)
+ {
+ TupleDesc tupdesc;
+ int i;
+
+ tupdesc = lookup_rowtype_tupdesc(typid, typmod);
+
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+
+ if (strcmp(attrname, NameStr(att->attname)) == 0 &&
+ !att->attisdropped)
+ {
+ /* Success, so generate a FieldSelect expression */
+ FieldSelect *fselect = makeNode(FieldSelect);
+
+ fselect->arg = (Expr *) param;
+ fselect->fieldnum = i + 1;
+ fselect->resulttype = att->atttypid;
+ fselect->resulttypmod = att->atttypmod;
+ /* save attribute's collation for parse_collate.c */
+ fselect->resultcollid = att->attcollation;
+
+ ReleaseTupleDesc(tupdesc);
+ return (Node *) fselect;
+ }
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("could not identify column \"%s\" in variable", attrname),
+ parser_errposition(pstate, location)));
+ }
+
+ return (Node *) param;
+}
+
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
@@ -1818,6 +1969,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
case EXPR_KIND_VALUES_SINGLE:
+ case EXPR_KIND_LET:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
@@ -1826,6 +1978,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -3460,6 +3613,7 @@ ParseExprKindName(ParseExprKind exprKind)
return "CHECK";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
return "DEFAULT";
case EXPR_KIND_INDEX_EXPRESSION:
return "index expression";
@@ -3475,6 +3629,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "PARTITION BY";
case EXPR_KIND_CALL_ARGUMENT:
return "CALL";
+ case EXPR_KIND_LET:
+ return "LET";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 44257154b8..b2c9900e00 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2347,6 +2347,7 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
+ case EXPR_KIND_VARIABLE_DEFAULT:
err = _("set-returning functions are not allowed in DEFAULT expressions");
break;
case EXPR_KIND_INDEX_EXPRESSION:
@@ -2370,6 +2371,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_CALL_ARGUMENT:
err = _("set-returning functions are not allowed in CALL arguments");
break;
+ case EXPR_KIND_LET:
+ err = _("set-returning functions are not allowed in CALL arguments");
+ break;
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4932e58022..c60fe011f7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -35,16 +35,6 @@
static void markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
Var *var, int levelsup);
-static Node *transformAssignmentIndirection(ParseState *pstate,
- Node *basenode,
- const char *targetName,
- bool targetIsArray,
- Oid targetTypeId,
- int32 targetTypMod,
- Oid targetCollation,
- ListCell *indirection,
- Node *rhs,
- int location);
static Node *transformAssignmentSubscripts(ParseState *pstate,
Node *basenode,
const char *targetName,
@@ -672,7 +662,7 @@ updateTargetListEntry(ParseState *pstate,
* might want to decorate indirection cells with their own location info,
* in which case the location argument could probably be dropped.)
*/
-static Node *
+Node *
transformAssignmentIndirection(ParseState *pstate,
Node *basenode,
const char *targetName,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index d830569641..c27aecedb5 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3359,7 +3359,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
* get executed. Also, utilities aren't rewritten at all (do we still
* need that check?)
*/
- if (event != CMD_SELECT && event != CMD_UTILITY)
+ if (event != CMD_SELECT && event != CMD_UTILITY && event != CMD_PLAN_UTILITY)
{
int result_relation;
RangeTblEntry *rt_entry;
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 61ef396d8a..6a068af799 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -212,7 +212,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
}
/*
- * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
+ * For SELECT, LET, UPDATE and DELETE, add security quals to enforce the USING
* policies. These security quals control access to existing table rows.
* Restrictive policies are combined together using AND, and permissive
* policies are combined together using OR.
@@ -222,6 +222,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
&restrictive_policies);
if (commandType == CMD_SELECT ||
+ commandType == CMD_PLAN_UTILITY ||
commandType == CMD_UPDATE ||
commandType == CMD_DELETE)
add_security_quals(rt_index,
@@ -423,6 +424,7 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
switch (cmd)
{
case CMD_SELECT:
+ case CMD_PLAN_UTILITY:
if (policy->polcmd == ACL_SELECT_CHR)
cmd_matches = true;
break;
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index c95a4d519d..47fb0f38b1 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -37,6 +37,7 @@
#include "executor/functions.h"
#include "executor/tqueue.h"
#include "executor/tstoreReceiver.h"
+#include "executor/svariableReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/portal.h"
@@ -143,6 +144,9 @@ CreateDestReceiver(CommandDest dest)
case DestTupleQueue:
return CreateTupleQueueDestReceiver(NULL);
+
+ case DestVariable:
+ return CreateVariableDestReceiver();
}
/* should never get here */
@@ -178,6 +182,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -222,6 +227,7 @@ NullCommand(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
@@ -268,6 +274,7 @@ ReadyForQuery(CommandDest dest)
case DestSQLFunction:
case DestTransientRel:
case DestTupleQueue:
+ case DestVariable:
break;
}
}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7a9ada2c71..e711cd3366 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -869,6 +869,7 @@ pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
stmt->utilityStmt = query->utilityStmt;
stmt->stmt_location = query->stmt_location;
stmt->stmt_len = query->stmt_len;
+ stmt->resultVariable = query->resultVariable;
}
else
{
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 66cc5c35c6..cd07fcb6ee 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -86,6 +86,9 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
qd->queryEnv = queryEnv;
qd->instrument_options = instrument_options; /* instrumentation wanted? */
+ qd->num_schema_variables = 0;
+ qd->schema_variables = NULL;
+
/* null these fields until set by ExecutorStart */
qd->tupDesc = NULL;
qd->estate = NULL;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b5804f64ad..da09893103 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
+#include "commands/schemavariable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
@@ -344,7 +345,7 @@ ProcessUtility(PlannedStmt *pstmt,
char *completionTag)
{
Assert(IsA(pstmt, PlannedStmt));
- Assert(pstmt->commandType == CMD_UTILITY);
+ Assert(pstmt->commandType == CMD_UTILITY || pstmt->commandType == CMD_PLAN_UTILITY);
Assert(queryString != NULL); /* required as of 8.4 */
/*
@@ -915,6 +916,21 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
}
+ case T_LetStmt:
+ {
+ if (pstmt->commandType == CMD_UTILITY)
+ doLetStmtReset(pstmt);
+ else
+ {
+ Assert(pstmt->commandType == CMD_PLAN_UTILITY);
+ doLetStmtEval(pstmt, params, queryEnv, queryString);
+ }
+
+ if (completionTag)
+ strcpy(completionTag, "LET");
+ }
+ break;
+
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
@@ -1221,6 +1237,10 @@ ProcessUtilitySlow(ParseState *pstate,
}
break;
+ case T_CreateSchemaVarStmt:
+ address = DefineSchemaVariable(pstate, (CreateSchemaVarStmt *) parsetree);
+ break;
+
/*
* ************* object creation / destruction **************
*/
@@ -2055,6 +2075,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_STATISTIC_EXT:
tag = "ALTER STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "ALTER VARIABLE";
+ break;
default:
tag = "???";
break;
@@ -2104,6 +2127,10 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ case T_LetStmt:
+ tag = "LET";
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
@@ -2358,6 +2385,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_VARIABLE:
+ tag = "DROP VARIABLE";
+ break;
default:
tag = "???";
}
@@ -2639,6 +2669,9 @@ CreateCommandTag(Node *parsetree)
case DISCARD_SEQUENCES:
tag = "DISCARD SEQUENCES";
break;
+ case DISCARD_VARIABLES:
+ tag = "DISCARD VARIABLES";
+ break;
default:
tag = "???";
}
@@ -2844,6 +2877,7 @@ CreateCommandTag(Node *parsetree)
tag = "DELETE";
break;
case CMD_UTILITY:
+ case CMD_PLAN_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
default:
@@ -2915,6 +2949,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_CreateSchemaVarStmt:
+ tag = "CREATE VARIABLE";
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -2961,6 +2999,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ case T_LetStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
/* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a45e093de7..952c0d9628 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -315,6 +315,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_READ_CHR:
+ read = ACL_READ;
+ break;
+ case ACL_WRITE_CHR:
+ read = ACL_WRITE;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -808,6 +814,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -903,6 +913,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'T':
objtype = OBJECT_TYPE;
break;
+ case 'V':
+ objtype = OBJECT_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
}
@@ -1627,6 +1640,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CONNECT;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
+ if (pg_strcasecmp(priv_type, "READ") == 0)
+ return ACL_READ;
+ if (pg_strcasecmp(priv_type, "WRITE") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1721,6 +1738,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 4c2408d655..ec10cb4468 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -38,6 +38,7 @@
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
#include "common/keywords.h"
@@ -7395,6 +7396,14 @@ get_parameter(Param *param, deparse_context *context)
return;
}
+ /* translate paramvarid to schema variable name */
+ if (param->paramkind == PARAM_VARIABLE)
+ {
+ appendStringInfo(context->buf, "%s",
+ schema_variable_get_name(param->paramvarid));
+ return;
+ }
+
/*
* Not PARAM_EXEC, or couldn't find referent: just print $N.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bba595ad1d..858a6dd4be 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1691,6 +1691,18 @@ get_relname_relid(const char *relname, Oid relnamespace)
ObjectIdGetDatum(relnamespace));
}
+/*
+ * get_varname_varid
+ * Given name and namespace of variable, look up the OID.
+ */
+Oid
+get_varname_varid(const char *varname, Oid varnamespace)
+{
+ return GetSysCacheOid2(VARIABLENAMENSP,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(varnamespace));
+}
+
#ifdef NOT_USED
/*
* get_relnatts
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b381782a3..35dc32f649 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -73,6 +73,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "utils/rel.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
@@ -968,6 +969,28 @@ static const struct cachedesc cacheinfo[] = {
0
},
2
+ },
+ {VariableRelationId, /* VARIABLENAMENSP */
+ VariableNameNspIndexId,
+ 2,
+ {
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ 0,
+ 0
+ },
+ 8
+ },
+ {VariableRelationId, /* VARIABLEOID */
+ VariableObjectIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
}
};
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 9b5869add8..c4e4d10c6a 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -296,6 +296,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading variables\n");
+ getVariables(fout);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 36e3383b85..58d15af7b1 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3465,6 +3465,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(type, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(type, "STATISTICS") == 0 ||
+ strcmp(type, "VARIABLE") == 0 ||
/* non-schema-specified objects */
strcmp(type, "DATABASE") == 0 ||
strcmp(type, "PROCEDURAL LANGUAGE") == 0 ||
@@ -3664,7 +3665,8 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
strcmp(te->desc, "SERVER") == 0 ||
strcmp(te->desc, "STATISTICS") == 0 ||
strcmp(te->desc, "PUBLICATION") == 0 ||
- strcmp(te->desc, "SUBSCRIPTION") == 0)
+ strcmp(te->desc, "SUBSCRIPTION") == 0 ||
+ strcmp(te->desc, "VARIABLE") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index f0ea83e6a9..c365d9fcf8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -258,6 +258,7 @@ static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
+static void dumpVariable(Archive *fout, VariableInfo *varinfo);
static void dumpDatabase(Archive *AH);
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
const char *dbname, Oid dboid);
@@ -4224,6 +4225,220 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+/*
+ * getVariables
+ * get information about variables
+ */
+void
+getVariables(Archive *fout)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer init_acl_subquery = createPQExpBuffer();
+ PQExpBuffer init_racl_subquery = createPQExpBuffer();
+ PGresult *res;
+ VariableInfo *varinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_varname;
+ int i_varnamespace;
+ int i_vartype;
+ int i_vartypname;
+ int i_vardefexpr;
+ int i_rolname;
+ int i_varacl;
+ int i_rvaracl;
+ int i_initvaracl;
+ int i_initrvaracl;
+ int i_vareoxaction;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion <= 110000)
+ return;
+
+ acl_subquery = createPQExpBuffer();
+ racl_subquery = createPQExpBuffer();
+ init_acl_subquery = createPQExpBuffer();
+ init_racl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
+ init_racl_subquery, "v.varacl", "v.varowner", "'V'",
+ dopt->binary_upgrade);
+
+ query = createPQExpBuffer();
+
+ resetPQExpBuffer(query);
+
+ /* Get the variables in current database. */
+ appendPQExpBuffer(query,
+ "SELECT v.tableoid, v.oid, v.varname, "
+ "v.vareoxaction, "
+ "v.varnamespace, "
+ "(%s varowner) AS rolname, "
+ "%s as varacl, "
+ "%s as rvaracl, "
+ "%s as initvaracl, "
+ "%s as initrvaracl, "
+ "v.vartype, "
+ "pg_catalog.format_type(v.vartype, v.vartypmod) as vartypname, "
+ "pg_catalog.pg_get_expr(v.vardefexpr,0) as vardefexpr "
+ "FROM pg_variable v "
+ "LEFT JOIN pg_init_privs pip "
+ "ON (v.oid = pip.objoid "
+ "AND pip.classoid = 'pg_variable'::regclass "
+ "AND pip.objsubid = 0)",
+ username_subquery,
+ acl_subquery->data,
+ racl_subquery->data,
+ init_acl_subquery->data,
+ init_racl_subquery->data);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(init_acl_subquery);
+ destroyPQExpBuffer(init_racl_subquery);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_varname = PQfnumber(res, "varname");
+ i_varnamespace = PQfnumber(res, "varnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_vartype = PQfnumber(res, "vartype");
+ i_vartypname = PQfnumber(res, "vartypname");
+ i_vareoxaction = PQfnumber(res, "vareoxaction");
+ i_vardefexpr = PQfnumber(res, "vardefexpr");
+ i_varacl = PQfnumber(res, "varacl");
+ i_rvaracl = PQfnumber(res, "rvaracl");
+ i_initvaracl = PQfnumber(res, "initvaracl");
+ i_initrvaracl = PQfnumber(res, "initrvaracl");
+
+ varinfo = pg_malloc(ntups * sizeof(VariableInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ TypeInfo *vtype;
+
+ varinfo[i].dobj.objType = DO_VARIABLE;
+ varinfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ varinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&varinfo[i].dobj);
+ varinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_varname));
+ varinfo[i].dobj.namespace =
+ findNamespace(fout,
+ atooid(PQgetvalue(res, i, i_varnamespace)));
+
+ varinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ varinfo[i].vartype = atooid(PQgetvalue(res, i, i_vartype));
+ varinfo[i].vartypname = pg_strdup(PQgetvalue(res, i, i_vartypname));
+
+ varinfo[i].vareoxaction = pg_strdup(PQgetvalue(res, i, i_vareoxaction));
+
+ varinfo[i].varacl = pg_strdup(PQgetvalue(res, i, i_varacl));
+ varinfo[i].rvaracl = pg_strdup(PQgetvalue(res, i, i_rvaracl));
+ varinfo[i].initvaracl = pg_strdup(PQgetvalue(res, i, i_initvaracl));
+ varinfo[i].initrvaracl = pg_strdup(PQgetvalue(res, i, i_initrvaracl));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ /* Do not try to dump ACL if no ACL exists. */
+ if (PQgetisnull(res, i, i_varacl) && PQgetisnull(res, i, i_rvaracl) &&
+ PQgetisnull(res, i, i_initvaracl) &&
+ PQgetisnull(res, i, i_initrvaracl))
+ varinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+
+ if (PQgetisnull(res, i, i_vardefexpr))
+ varinfo[i].vardefexpr = NULL;
+ else
+ varinfo[i].vardefexpr = pg_strdup(PQgetvalue(res, i, i_vardefexpr));
+
+ if (strlen(varinfo[i].rolname) == 0)
+ write_msg(NULL, "WARNING: owner of variable \"%s\" appears to be invalid\n",
+ varinfo[i].dobj.name);
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(varinfo[i].dobj), fout);
+
+ vtype = findTypeByOid(varinfo[i].vartype);
+ addObjectDependency(&varinfo[i].dobj, vtype->dobj.dumpId);
+ }
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpVariable
+ * dump the definition of the given variables
+ */
+static void
+dumpVariable(Archive *fout, VariableInfo *varinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ const char *varname;
+ const char *vartypname;
+ const char *vardefexpr;
+ const char *vareoxaction;
+
+ /* Skip if not to be dumped */
+ if (!varinfo->dobj.dump || dopt->dataOnly)
+ return;
+
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ varname = fmtQualifiedDumpable(varinfo);
+ vartypname = varinfo->vartypname;
+ vardefexpr = varinfo->vardefexpr;
+ vareoxaction = varinfo->vareoxaction;
+
+ appendPQExpBuffer(delq, "DROP VARIABLE %s;\n",
+ varname);
+
+ appendPQExpBuffer(query, "CREATE VARIABLE %s AS %s",
+ varname, vartypname);
+
+ if (vardefexpr)
+ appendPQExpBuffer(query, " DEFAULT %s",
+ vardefexpr);
+
+ if (strcmp(vareoxaction, "d") == 0)
+ appendPQExpBuffer(query, " ON COMMIT DROP");
+ else if (strcmp(vareoxaction, "r") == 0)
+ appendPQExpBuffer(query, " ON TRANSACTION END RESET");
+
+ appendPQExpBuffer(query, ";\n");
+
+ ArchiveEntry(fout, varinfo->dobj.catId, varinfo->dobj.dumpId,
+ varinfo->dobj.name,
+ NULL,
+ NULL,
+ varinfo->rolname, false,
+ "VARIABLE", SECTION_PRE_DATA,
+ query->data, delq->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ if (varinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, "VARIABLE", varname,
+ NULL, varinfo->rolname,
+ varinfo->dobj.catId, 0, varinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -9791,6 +10006,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_VARIABLE:
+ dumpVariable(fout, (VariableInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -17877,6 +18095,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_OPFAMILY:
case DO_COLLATION:
case DO_CONVERSION:
+ case DO_VARIABLE:
case DO_TABLE:
case DO_ATTRDEF:
case DO_PROCLANG:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 1448005f30..5471e667fc 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -84,7 +84,8 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_VARIABLE
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -625,6 +626,23 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/*
+ * The VariableInfo struct is used to represent schema variables
+ */
+typedef struct _VariableInfo
+{
+ DumpableObject dobj;
+ Oid vartype;
+ char *vartypname;
+ char *rolname; /* name of owner, or empty string */
+ char *vareoxaction;
+ char *vardefexpr;
+ char *varacl;
+ char *rvaracl;
+ char *initvaracl;
+ char *initrvaracl;
+} VariableInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -725,5 +743,6 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern void getVariables(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 6227a8fd26..969a021771 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -1477,6 +1477,10 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POST-DATA BOUNDARY (ID %d)",
obj->dumpId);
return;
+ case DO_VARIABLE:
+ snprintf(buf, bufsize,
+ "VARIABLE %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
}
/* shouldn't get here */
snprintf(buf, bufsize,
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index ec751a7c23..2a67766ed4 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2601,6 +2601,38 @@ my %tests = (
},
},
+ 'CREATE VARIABLE test_variable' => {
+ all_runs => 1,
+ catch_all => 'CREATE ... commands',
+ create_order => 61,
+ create_sql => 'CREATE VARIABLE dump_test.variable AS integer DEFAULT 0;',
+ regexp => qr/^
+ \QCREATE VARIABLE dump_test.variable AS integer DEFAULT 0;\E/xm,
+ like => {
+ binary_upgrade => 1,
+ clean => 1,
+ clean_if_exists => 1,
+ createdb => 1,
+ defaults => 1,
+ exclude_test_table => 1,
+ exclude_test_table_data => 1,
+ no_blobs => 1,
+ no_privs => 1,
+ no_owner => 1,
+ only_dump_test_schema => 1,
+ pg_dumpall_dbprivs => 1,
+ schema_only => 1,
+ section_pre_data => 1,
+ test_schema_plus_blobs => 1,
+ with_oids => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_test_table => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1,
+ role => 1,
+ section_post_data => 1, }, },
+
'CREATE VIEW test_view' => {
create_order => 61,
create_sql => 'CREATE VIEW dump_test.test_view
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..73a752fd7e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -853,6 +853,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
break;
}
break;
+ case 'V': /* Variables */
+ success = listVariables(pattern, show_verbose);
+ break;
case 'x': /* Extensions */
if (show_verbose)
success = listExtensionContents(pattern);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4ca0db1d0c..fd4e1285d9 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4198,6 +4198,84 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
return true;
}
+/*
+ * \dV
+ *
+ * listVariables()
+ */
+bool
+listVariables(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false, false, false, false, false};
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname as \"%s\",\n"
+ " v.varname as \"%s\",\n"
+ " pg_catalog.format_type(v.vartype, v.vartypmod) as \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(v.varowner) as \"%s\",\n"
+ " pg_catalog.pg_get_expr(v.vardefexpr, 0) as \"%s\",\n"
+ " CASE v.vareoxaction\n"
+ " WHEN 'd' THEN 'ON COMMIT DROP'\n"
+ " WHEN 'r' THEN 'ON TRANSACTION END RESET' END as \"%s\"\n",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Type"),
+ gettext_noop("Owner"),
+ gettext_noop("Default"),
+ gettext_noop("Transaction end action"));
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_variable v"
+ "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = v.varnamespace");
+
+ appendPQExpBufferStr(&buf, "\nWHERE true\n");
+ if (!pattern)
+ appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
+ " AND n.nspname <> 'information_schema'\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "v.varname", NULL,
+ "pg_catalog.pg_variable_is_visible(v.oid)");
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ /*
+ * Most functions in this file are content to print an empty table when
+ * there are no matching objects. We intentionally deviate from that
+ * here, but only in !quiet mode, for historical reasons.
+ */
+ if (PQntuples(res) == 0 && !pset.quiet)
+ {
+ if (pattern)
+ psql_error("Did not find any schema variable named \"%s\".\n",
+ pattern);
+ else
+ psql_error("Did not find any schema variables.\n");
+ }
+ else
+ {
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of variables");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+ }
+
+ PQclear(res);
+ return true;
+}
/*
* \dFp
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..ecc4e3a531 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
/* \dt, \di, \ds, \dS, etc. */
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
+/* \dV */
+extern bool listVariables(const char *pattern, bool varbose);
+
/* \dD */
extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 586aebddd3..5e0175b384 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
@@ -257,6 +257,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[S+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dV [PATTERN] list variables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 7549b40192..6ce9a3d6ec 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -610,6 +610,22 @@ static const SchemaQuery Query_for_list_of_statistics = {
.result = "pg_catalog.quote_ident(s.stxname)",
};
+static const SchemaQuery Query_for_list_of_variables = {
+ /* min_server_version */
+ 0,
+ /* catname */
+ "pg_catalog.pg_variable v",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_variable_is_visible(v.oid)",
+ /* namespace */
+ "v.varnamespace",
+ /* result */
+ "pg_catalog.quote_ident(v.varname)",
+ /* qualresult */
+ NULL
+};
/*
* Queries to get lists of names of various kinds of things, possibly
@@ -1054,6 +1070,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
{"USER MAPPING FOR", NULL, NULL, NULL},
+ {"VARIABLE", NULL, NULL, &Query_for_list_of_variables},
{"VIEW", NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
@@ -1409,7 +1426,7 @@ psql_completion(const char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
- "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LET", "LISTEN", "LOAD", "LOCK",
"MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
"RESET", "REVOKE", "ROLLBACK",
@@ -1426,9 +1443,9 @@ psql_completion(const char *text, int start, int end)
"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
- "\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+ "\\dm", "\\dn", "\\do", "\\dO", "\\dp"
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+ "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", "\\dV",
"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
"\\endif", "\\errverbose", "\\ev",
"\\f",
@@ -1793,6 +1810,9 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
else if (Matches4("ALTER", "SYSTEM", "SET", MatchAny))
COMPLETE_WITH_CONST("TO");
+ /* ALTER VARIABLE <name> */
+ else if (Matches3("ALTER", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
/* ALTER VIEW <name> */
else if (Matches3("ALTER", "VIEW", MatchAny))
COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
@@ -2643,6 +2663,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
COMPLETE_WITH_LIST2("GROUP", "ROLE");
+/* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
+ /* Complete CREATE VARIABLE <name> with AS */
+ else if (TailMatches3("CREATE", "VARIABLE", MatchAny))
+ COMPLETE_WITH_CONST("AS");
+ /* Complete CREATE VARIABLE <name> with AS types*/
+ else if (TailMatches4("CREATE", "VARIABLE", MatchAny, "AS"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE VIEW <name> with AS */
else if (TailMatches3("CREATE", "VIEW", MatchAny))
@@ -2696,7 +2724,7 @@ psql_completion(const char *text, int start, int end)
/* DISCARD */
else if (Matches1("DISCARD"))
- COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP");
+ COMPLETE_WITH_LIST5("ALL", "PLANS", "SEQUENCES", "TEMP", "VARIABLES");
/* DO */
else if (Matches1("DO"))
@@ -2798,6 +2826,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP VARIABLE */
+ else if (Matches2("DROP", "VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ else if (Matches3("DROP", "VARIABLE", MatchAny))
+ COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -2808,14 +2842,14 @@ psql_completion(const char *text, int start, int end)
* Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "ANALYZE", "VERBOSE");
+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "ANALYZE", "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "ANALYZE"))
- COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
- "VERBOSE");
+ COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
+ "VERBOSE", "LET");
else if (Matches2("EXPLAIN", "VERBOSE") ||
Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
- COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
+ COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "LET");
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
@@ -2924,6 +2958,7 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'ALL ROUTINES IN SCHEMA'"
" UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
" UNION SELECT 'ALL TABLES IN SCHEMA'"
+ " UNION SELECT 'ALL VARIABLES IN SCHEMA'"
" UNION SELECT 'DATABASE'"
" UNION SELECT 'DOMAIN'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
@@ -2937,14 +2972,16 @@ psql_completion(const char *text, int start, int end)
" UNION SELECT 'SEQUENCE'"
" UNION SELECT 'TABLE'"
" UNION SELECT 'TABLESPACE'"
- " UNION SELECT 'TYPE'");
+ " UNION SELECT 'TYPE'"
+ " UNION SELECT 'VARIABLE'");
}
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
- COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA",
+ COMPLETE_WITH_LIST6("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
- "TABLES IN SCHEMA");
+ "TABLES IN SCHEMA",
+ "VARIABLES IN SCHEMA");
else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
@@ -2978,6 +3015,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches1("TYPE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+ else if (TailMatches1("VARIABLE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
COMPLETE_WITH_CONST("TO");
else
@@ -3130,7 +3169,7 @@ psql_completion(const char *text, int start, int end)
/* PREPARE xx AS */
else if (Matches3("PREPARE", MatchAny, "AS"))
- COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
+ COMPLETE_WITH_LIST5("SELECT", "UPDATE", "INSERT", "DELETE FROM", "LET");
/*
* PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -3353,6 +3392,14 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
COMPLETE_WITH_CONST("=");
+/* LET --- can be inside EXPLAIN, PREPARE etc */
+ /* If prev. word is LET suggest a list of variables */
+ else if (TailMatches1("LET"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
+ /* Complete LET <variable> with "=" */
+ else if (TailMatches2("LET", MatchAny))
+ COMPLETE_WITH_CONST("=");
+
/* USER MAPPING */
else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 46c271a46c..3e38a05e55 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -180,7 +180,8 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_VARIABLE /* pg_variable */
} ObjectClass;
#define LAST_OCLASS OCLASS_TRANSFORM
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 254fbef1f7..67ed04f351 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -360,4 +360,10 @@ DECLARE_UNIQUE_INDEX(pg_subscription_subname_index, 6115, on pg_subscription usi
DECLARE_UNIQUE_INDEX(pg_subscription_rel_srrelid_srsubid_index, 6117, on pg_subscription_rel using btree(srrelid oid_ops, srsubid oid_ops));
#define SubscriptionRelSrrelidSrsubidIndexId 6117
+DECLARE_UNIQUE_INDEX(pg_variable_oid_index, 4288, on pg_variable using btree(oid oid_ops));
+#define VariableObjectIndexId 4288
+
+DECLARE_UNIQUE_INDEX(pg_variable_varname_nsp_index, 4289, on pg_variable using btree(varname name_ops, varnamespace oid_ops));
+#define VariableNameNspIndexId 4289
+
#endif /* INDEXING_H */
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 0e202372d5..8812075b2e 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -75,10 +75,13 @@ extern Oid RangeVarGetAndCheckCreationNamespace(RangeVar *newRelation,
extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
+extern bool VariableIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
+extern bool VariableIsVisible(Oid varid);
+
extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
bool expand_variadic,
@@ -146,6 +149,10 @@ extern void SetTempNamespaceState(Oid tempNamespaceId,
Oid tempToastNamespaceId);
extern void ResetTempTableNamespace(void);
+extern List *NamesFromList(List *names);
+extern Oid lookup_variable(const char *nspname, const char *varname, bool missing_ok);
+extern Oid identify_variable(List *names, char **attrname, bool *not_uniq);
+
extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
extern OverrideSearchPath *CopyOverrideSearchPath(OverrideSearchPath *path);
extern bool OverrideSearchPathMatchesCurrent(OverrideSearchPath *path);
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index aee49fdb6d..f84ea21c68 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -57,6 +57,7 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 860571440a..7f3a0884d2 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5961,6 +5961,9 @@
proname => 'pg_collation_is_visible', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid',
prosrc => 'pg_collation_is_visible' },
+{ oid => '4187', descr => 'is schema variable visible in search path?',
+ proname => 'pg_variable_is_visible', procost => '10', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid', prosrc => 'pg_variable_is_visible' },
{ oid => '2854', descr => 'get OID of current session\'s temp schema, if any',
proname => 'pg_my_temp_schema', provolatile => 's', proparallel => 'r',
diff --git a/src/include/catalog/pg_variable.h b/src/include/catalog/pg_variable.h
new file mode 100644
index 0000000000..62355edf4c
--- /dev/null
+++ b/src/include/catalog/pg_variable.h
@@ -0,0 +1,101 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.h
+ * definition of schema variables system catalog (pg_variables)
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_variable.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VARIABLE_H
+#define PG_VARIABLE_H
+
+#include "catalog/genbki.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable_d.h"
+#include "utils/acl.h"
+
+/* ----------------
+ * pg_variable definition. cpp turns this into
+ * typedef struct FormData_pg_variable
+ * ----------------
+ */
+CATALOG(pg_variable,4287,VariableRelationId)
+{
+ NameData varname; /* variable name */
+ Oid varnamespace; /* OID of namespace containing variable class */
+ Oid vartype; /* OID of entry in pg_type for variable's type */
+ int32 vartypmod; /* typmode for variable's type */
+ Oid varowner; /* class owner */
+ Oid varcollation; /* variable collation */
+ char vareoxaction; /* action on transaction end */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+
+ /* list of expression trees for variable default (NULL if none) */
+ pg_node_tree vardefexpr BKI_DEFAULT(_null_);
+
+ aclitem varacl[1] BKI_DEFAULT(_null_); /* access permissions */
+
+#endif
+} FormData_pg_variable;
+
+typedef enum VariableEOXActionCodes
+{
+ VARIABLE_EOX_CODE_NOOP = 'n', /* NOOP */
+ VARIABLE_EOX_CODE_DROP = 'd', /* ON COMMIT DROP */
+ VARIABLE_EOX_CODE_RESET = 'r', /* ON COMMIT RESET */
+} VariableEOXActionCodes;
+
+/* ----------------
+ * Form_pg_variable corresponds to a pointer to a tuple with
+ * the format of pg_variable relation.
+ * ----------------
+ */
+typedef FormData_pg_variable *Form_pg_variable;
+
+typedef struct Variable
+{
+ Oid oid;
+ char *name;
+ Oid namespace;
+ Oid typid;
+ int32 typmod;
+ Oid owner;
+ Oid collation;
+ VariableEOXAction eoxaction;
+ Node *defexpr;
+ Acl *acl;
+} Variable;
+
+/* returns fields from pg_variable table */
+extern char *get_schema_variable_name(Oid varid);
+extern void get_schema_variable_type_typmod_collid(Oid varid,
+ Oid *typid,
+ int32 *typmod,
+ Oid *collid);
+
+/* returns name of variable based on current search path */
+extern char *schema_variable_get_name(Oid varid);
+
+extern Variable *GetVariable(Oid varid, bool missing_ok);
+extern ObjectAddress VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Oid varCollation,
+ Node *varDefexpr,
+ VariableEOXAction eoxaction,
+ bool if_not_exists);
+
+
+#endif /* PG_VARIABLE_H */
diff --git a/src/include/commands/schemavariable.h b/src/include/commands/schemavariable.h
new file mode 100644
index 0000000000..d1577fcec3
--- /dev/null
+++ b/src/include/commands/schemavariable.h
@@ -0,0 +1,42 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemavariable.h
+ * prototypes for schemavariable.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/schemavariable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SCHEMAVARIABLE_H
+#define SCHEMAVARIABLE_H
+
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable.h"
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "utils/queryenvironment.h"
+
+extern void ResetSchemaVariableCache(void);
+
+extern void RemoveVariableById(Oid varid);
+extern ObjectAddress DefineSchemaVariable(ParseState *pstate, CreateSchemaVarStmt *stmt);
+
+extern Datum GetSchemaVariable(Oid varid, bool *isNull, Oid expected_typid, bool copy);
+extern Datum CopySchemaVariable(Oid varid, bool *isNull, Oid *typid);
+
+extern void SetSchemaVariable(Oid varid, Datum value, bool isNull, Oid typid, int32 typmod);
+
+extern void doLetStmtReset(PlannedStmt *pstmt);
+extern void doLetStmtEval(PlannedStmt *pstmt, ParamListInfo params, QueryEnvironment *queryEnv, const char *queryString);
+
+extern void register_variable_on_commit_action(Oid varid, VariableEOXAction action);
+extern void AtPreEOXact_SchemaVariable_on_commit_actions(bool isCommit);
+extern void AtEOXact_SchemaVariable_on_commit_actions(bool isCommit);
+
+#endif
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77616..1e92e8e1be 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -351,6 +352,13 @@ typedef struct ExprEvalStep
Oid paramtype; /* OID of parameter's datatype */
} param;
+ /* for EEOP_PARAM_VARIABLE */
+ struct
+ {
+ Oid varid; /* OID of assigned variable */
+ Oid vartype; /* OID of parameter's datatype */
+ } vparam;
+
/* for EEOP_PARAM_CALLBACK */
struct
{
@@ -700,6 +708,8 @@ extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
extern void ExecEvalParamExecParams(Bitmapset *params, EState *estate);
extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern void ExecEvalParamVariable(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext);
extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op);
diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h
index 10e9ded246..1ba9b9f4c6 100644
--- a/src/include/executor/execdesc.h
+++ b/src/include/executor/execdesc.h
@@ -48,6 +48,10 @@ typedef struct QueryDesc
EState *estate; /* executor's query-wide state */
PlanState *planstate; /* tree of per-plan-node state */
+ /* reference to schema variables buffer */
+ int num_schema_variables;
+ SchemaVariableValue *schema_variables;
+
/* This field is set by ExecutorRun */
bool already_executed; /* true if previously executed */
diff --git a/src/include/executor/svariableReceiver.h b/src/include/executor/svariableReceiver.h
new file mode 100644
index 0000000000..8c8117701f
--- /dev/null
+++ b/src/include/executor/svariableReceiver.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * svariableReceiver.h
+ * prototypes for svariableReceiver.c
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/svariableReceiver.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SVARIABLE_RECEIVER_H
+#define SVARIABLE_RECEIVER_H
+
+#include "tcop/dest.h"
+
+
+extern DestReceiver *CreateVariableDestReceiver(void);
+
+extern void SetVariableDestReceiverParams(DestReceiver *self, Oid varid);
+
+#endif /* SVARIABLE_RECEIVER_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index c830f141b1..0d9cdd551c 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -464,6 +464,18 @@ typedef struct ResultRelInfo
bool ri_PartitionReadyForRouting;
} ResultRelInfo;
+/* ----------------
+ * SchemaVariableValue
+ * ----------------
+ */
+typedef struct SchemaVariableValue
+{
+ Oid varid;
+ Oid typid;
+ bool isnull;
+ Datum value;
+} SchemaVariableValue;
+
/* ----------------
* EState information
*
@@ -518,6 +530,13 @@ typedef struct EState
ParamListInfo es_param_list_info; /* values of external params */
ParamExecData *es_param_exec_vals; /* values of internal params */
+ /* Variables info: */
+ /* number of used schema variables */
+ int es_num_schema_variables;
+
+ /* array of copied values of schema variables */
+ SchemaVariableValue *es_schema_variables;
+
QueryEnvironment *es_queryEnv; /* query environment */
/* Other working state: */
@@ -565,6 +584,8 @@ typedef struct EState
/* The per-query shared memory area to use for parallel execution. */
struct dsa_area *es_query_dsa;
+ int es_result_variable; /* Oid of target variable */
+
/*
* JIT information. es_jit_flags indicates whether JIT should be performed
* and with which options. es_jit is created on-demand when JITing is
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 697d3d7a5f..dd7fd8ed42 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -348,6 +348,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSchemaVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
@@ -419,6 +420,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
+ T_LetStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -663,6 +665,7 @@ typedef enum CmdType
CMD_DELETE,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
+ CMD_PLAN_UTILITY, /* only let stmt now, requires planning */
CMD_NOTHING /* dummy command for instead nothing rules
* with qual */
} CmdType;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07ab1a3dde..539f248851 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -84,7 +84,9 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
-#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_READ (1<<12) /* for variables */
+#define ACL_WRITE (1<<13) /* for variables */
+#define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -121,6 +123,7 @@ typedef struct Query
int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */
+ int resultVariable; /* Oid of target variable or 0 */
bool hasAggs; /* has aggregates in tlist or havingQual */
bool hasWindowFuncs; /* has window functions in tlist */
@@ -131,6 +134,7 @@ typedef struct Query
bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */
bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */
bool hasRowSecurity; /* rewriter has applied some RLS policy */
+ bool hasSchemaVariable; /* uses schema variables */
List *cteList; /* WITH list (of CommonTableExpr's) */
@@ -1505,6 +1509,18 @@ typedef struct UpdateStmt
WithClause *withClause; /* WITH clause */
} UpdateStmt;
+/* ----------------------
+ * Let Statement
+ * ----------------------
+ */
+typedef struct LetStmt
+{
+ NodeTag type;
+ List *target; /* target variable */
+ Node *selectStmt; /* source expression */
+ int location;
+} LetStmt;
+
/* ----------------------
* Select Statement
*
@@ -1682,6 +1698,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -2497,6 +2514,21 @@ typedef struct AlterSeqStmt
bool missing_ok; /* skip error if a role is missing? */
} AlterSeqStmt;
+/* ----------------------
+ * {Create|Alter} VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSchemaVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the type of variable */
+ CollateClause *collClause;
+ Node *defexpr; /* default expression */
+ VariableEOXAction eoxaction; /* on commit action */
+ bool if_not_exists; /* do nothing if it already exists */
+} CreateSchemaVarStmt;
+
/* ----------------------
* Create {Aggregate|Operator|Type} Statement
* ----------------------
@@ -3238,7 +3270,8 @@ typedef enum DiscardMode
DISCARD_ALL,
DISCARD_PLANS,
DISCARD_SEQUENCES,
- DISCARD_TEMP
+ DISCARD_TEMP,
+ DISCARD_VARIABLES
} DiscardMode;
typedef struct DiscardStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd03a..cbf5b8e0a0 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -43,7 +43,7 @@ typedef struct PlannedStmt
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete|utility */
+ CmdType commandType; /* select|let|insert|update|delete|utility */
uint64 queryId; /* query identifier (copied from Query) */
@@ -81,6 +81,9 @@ typedef struct PlannedStmt
*/
List *rootResultRelations;
+ /* Oid of target variable for LET command */
+ Oid resultVariable;
+
List *subplans; /* Plan trees for SubPlan expressions; note
* that some could be NULL */
@@ -96,6 +99,8 @@ typedef struct PlannedStmt
Node *utilityStmt; /* non-null if this is utility stmt */
+ List *schemaVariables; /* list of OIDs for PARAM_VARIABLE Params */
+
/* statement location in source string (copied from Query) */
int stmt_location; /* start location, or -1 if unknown */
int stmt_len; /* length in bytes; 0 means "rest of string" */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..2a5f24c612 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -43,15 +43,25 @@ typedef struct Alias
List *colnames; /* optional list of column aliases */
} Alias;
-/* What to do at commit time for temporary relations */
+/*
+ * What to do at commit time for temporary relations or
+ * persistent/temporary variable.
+ */
typedef enum OnCommitAction
{
ONCOMMIT_NOOP, /* No ON COMMIT clause (do nothing) */
ONCOMMIT_PRESERVE_ROWS, /* ON COMMIT PRESERVE ROWS (do nothing) */
ONCOMMIT_DELETE_ROWS, /* ON COMMIT DELETE ROWS */
- ONCOMMIT_DROP /* ON COMMIT DROP */
+ ONCOMMIT_DROP, /* ON COMMIT DROP */
} OnCommitAction;
+typedef enum VariableEOXAction
+{
+ VARIABLE_EOX_NOOP, /* Do nothing */
+ VARIABLE_EOX_DROP, /* ON TRANSACTION END DROP */
+ VARIABLE_EOX_RESET, /* ON TRANSACTION END RESET */
+} VariableEOXAction;
+
/*
* RangeVar - range variable, used in FROM clauses
*
@@ -229,13 +239,17 @@ typedef struct Const
* of the `paramid' field contain the SubLink's subLinkId, and
* the low-order 16 bits contain the column number. (This type
* of Param is also converted to PARAM_EXEC during planning.)
+ *
+ * PARAM_VARIABLE: The parameter is a access to schema variable
+ * paramid holds varid.
*/
typedef enum ParamKind
{
PARAM_EXTERN,
PARAM_EXEC,
PARAM_SUBLINK,
- PARAM_MULTIEXPR
+ PARAM_MULTIEXPR,
+ PARAM_VARIABLE
} ParamKind;
typedef struct Param
@@ -246,6 +260,7 @@ typedef struct Param
Oid paramtype; /* pg_type OID of parameter's datatype */
int32 paramtypmod; /* typmod value, if known */
Oid paramcollid; /* OID of collation, or InvalidOid if none */
+ Oid paramvarid; /* OID of schema variable if it is used */
int location; /* token location, or -1 if unknown */
} Param;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index adb4265047..8d8adb8cee 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -145,6 +145,8 @@ typedef struct PlannerGlobal
bool parallelModeNeeded; /* parallel mode actually required? */
char maxParallelHazard; /* worst PROPARALLEL hazard level */
+
+ List *schemaVariables; /* list of used schema variables */
} PlannerGlobal;
/* macro for fetching the Plan associated with a SubPlan node */
@@ -329,6 +331,7 @@ typedef struct PlannerInfo
bool hasPseudoConstantQuals; /* true if any RestrictInfo has
* pseudoconstant = true */
bool hasRecursion; /* true if planning a recursive WITH item */
+ bool hasSchemaVariable; /* true if schema variables was used */
/* These fields are used only when hasRecursion is true: */
int wt_param_id; /* PARAM_EXEC ID for the work table */
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index c8ab0280d2..37b67d136f 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -120,5 +120,6 @@ extern void extract_query_dependencies(Node *query,
List **relationOids,
List **invalItems,
bool *hasRowSecurity);
+extern void pull_up_has_schema_variable(PlannerInfo *root);
#endif /* PLANMAIN_H */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db40147b..d3ed3f4d0f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -231,6 +231,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("let", LET, UNRESERVED_KEYWORD)
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -434,6 +435,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 0230543810..143597ce80 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -69,7 +69,9 @@ typedef enum ParseExprKind
EXPR_KIND_TRIGGER_WHEN, /* WHEN condition in CREATE TRIGGER */
EXPR_KIND_POLICY, /* USING or WITH CHECK expr in policy */
EXPR_KIND_PARTITION_EXPRESSION, /* PARTITION BY expression */
- EXPR_KIND_CALL_ARGUMENT /* procedure argument in CALL */
+ EXPR_KIND_CALL_ARGUMENT, /* procedure argument in CALL */
+ EXPR_KIND_VARIABLE_DEFAULT, /* default value for schema variable */
+ EXPR_KIND_LET /* LET assignment (should be same like UPDATE) */
} ParseExprKind;
@@ -202,6 +204,7 @@ struct ParseState
bool p_hasTargetSRFs;
bool p_hasSubLinks;
bool p_hasModifyingCTE;
+ bool p_hasSchemaVariable;
Node *p_last_srf; /* most recent set-returning func/op found */
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index ec6e0c102f..1ee199ed8f 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -32,6 +32,16 @@ extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
int attrno,
List *indirection,
int location);
+extern Node *transformAssignmentIndirection(ParseState *pstate,
+ Node *basenode,
+ const char *targetName,
+ bool targetIsArray,
+ Oid targetTypeId,
+ int32 targetTypMod,
+ Oid targetCollation,
+ ListCell *indirection,
+ Node *rhs,
+ int location);
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
char *colname, int attrno,
List *indirection,
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 82f0f2e741..c49b653555 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -96,7 +96,8 @@ typedef enum
DestCopyOut, /* results sent to COPY TO code */
DestSQLFunction, /* results sent to SQL-language func mgr */
DestTransientRel, /* results sent to transient relation */
- DestTupleQueue /* results sent to tuple queue */
+ DestTupleQueue, /* results sent to tuple queue */
+ DestVariable /* results sents to schema variable */
} CommandDest;
/* ----------------
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f4d4be8d0d..c624d8dd0b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -147,9 +147,11 @@ typedef ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
+#define ACL_READ_CHR 'S' /* 'R' is occupated by old RULE priv */
+#define ACL_WRITE_CHR 'W'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcSW"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -166,6 +168,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_READ|ACL_WRITE)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -253,6 +256,8 @@ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_variable_aclmask(Oid var_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode);
@@ -269,6 +274,7 @@ extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
+extern AclResult pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, ObjectType objtype,
const char *objectname);
@@ -305,6 +311,7 @@ extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool pg_publication_ownercheck(Oid pub_oid, Oid roleid);
extern bool pg_subscription_ownercheck(Oid sub_oid, Oid roleid);
extern bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid);
+extern bool pg_variable_ownercheck(Oid stat_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index e55ea4035b..cb3f4aaca9 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -122,6 +122,7 @@ extern bool get_func_leakproof(Oid funcid);
extern float4 get_func_cost(Oid funcid);
extern float4 get_func_rows(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
+extern Oid get_varname_varid(const char *varname, Oid varnamespace);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4f333586ee..453699be3c 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -107,9 +107,11 @@ enum SysCacheIdentifier
TYPENAMENSP,
TYPEOID,
USERMAPPINGOID,
- USERMAPPINGUSERSERVER
+ USERMAPPINGUSERSERVER,
+ VARIABLENAMENSP,
+ VARIABLEOID
-#define SysCacheSize (USERMAPPINGUSERSERVER + 1)
+#define SysCacheSize (VARIABLEOID + 1)
};
extern void InitCatalogCache(void);
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b500..48286f8e1a 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -105,5 +105,7 @@ ORDER BY 1, 2;
pg_index | indpred | pg_node_tree
pg_largeobject | data | bytea
pg_largeobject_metadata | lomacl | aclitem[]
-(11 rows)
+ pg_variable | varacl | aclitem[]
+ pg_variable | vardefexpr | pg_node_tree
+(13 rows)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 48e0508a96..6cd3a77a8f 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -164,6 +164,7 @@ pg_ts_parser|t
pg_ts_template|t
pg_type|t
pg_user_mapping|t
+pg_variable|t
point_tbl|t
polygon_tbl|t
quad_box_tbl|t
diff --git a/src/test/regress/expected/schema_variables.out b/src/test/regress/expected/schema_variables.out
new file mode 100644
index 0000000000..c659c83dcf
--- /dev/null
+++ b/src/test/regress/expected/schema_variables.out
@@ -0,0 +1,491 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+DROP VARIABLE var1, var2;
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+CREATE ROLE var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT var1;
+ERROR: permission denied for schema variable var1
+SET ROLE TO DEFAULT;
+GRANT READ ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+ERROR: permission denied for schema variable var1
+-- should to work
+SELECT var1;
+ var1
+------
+
+(1 row)
+
+SET ROLE TO DEFAULT;
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+SET ROLE TO var_test_role;
+-- should to work
+LET var1 = 333;
+SET ROLE TO DEFAULT;
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO var_test_role;
+-- should to fail
+SELECT public.var1;
+ERROR: permission denied for schema variable var1
+-- should to work;
+SELECT secure_var();
+ secure_var
+------------
+ 333
+(1 row)
+
+SET ROLE TO DEFAULT;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+ QUERY PLAN
+-----------------------------------------------
+ Function Scan on pg_catalog.generate_series g
+ Output: v
+ Function Call: generate_series(1, 100)
+ Filter: ((g.v)::numeric = var1)
+(4 rows)
+
+CREATE VIEW schema_var_view AS SELECT var1;
+SELECT * FROM schema_var_view;
+ var1
+------
+ 333
+(1 row)
+
+\c -
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+ var1
+------
+
+(1 row)
+
+LET var1 = pi();
+SELECT var1;
+ var1
+------------------
+ 3.14159265358979
+(1 row)
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+ QUERY PLAN
+----------------------------
+ Result
+ Output: 3.14159265358979
+(2 rows)
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+EXECUTE var_pp(100, 1.23456);
+SELECT var1;
+ var1
+-----------
+ 101.23456
+(1 row)
+
+CREATE VARIABLE var3 AS int;
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+SELECT inc(1);
+ inc
+-----
+ 1
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 2
+(1 row)
+
+SELECT inc(1);
+ inc
+-----
+ 3
+(1 row)
+
+SELECT inc(1) FROM generate_series(1,10);
+ inc
+-----
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+(10 rows)
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var3 = 0;
+ERROR: permission denied for schema variable var3
+SET ROLE TO DEFAULT;
+DROP VIEW schema_var_view;
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+-- composite variables
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+\d v1
+\d v2
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+SELECT v1;
+ v1
+------------
+ (1,2,3.14)
+(1 row)
+
+SELECT v2;
+ v2
+---------------
+ (10,20,31.40)
+(1 row)
+
+SELECT (v1).*;
+ x | y | z
+---+---+------
+ 1 | 2 | 3.14
+(1 row)
+
+SELECT (v2).*;
+ x | y | z
+----+----+-------
+ 10 | 20 | 31.40
+(1 row)
+
+SELECT v1.x + v1.z;
+ ?column?
+----------
+ 4.14
+(1 row)
+
+SELECT v2.x + v2.z;
+ ?column?
+----------
+ 41.40
+(1 row)
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+SELECT v2.x;
+ERROR: permission denied for schema variable v2
+SET ROLE TO DEFAULT;
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+DROP ROLE var_test_role;
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+ relname
+----------
+ pg_class
+(1 row)
+
+-- should to fail
+SELECT varx.xxx;
+ERROR: type text is not composite
+-- variables can be updated under RO transaction
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+SELECT varx;
+ varx
+-------
+ hello
+(1 row)
+
+DROP VARIABLE varx;
+CREATE TYPE t1 AS (a int, b numeric, c text);
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+ v1
+----------------------------
+ (1,3.14159265358979,hello)
+(1 row)
+
+LET v1.b = 10.2222;
+SELECT v1;
+ v1
+-------------------
+ (1,10.2222,hello)
+(1 row)
+
+-- should to fail
+LET v1.x = 10;
+ERROR: cannot assign to field "x" of column "x" because there is no such column in data type t1
+LINE 1: LET v1.x = 10;
+ ^
+DROP VARIABLE v1;
+DROP TYPE t1;
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+ va1
+------------
+ {10.1,2.1}
+(1 row)
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+ va2
+--------------------
+ (10.2,"{0.0,0.0}")
+(1 row)
+
+LET va2.b[1] = 10.3;
+SELECT va2;
+ va2
+---------------------
+ (10.2,"{10.3,0.0}")
+(1 row)
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+ v1
+------------------
+ 6.28318530717958
+(1 row)
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+ v2
+--------------------------
+ (3.14159265358979,Hello)
+(1 row)
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+ERROR: cannot drop type t2 because other objects depend on it
+DETAIL: schema variable v2 depends on type t2
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+ var1
+------
+ 1000
+(1 row)
+
+CREATE ROLE var_test_role;
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+DROP VARIABLE var_schema2.var1;
+ERROR: permission denied for schema var_schema2
+SET ROLE TO DEFAULT;
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+SET ROLE TO var_test_role;
+SELECT public.var1;
+ var1
+------
+ 1000
+(1 row)
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+SELECT public.var1_renamed;
+ var1_renamed
+--------------
+ 1000
+(1 row)
+
+DROP VARIABLE public.var1_renamed;
+SET ROLE TO DEFAULt;
+DROP ROLE var_test_role;
+CREATE VARIABLE xx AS text DEFAULT 'hello';
+SELECT xx, upper(xx);
+ xx | upper
+-------+-------
+ hello | HELLO
+(1 row)
+
+LET xx = 'Hi';
+SELECT xx;
+ xx
+----
+ Hi
+(1 row)
+
+DROP VARIABLE xx;
+-- ON TRANSACTION END RESET tests
+CREATE VARIABLE t1 AS int DEFAULT -1 ON TRANSACTION END RESET;
+BEGIN;
+ SELECT t1;
+ t1
+----
+ -1
+(1 row)
+
+ LET t1 = 100;
+ SELECT t1;
+ t1
+-----
+ 100
+(1 row)
+
+COMMIT;
+SELECT t1;
+ t1
+----
+ -1
+(1 row)
+
+BEGIN;
+ SELECT t1;
+ t1
+----
+ -1
+(1 row)
+
+ LET t1 = 100;
+ SELECT t1;
+ t1
+-----
+ 100
+(1 row)
+
+ROLLBACK;
+SELECT t1;
+ t1
+----
+ -1
+(1 row)
+
+DROP VARIABLE t1;
+CREATE VARIABLE v1 AS int DEFAULT 0;
+CREATE VARIABLE v2 AS text DEFAULT 'none';
+LET v1 = 100;
+LET v2 = 'Hello';
+SELECT v1, v2;
+ v1 | v2
+-----+-------
+ 100 | Hello
+(1 row)
+
+LET v1 = DEFAULT;
+LET v2 = DEFAULT;
+SELECT v1, v2;
+ v1 | v2
+----+------
+ 0 | none
+(1 row)
+
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+-- ON COMMIT DROP tests
+-- should be 0 always
+SELECT count(*) FROM pg_variable;
+ count
+-------
+ 0
+(1 row)
+
+CREATE TEMP VARIABLE g AS int ON COMMIT DROP;
+SELECT count(*) FROM pg_variable;
+ count
+-------
+ 0
+(1 row)
+
+BEGIN;
+ CREATE TEMP VARIABLE g AS int ON COMMIT DROP;
+COMMIT;
+SELECT count(*) FROM pg_variable;
+ count
+-------
+ 0
+(1 row)
+
+BEGIN;
+ CREATE TEMP VARIABLE g AS int ON COMMIT DROP;
+ROLLBACK;
+SELECT count(*) FROM pg_variable;
+ count
+-------
+ 0
+(1 row)
+
+-- test on query with workers
+create table svar_test(a int);
+insert into svar_test select * from generate_series(1,1000000);
+analyze svar_test;
+create variable zero int;
+let zero = 0;
+-- parallel workers should be used
+explain (costs off) select count(*) from svar_test where a%10 = zero;
+ QUERY PLAN
+--------------------------------------------------
+ Finalize Aggregate
+ -> Gather
+ Workers Planned: 2
+ -> Partial Aggregate
+ -> Parallel Seq Scan on svar_test
+ Filter: ((a % 10) = zero)
+(6 rows)
+
+-- result should be 100000
+select count(*) from svar_test where a%10 = zero;
+ count
+--------
+ 100000
+(1 row)
+
+drop table svar_test;
+drop variable zero;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 16f979c8d9..9bf379b87b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml schema_variables
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 42632be675..42bf4ecb3f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -191,3 +191,4 @@ test: partition_aggregate
test: event_trigger
test: fast_default
test: stats
+test: schema_variables
diff --git a/src/test/regress/sql/schema_variables.sql b/src/test/regress/sql/schema_variables.sql
new file mode 100644
index 0000000000..4be31e1bb0
--- /dev/null
+++ b/src/test/regress/sql/schema_variables.sql
@@ -0,0 +1,328 @@
+CREATE VARIABLE var1 AS integer;
+CREATE TEMP VARIABLE var2 AS text;
+
+DROP VARIABLE var1, var2;
+
+-- functional interface
+CREATE VARIABLE var1 AS numeric;
+
+CREATE ROLE var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT READ ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+-- should to fail
+LET var1 = 10;
+-- should to work
+SELECT var1;
+
+SET ROLE TO DEFAULT;
+
+GRANT WRITE ON VARIABLE var1 TO var_test_role;
+
+SET ROLE TO var_test_role;
+
+-- should to work
+LET var1 = 333;
+
+SET ROLE TO DEFAULT;
+
+REVOKE ALL ON VARIABLE var1 FROM var_test_role;
+
+CREATE OR REPLACE FUNCTION secure_var()
+RETURNS int AS $$
+ SELECT public.var1::int;
+$$ LANGUAGE sql SECURITY DEFINER;
+
+SELECT secure_var();
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+SELECT public.var1;
+
+-- should to work;
+SELECT secure_var();
+
+SET ROLE TO DEFAULT;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM generate_series(1,100) g(v) WHERE v = var1;
+
+CREATE VIEW schema_var_view AS SELECT var1;
+
+SELECT * FROM schema_var_view;
+
+\c -
+
+-- should to work still, but var will be empty
+SELECT * FROM schema_var_view;
+
+LET var1 = pi();
+
+SELECT var1;
+
+-- we can look on execution plan
+EXPLAIN (VERBOSE, COSTS OFF) LET var1 = pi();
+
+-- LET can be prepared
+PREPARE var_pp(int, numeric) AS LET var1 = $1 + $2;
+
+EXECUTE var_pp(100, 1.23456);
+
+SELECT var1;
+
+CREATE VARIABLE var3 AS int;
+
+CREATE OR REPLACE FUNCTION inc(int)
+RETURNS int AS $$
+BEGIN
+ LET public.var3 = COALESCE(public.var3 + $1, $1);
+ RETURN var3;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT inc(1);
+SELECT inc(1);
+SELECT inc(1);
+
+SELECT inc(1) FROM generate_series(1,10);
+
+SET ROLE TO var_test_role;
+
+-- should to fail
+LET var3 = 0;
+
+SET ROLE TO DEFAULT;
+
+DROP VIEW schema_var_view;
+
+DROP VARIABLE var1 CASCADE;
+DROP VARIABLE var3 CASCADE;
+
+-- composite variables
+
+CREATE TYPE sv_xyz AS (x int, y int, z numeric(10,2));
+
+CREATE VARIABLE v1 AS sv_xyz;
+CREATE VARIABLE v2 AS sv_xyz;
+
+\d v1
+\d v2
+
+LET v1 = (1,2,3.14);
+LET v2 = (10,20,3.14*10);
+
+-- should to work too - there are prepared casts
+LET v1 = (1,2,3.14);
+
+SELECT v1;
+SELECT v2;
+SELECT (v1).*;
+SELECT (v2).*;
+
+SELECT v1.x + v1.z;
+SELECT v2.x + v2.z;
+
+-- access to composite fields should be safe too
+-- should to fail
+SET ROLE TO var_test_role;
+
+SELECT v2.x;
+
+SET ROLE TO DEFAULT;
+
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+DROP ROLE var_test_role;
+
+-- scalar variables should not be in conflict with qualified column
+CREATE VARIABLE varx AS text;
+SELECT varx.relname FROM pg_class varx WHERE varx.relname = 'pg_class';
+
+-- should to fail
+SELECT varx.xxx;
+
+-- variables can be updated under RO transaction
+
+BEGIN;
+SET TRANSACTION READ ONLY;
+LET varx = 'hello';
+COMMIT;
+
+SELECT varx;
+
+DROP VARIABLE varx;
+
+CREATE TYPE t1 AS (a int, b numeric, c text);
+
+CREATE VARIABLE v1 AS t1;
+LET v1 = (1, pi(), 'hello');
+SELECT v1;
+LET v1.b = 10.2222;
+SELECT v1;
+
+-- should to fail
+LET v1.x = 10;
+
+DROP VARIABLE v1;
+DROP TYPE t1;
+
+-- arrays are supported
+CREATE VARIABLE va1 AS numeric[];
+LET va1 = ARRAY[1.1,2.1];
+LET va1[1] = 10.1;
+SELECT va1;
+
+CREATE TYPE ta2 AS (a numeric, b numeric[]);
+CREATE VARIABLE va2 AS ta2;
+LET va2 = (10.1, ARRAY[0.0, 0.0]);
+LET va2.a = 10.2;
+SELECT va2;
+LET va2.b[1] = 10.3;
+SELECT va2;
+
+DROP VARIABLE va1;
+DROP VARIABLE va2;
+DROP TYPE ta2;
+
+-- default values
+CREATE VARIABLE v1 AS numeric DEFAULT pi();
+LET v1 = v1 * 2;
+SELECT v1;
+
+CREATE TYPE t2 AS (a numeric, b text);
+CREATE VARIABLE v2 AS t2 DEFAULT (NULL, 'Hello');
+LET public.v2.a = pi();
+SELECT v2;
+
+-- shoudl fail due dependency
+DROP TYPE t2;
+
+-- should be ok
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+-- tests of alters
+CREATE SCHEMA var_schema1;
+CREATE SCHEMA var_schema2;
+
+CREATE VARIABLE var_schema1.var1 AS integer;
+LET var_schema1.var1 = 1000;
+SELECT var_schema1.var1;
+ALTER VARIABLE var_schema1.var1 SET SCHEMA var_schema2;
+SELECT var_schema2.var1;
+
+CREATE ROLE var_test_role;
+
+ALTER VARIABLE var_schema2.var1 OWNER TO var_test_role;
+SET ROLE TO var_test_role;
+
+-- should fail, no access to schema var_schema2.var
+SELECT var_schema2.var1;
+DROP VARIABLE var_schema2.var1;
+
+SET ROLE TO DEFAULT;
+
+ALTER VARIABLE var_schema2.var1 SET SCHEMA public;
+
+SET ROLE TO var_test_role;
+SELECT public.var1;
+
+ALTER VARIABLE public.var1 RENAME TO var1_renamed;
+
+SELECT public.var1_renamed;
+
+DROP VARIABLE public.var1_renamed;
+
+SET ROLE TO DEFAULt;
+
+DROP ROLE var_test_role;
+
+CREATE VARIABLE xx AS text DEFAULT 'hello';
+
+SELECT xx, upper(xx);
+
+LET xx = 'Hi';
+
+SELECT xx;
+
+DROP VARIABLE xx;
+
+-- ON TRANSACTION END RESET tests
+CREATE VARIABLE t1 AS int DEFAULT -1 ON TRANSACTION END RESET;
+
+BEGIN;
+ SELECT t1;
+ LET t1 = 100;
+ SELECT t1;
+COMMIT;
+
+SELECT t1;
+
+BEGIN;
+ SELECT t1;
+ LET t1 = 100;
+ SELECT t1;
+ROLLBACK;
+
+SELECT t1;
+
+DROP VARIABLE t1;
+
+CREATE VARIABLE v1 AS int DEFAULT 0;
+CREATE VARIABLE v2 AS text DEFAULT 'none';
+
+LET v1 = 100;
+LET v2 = 'Hello';
+SELECT v1, v2;
+LET v1 = DEFAULT;
+LET v2 = DEFAULT;
+SELECT v1, v2;
+
+DROP VARIABLE v1;
+DROP VARIABLE v2;
+
+-- ON COMMIT DROP tests
+-- should be 0 always
+SELECT count(*) FROM pg_variable;
+
+CREATE TEMP VARIABLE g AS int ON COMMIT DROP;
+
+SELECT count(*) FROM pg_variable;
+
+BEGIN;
+ CREATE TEMP VARIABLE g AS int ON COMMIT DROP;
+COMMIT;
+
+SELECT count(*) FROM pg_variable;
+
+BEGIN;
+ CREATE TEMP VARIABLE g AS int ON COMMIT DROP;
+ROLLBACK;
+
+SELECT count(*) FROM pg_variable;
+
+-- test on query with workers
+create table svar_test(a int);
+insert into svar_test select * from generate_series(1,1000000);
+analyze svar_test;
+create variable zero int;
+let zero = 0;
+
+-- parallel workers should be used
+explain (costs off) select count(*) from svar_test where a%10 = zero;
+
+-- result should be 100000
+select count(*) from svar_test where a%10 = zero;
+
+drop table svar_test;
+drop variable zero;
+
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-15 16:06 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-15 16:06 UTC (permalink / raw)
To: Dean Rasheed <dean.a.rasheed@gmail.com>; +Cc: Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
> The code is more cleaner now, there are more tests, and documentation is
> mostly complete. I am sorry - my English is not good.
> New features:
>
> o ON COMMIT DROP and ON TRANSACTION END RESET -- remove temp variable on
> commit, reset variable on transaction end (commit, rollback)
> o LET var = DEFAULT -- reset specified variable
>
>
fix some forgotten warnings and dependency issue
few more tests
Regards
Pavel
> Regards
>
> Pavel
>
>
>> Regards,
>> Dean
>>
>
Attachments:
[application/gzip] schema-variables-20180915-01.patch.gz (60.1K, ../../CAFj8pRBkPswn0JA8U3vFOdBGqcBu0ZM7jC7JcDdCS1EbZfzb2A@mail.gmail.com/3-schema-variables-20180915-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-17 19:46 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-17 19:46 UTC (permalink / raw)
To: Dean Rasheed <dean.a.rasheed@gmail.com>; +Cc: Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
so 15. 9. 2018 v 18:06 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
>
>
>
>
>> The code is more cleaner now, there are more tests, and documentation is
>> mostly complete. I am sorry - my English is not good.
>> New features:
>>
>> o ON COMMIT DROP and ON TRANSACTION END RESET -- remove temp variable on
>> commit, reset variable on transaction end (commit, rollback)
>> o LET var = DEFAULT -- reset specified variable
>>
>>
> fix some forgotten warnings and dependency issue
> few more tests
>
>
new update:
o support NOT NULL check
o implementation limited transaction variables - these variables doesn't
respects subtransactions(this is much more complex), drop variable drops
content although the drop can be reverted (maybe this limit will be
removed).
CREATE TRANSACTION VARIABLE fx AS int;
LET fx = 10;
BEGIN
LET fx = 20;
ROLLBACK;
SELECT fx;
Regards
Pavel
> Regards
>
> Pavel
>
>
>> Regards
>>
>> Pavel
>>
>>
>>> Regards,
>>> Dean
>>>
>>
Attachments:
[application/gzip] schema-variables-20180917-01.patch.gz (63.0K, ../../CAFj8pRAi2Xd93ae-SsnMOC5W4hWenmkftTnCrHgdcPpvbOs1bg@mail.gmail.com/3-schema-variables-20180917-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-19 08:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-19 08:30 UTC (permalink / raw)
To: Dean Rasheed <dean.a.rasheed@gmail.com>; +Cc: Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
new update:
I fixed pg_restore, and I cleaned a code related to transaction processing
There should be a full functionality now.
Regards
Pavel
Attachments:
[application/gzip] schema-variables-20180919-01.patch.gz (64.3K, ../../CAFj8pRCZuq=0MRsYrNzBqxPx5fqmKFN3i-BsPo8j=yW6N_=WDA@mail.gmail.com/3-schema-variables-20180919-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-19 11:23 ` Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-19 12:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 2 replies; 433+ messages in thread
From: Arthur Zakirov @ 2018-09-19 11:23 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hello,
On Wed, Sep 19, 2018 at 10:30:31AM +0200, Pavel Stehule wrote:
> Hi
>
> new update:
>
> I fixed pg_restore, and I cleaned a code related to transaction processing
>
> There should be a full functionality now.
I reviewed a little bit the patch. I have a few comments.
> <title><structname>pg_views</structname> Columns</title>
I think there is a typo here. It should be "pg_variable".
> GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
Shouldn't we use here GRANT { SELECT | LET | ... } syntax for the
constistency. Same for REVOKE. I'm not experienced syntax developer
though. But we use SELECT and LET commands when working with variables.
So we should GRANT and REVOKE priveleges for this commands.
> [ { ON COMMIT DROP | ON TRANSACTION END RESET } ]
I think we may join them and have the syntax { ON COMMIT DROP | RESET }
to get more simpler syntax. If we create a variable with ON COMMIT
DROP, PostgreSQL will drop it regardless of whether transaction was
committed or rollbacked:
=# ...
=# begin;
=# create variable int1 int on commit drop;
=# rollback;
=# -- There is no variable int1
CREATE TABLE syntax has similar options [1]. ON COMMIT controls
the behaviour of temporary tables at the end a transaction block,
whether it was committed or rollbacked. But I'm not sure is this a good
example of precedence.
> - ONCOMMIT_DROP /* ON COMMIT DROP */
> + ONCOMMIT_DROP, /* ON COMMIT DROP */
> } OnCommitAction;
There is the extra comma here after ONCOMMIT_DROP.
1 - https://www.postgresql.org/docs/current/static/sql-createtable.html
--
Arthur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
@ 2018-09-19 12:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 12:53 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-19 12:08 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
st 19. 9. 2018 v 13:23 odesílatel Arthur Zakirov <a.zakirov@postgrespro.ru>
napsal:
> Hello,
>
> On Wed, Sep 19, 2018 at 10:30:31AM +0200, Pavel Stehule wrote:
> > Hi
> >
> > new update:
> >
> > I fixed pg_restore, and I cleaned a code related to transaction
> processing
> >
> > There should be a full functionality now.
>
> I reviewed a little bit the patch. I have a few comments.
>
> > <title><structname>pg_views</structname> Columns</title>
>
> I think there is a typo here. It should be "pg_variable".
>
I'll fix it.
>
> > GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
>
> Shouldn't we use here GRANT { SELECT | LET | ... } syntax for the
> constistency. Same for REVOKE. I'm not experienced syntax developer
> though. But we use SELECT and LET commands when working with variables.
> So we should GRANT and REVOKE priveleges for this commands.
>
I understand to your proposal, - and I have not strong opinion. Originally
I proposed {SELECT|UPDATE), but some people prefer {READ|WRITE}. Now I
prefer Peter's proposal (what is implemented now) - READ|WRITE, because it
is very illustrative - and the mentioned difference is good because the
variables are not tables (by default), are not persistent, so different
rights are good for me. I see "GRANT LET" like very low clear, because
nobody knows what LET command does. Unfortunately we cannot to use standard
"SET" command, because it is used in Postgres for different purpose.
READ|WRITE are totally clear, and for user it is another signal so
variables are different than tables (so it is not one row table).
I prefer current state, but if common opinion will be different, I have not
problem to change it.
> > [ { ON COMMIT DROP | ON TRANSACTION END RESET } ]
>
> I think we may join them and have the syntax { ON COMMIT DROP | RESET }
> to get more simpler syntax. If we create a variable with ON COMMIT
> DROP, PostgreSQL will drop it regardless of whether transaction was
> committed or rollbacked:
>
I though about it too. I'll try to explain my idea. Originally I was
surprised so postgres uses "ON COMMIT" syntax, but in documentation is used
term "at transaction end". But it has some sense. ON COMMIT DROP is allowed
only for temporary tables and ON COMMIT DELETE ROWS is allowed for tables.
With these clauses the PostgreSQL is more aggressive in cleaning. It
doesn't need to calculate with rollback, because the rollback does cleaning
by self. So syntax "ON COMMIT" is fully correct it is related only for
commit event. It has not sense on rollback event (and doesn't change a
behave on rollback event).
The content of variables is not transactional (by default). It is not
destroyed by rollback. So I have to calculate with rollback too. So the
most correct syntax should be "ON COMMIT ON ROLLBACK RESET" what is little
bit messy and I used "ON TRANSACTION END". It should be signal, so this
event is effective on rollback event and it is valid for not transaction
variable. This logic is not valid to transactional variables, where ON
COMMIT RESET has sense. But this behave is not default and then I prefer
more generic syntax.
Again I have not a problem to change it, but I am thinking so current
design is logically correct.
> =# ...
> =# begin;
> =# create variable int1 int on commit drop;
> =# rollback;
> =# -- There is no variable int1
>
>
PostgreSQL catalog is transactional (where the metadata is stored), so when
I am working with metadata, then I use ON COMMIT syntax, because the behave
of ON ROLLBACK cannot be changed.
So I see two different cases - work with catalog (what is transactional)
and work with variable value, what is (like other variables in programming
languages) not transactional. "ON TRANSACTION END RESET" means - does reset
on any transaction end.
I hope so I explained it cleanly - if not, please, ask.
CREATE TABLE syntax has similar options [1]. ON COMMIT controls
> the behaviour of temporary tables at the end a transaction block,
> whether it was committed or rollbacked. But I'm not sure is this a good
> example of precedence.
>
> > - ONCOMMIT_DROP /* ON COMMIT DROP */
> > + ONCOMMIT_DROP, /* ON COMMIT DROP */
> > } OnCommitAction;
>
> There is the extra comma here after ONCOMMIT_DROP.
>
I'll fix it.
Regards
Pavel
>
> 1 - https://www.postgresql.org/docs/current/static/sql-createtable.html
>
> --
> Arthur Zakirov
> Postgres Professional: http://www.postgrespro.com
> Russian Postgres Company
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-19 12:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-19 12:53 ` Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-19 14:36 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Arthur Zakirov @ 2018-09-19 12:53 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
On Wed, Sep 19, 2018 at 02:08:04PM +0200, Pavel Stehule wrote:
> Unfortunately we cannot to use standard
> "SET" command, because it is used in Postgres for different purpose.
> READ|WRITE are totally clear, and for user it is another signal so
> variables are different than tables (so it is not one row table).
>
> I prefer current state, but if common opinion will be different, I have not
> problem to change it.
I see. I grepped the thread before writhing this but somehow missed the
discussion.
> The content of variables is not transactional (by default). It is not
> destroyed by rollback. So I have to calculate with rollback too. So the
> most correct syntax should be "ON COMMIT ON ROLLBACK RESET" what is little
> bit messy and I used "ON TRANSACTION END". It should be signal, so this
> event is effective on rollback event and it is valid for not transaction
> variable. This logic is not valid to transactional variables, where ON
> COMMIT RESET has sense. But this behave is not default and then I prefer
> more generic syntax.
> ...
> So I see two different cases - work with catalog (what is transactional)
> and work with variable value, what is (like other variables in programming
> languages) not transactional. "ON TRANSACTION END RESET" means - does reset
> on any transaction end.
>
> I hope so I explained it cleanly - if not, please, ask.
I understood what you mean, thank you. I thought that
{ ON COMMIT DROP | ON TRANSACTION END RESET } parameters are used only
for transactional variables in the first place. But is there any sense
in using this parameters with non-transactional variables? That is when
we create non-transactional variable we don't want that the variable
will rollback or reset its value after the end of a transaction.
--
Arthur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-19 12:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 12:53 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
@ 2018-09-19 14:36 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-21 19:46 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-19 14:36 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
st 19. 9. 2018 v 14:53 odesílatel Arthur Zakirov <a.zakirov@postgrespro.ru>
napsal:
> On Wed, Sep 19, 2018 at 02:08:04PM +0200, Pavel Stehule wrote:
> > Unfortunately we cannot to use standard
> > "SET" command, because it is used in Postgres for different purpose.
> > READ|WRITE are totally clear, and for user it is another signal so
> > variables are different than tables (so it is not one row table).
> >
> > I prefer current state, but if common opinion will be different, I have
> not
> > problem to change it.
>
> I see. I grepped the thread before writhing this but somehow missed the
> discussion.
>
> > The content of variables is not transactional (by default). It is not
> > destroyed by rollback. So I have to calculate with rollback too. So the
> > most correct syntax should be "ON COMMIT ON ROLLBACK RESET" what is
> little
> > bit messy and I used "ON TRANSACTION END". It should be signal, so this
> > event is effective on rollback event and it is valid for not transaction
> > variable. This logic is not valid to transactional variables, where ON
> > COMMIT RESET has sense. But this behave is not default and then I prefer
> > more generic syntax.
> > ...
> > So I see two different cases - work with catalog (what is transactional)
> > and work with variable value, what is (like other variables in
> programming
> > languages) not transactional. "ON TRANSACTION END RESET" means - does
> reset
> > on any transaction end.
> >
> > I hope so I explained it cleanly - if not, please, ask.
>
> I understood what you mean, thank you. I thought that
> { ON COMMIT DROP | ON TRANSACTION END RESET } parameters are used only
> for transactional variables in the first place. But is there any sense
> in using this parameters with non-transactional variables? That is when
> we create non-transactional variable we don't want that the variable
> will rollback or reset its value after the end of a transaction.
>
ON COMMIT DROP is used only for temp variables (transaction or not
transaction). The purpose is same like for tables. Sometimes you can to
have object with shorter life than is session.
ON TRANSACTION END RESET has sense mainly for not transaction variables. I
see two use cases.
1. protect some sensitive data - on transaction end guaranteed reset and
cleaning on end transaction. So you can be sure, so variable is not
initialized (has default value), or you are inside transaction.
2. automatic initialization - ON TRANSACTION END RESET ensure so variable
is in init state for any transaction.
Both cases has sense for transaction or not transaction variables.
I am thinking so transaction life time for content has sense. Is cheaper to
reset variable than drop it (what ON COMMIT DROP does)
What do you think?
Pavel
> --
> Arthur Zakirov
> Postgres Professional: http://www.postgrespro.com
> Russian Postgres Company
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-19 12:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 12:53 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-19 14:36 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-21 19:46 ` Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-22 03:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Arthur Zakirov @ 2018-09-21 19:46 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
On Wed, Sep 19, 2018 at 04:36:40PM +0200, Pavel Stehule wrote:
> ON COMMIT DROP is used only for temp variables (transaction or not
> transaction). The purpose is same like for tables. Sometimes you can to
> have object with shorter life than is session.
>
> ON TRANSACTION END RESET has sense mainly for not transaction variables. I
> see two use cases.
>
> 1. protect some sensitive data - on transaction end guaranteed reset and
> cleaning on end transaction. So you can be sure, so variable is not
> initialized (has default value), or you are inside transaction.
>
> 2. automatic initialization - ON TRANSACTION END RESET ensure so variable
> is in init state for any transaction.
>
> Both cases has sense for transaction or not transaction variables.
>
> I am thinking so transaction life time for content has sense. Is cheaper to
> reset variable than drop it (what ON COMMIT DROP does)
>
> What do you think?
Thanks, I understood the cases.
But I think there is more sense to use these options only with transactional
variables. It is more consistent and simple for me.
As a summary, it is 1 voice vs 1 voice :) So it is better to leave the
syntax as is without changes for now.
--
Arthur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-19 12:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 12:53 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-19 14:36 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-21 19:46 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
@ 2018-09-22 03:35 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-09-22 03:35 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
pá 21. 9. 2018 v 21:46 odesílatel Arthur Zakirov <a.zakirov@postgrespro.ru>
napsal:
> On Wed, Sep 19, 2018 at 04:36:40PM +0200, Pavel Stehule wrote:
> > ON COMMIT DROP is used only for temp variables (transaction or not
> > transaction). The purpose is same like for tables. Sometimes you can to
> > have object with shorter life than is session.
> >
> > ON TRANSACTION END RESET has sense mainly for not transaction variables.
> I
> > see two use cases.
> >
> > 1. protect some sensitive data - on transaction end guaranteed reset and
> > cleaning on end transaction. So you can be sure, so variable is not
> > initialized (has default value), or you are inside transaction.
> >
> > 2. automatic initialization - ON TRANSACTION END RESET ensure so variable
> > is in init state for any transaction.
> >
> > Both cases has sense for transaction or not transaction variables.
> >
> > I am thinking so transaction life time for content has sense. Is cheaper
> to
> > reset variable than drop it (what ON COMMIT DROP does)
> >
> > What do you think?
>
> Thanks, I understood the cases.
>
> But I think there is more sense to use these options only with
> transactional
> variables. It is more consistent and simple for me.
>
I agree so it can be hard to imagine - and if I return back to start
discussion about schema variables - it can be hard because it joins some
concepts - variables has persistent transactional metadata, but the content
is not transactional.
I don't think so the variability is a issue in this case. There is a lot of
examples, so lot of combinations are possible - global temp tables and
package variables (Oracle), local temp tables and local variables
(Postgres), session variables and memory tables (MSSQL). Any combination of
feature has cases where can be very practical and useful.
ON TRANSACTION END RESET can be useful, because we have not a session event
triggers (and in this moment I am not sure if it is necessary and practical
- their usage can be very fragile). But some work can do ON xxx clauses,
that should not to have negative impact on performance or fragility.
ON TRANSACTION END RESET ensure cleaned and initialized to default value
for any transaction. Other possibility is ON COMMAND END RESET (but I would
not to implement it now), ...
> As a summary, it is 1 voice vs 1 voice :) So it is better to leave the
> syntax as is without changes for now.
>
:) now is enough time to think about syntax. Some features can be removed
and returned back later, where this concept will be more absorbed.
Regards
Pavel
>
> --
> Arthur Zakirov
> Postgres Professional: http://www.postgrespro.com
> Russian Postgres Company
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
@ 2018-09-20 09:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-20 09:08 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
st 19. 9. 2018 v 13:23 odesílatel Arthur Zakirov <a.zakirov@postgrespro.ru>
napsal:
> Hello,
>
> On Wed, Sep 19, 2018 at 10:30:31AM +0200, Pavel Stehule wrote:
> > Hi
> >
> > new update:
> >
> > I fixed pg_restore, and I cleaned a code related to transaction
> processing
> >
> > There should be a full functionality now.
>
> I reviewed a little bit the patch. I have a few comments.
>
> > <title><structname>pg_views</structname> Columns</title>
>
> I think there is a typo here. It should be "pg_variable".
>
fixed
> > - ONCOMMIT_DROP /* ON COMMIT DROP */
> > + ONCOMMIT_DROP, /* ON COMMIT DROP */
> > } OnCommitAction;
>
> There is the extra comma here after ONCOMMIT_DROP.
>
fixed
Thank you for comments
attached updated patch
> 1 - https://www.postgresql.org/docs/current/static/sql-createtable.html
>
> --
> Arthur Zakirov
> Postgres Professional: http://www.postgrespro.com
> Russian Postgres Company
>
Attachments:
[application/gzip] schema-variables-20180920-01.patch.gz (64.5K, ../../CAFj8pRCO+S9JVi+8L8MT7pcpz2+-orj5ju8k=e+PqZBhdYPhXg@mail.gmail.com/3-schema-variables-20180920-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-22 06:00 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-22 06:00 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
rebased against yesterday changes in tab-complete.c
Regards
Pavel
Attachments:
[application/gzip] schema-variables-20180922-01.patch.gz (64.1K, ../../CAFj8pRCB7ZDgTWD2BLaiATFEg_VpVgqTGkJwwd4t1hMTZ=4W=Q@mail.gmail.com/3-schema-variables-20180922-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-29 08:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-09-29 08:34 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
so 22. 9. 2018 v 8:00 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> rebased against yesterday changes in tab-complete.c
>
rebased against last changes in master
Regards
Pavel
> Regards
>
> Pavel
>
Attachments:
[application/gzip] schema-variables-20180929-01.patch.gz (64.1K, ../../CAFj8pRAc_=XEpS7csp0bozopsCwLc3z-C+_46RUYdPB7kxbNAw@mail.gmail.com/3-schema-variables-20180929-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-09-29 22:19 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-10-02 23:01 ` Re: [HACKERS] proposal: schema variables Thomas Munro <thomas.munro@enterprisedb.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-23 12:50 ` Re: [HACKERS] proposal: schema variables Erik Rijkers <er@xs4all.nl>
0 siblings, 3 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-09-29 22:19 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
so 29. 9. 2018 v 10:34 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
>
>
> so 22. 9. 2018 v 8:00 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
>
>> Hi
>>
>> rebased against yesterday changes in tab-complete.c
>>
>
> rebased against last changes in master
>
+ using content of schema variable for estimation
+ subtransaction support
I hope so now, there are almost complete functionality. Please, check it.
Regards
Pavel
>
> Regards
>
> Pavel
>
>
>
>> Regards
>>
>> Pavel
>>
>
Attachments:
[application/gzip] schema-variables-20180929-02.patch.gz (65.7K, ../../CAFj8pRBSTLxQXcGXruo3zRDBdk378gA+nsi9FM9SxhV1qa0iCw@mail.gmail.com/3-schema-variables-20180929-02.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-10-02 23:01 ` Thomas Munro <thomas.munro@enterprisedb.com>
2018-10-05 01:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2 siblings, 1 reply; 433+ messages in thread
From: Thomas Munro @ 2018-10-02 23:01 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Arthur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; gilles.darold@dalibo.com, PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
On Sun, Sep 30, 2018 at 11:20 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:
> I hope so now, there are almost complete functionality. Please, check it.
Hi Pavel,
FYI there is a regression test failure on Windows:
plpgsql ... FAILED
*** 4071,4077 ****
end;
$$ language plpgsql;
select stacked_diagnostics_test();
- NOTICE: sqlstate: 22012, message: division by zero, context:
[PL/pgSQL function zero_divide() line 4 at RETURN <- SQL statement
"SELECT zero_divide()" <- PL/pgSQL function stacked_diagnostics_test()
line 6 at PERFORM]
+ NOTICE: sqlstate: 42702, message: column reference "v" is ambiguous,
context: [PL/pgSQL function zero_divide() line 4 at RETURN <- SQL
statement "SELECT zero_divide()" <- PL/pgSQL function
stacked_diagnostics_test() line 6 at PERFORM]
https://ci.appveyor.com/project/postgresql-cfbot/postgresql/build/1.0.15234
--
Thomas Munro
http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-02 23:01 ` Re: [HACKERS] proposal: schema variables Thomas Munro <thomas.munro@enterprisedb.com>
@ 2018-10-05 01:34 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-10-05 01:34 UTC (permalink / raw)
To: Thomas Munro <thomas.munro@enterprisedb.com>; +Cc: Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
st 3. 10. 2018 v 1:01 odesílatel Thomas Munro <thomas.munro@enterprisedb.com>
napsal:
> On Sun, Sep 30, 2018 at 11:20 AM Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> > I hope so now, there are almost complete functionality. Please, check it.
>
> Hi Pavel,
>
> FYI there is a regression test failure on Windows:
>
> plpgsql ... FAILED
>
> *** 4071,4077 ****
> end;
> $$ language plpgsql;
> select stacked_diagnostics_test();
> - NOTICE: sqlstate: 22012, message: division by zero, context:
> [PL/pgSQL function zero_divide() line 4 at RETURN <- SQL statement
> "SELECT zero_divide()" <- PL/pgSQL function stacked_diagnostics_test()
> line 6 at PERFORM]
> + NOTICE: sqlstate: 42702, message: column reference "v" is ambiguous,
> context: [PL/pgSQL function zero_divide() line 4 at RETURN <- SQL
> statement "SELECT zero_divide()" <- PL/pgSQL function
> stacked_diagnostics_test() line 6 at PERFORM]
>
> https://ci.appveyor.com/project/postgresql-cfbot/postgresql/build/1.0.15234
please, check attached patch
Thank you for report
Pavel
>
> --
> Thomas Munro
> http://www.enterprisedb.com
>
Attachments:
[application/gzip] schema-variables-20181004-02.patch.gz (65.7K, ../../CAFj8pRCTbd2PrwcTd7hj-bctsVD1E5cpke_Z27k-vcgwGvLvLw@mail.gmail.com/3-schema-variables-20181004-02.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-10-07 17:13 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2018-10-07 17:13 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
ne 30. 9. 2018 v 0:19 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
>
>
> so 29. 9. 2018 v 10:34 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
>
>>
>>
>> so 22. 9. 2018 v 8:00 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
>> napsal:
>>
>>> Hi
>>>
>>> rebased against yesterday changes in tab-complete.c
>>>
>>
>> rebased against last changes in master
>>
>
> + using content of schema variable for estimation
> + subtransaction support
>
> I hope so now, there are almost complete functionality. Please, check it.
>
new update
minor white space issue
one more regress test and 2 pg_dump tests
Regards
Pavel
>
> Regards
>
> Pavel
>
>
>>
>> Regards
>>
>> Pavel
>>
>>
>>
>>> Regards
>>>
>>> Pavel
>>>
>>
Attachments:
[application/gzip] schema-variables-20181007-01.patch.gz (48.3K, ../../CAFj8pRAR03rRfAsbYpsP-NDq3npWZdiQoX9vkrX2HSuA9COuUg@mail.gmail.com/3-schema-variables-20181007-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-11-21 07:24 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-11-30 23:17 ` Re: [HACKERS] proposal: schema variables Dmitry Dolgov <9erthalion6@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-11-21 07:24 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
just rebase
Regards
Pavel
Attachments:
[application/gzip] schema-variables-20181121-01.patch.gz (65.8K, ../../CAFj8pRBrc6AULAT6e4cAbd86Y4qjTP9app=TsU15Hq=_2NNYfA@mail.gmail.com/3-schema-variables-20181121-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-11-30 23:17 ` Dmitry Dolgov <9erthalion6@gmail.com>
2018-12-01 06:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Dmitry Dolgov @ 2018-11-30 23:17 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Artur Zakirov <a.zakirov@postgrespro.ru>; dean.a.rasheed@gmail.com, Fabien COELHO <coelho@cri.ensmp.fr>; gilles.darold@dalibo.com, PostgreSQL Developers <pgsql-hackers@lists.postgresql.org>
> On Wed, Nov 21, 2018 at 8:25 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:
>
> just rebase
Thanks for working on this patch.
I'm a bit confused, but cfbot again says that there are some conflicts.
Probably they are the minor one, from src/bin/psql/help.c
For now I'm moving it to the next CF.
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-30 23:17 ` Re: [HACKERS] proposal: schema variables Dmitry Dolgov <9erthalion6@gmail.com>
@ 2018-12-01 06:32 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-12-01 06:32 UTC (permalink / raw)
To: Dmitry Dolgov <9erthalion6@gmail.com>; +Cc: Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
so 1. 12. 2018 v 0:16 odesílatel Dmitry Dolgov <9erthalion6@gmail.com>
napsal:
> > On Wed, Nov 21, 2018 at 8:25 AM Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> >
> > just rebase
>
> Thanks for working on this patch.
>
> I'm a bit confused, but cfbot again says that there are some conflicts.
> Probably they are the minor one, from src/bin/psql/help.c
>
rebased again
Regards
Pavel
> For now I'm moving it to the next CF.
>
Attachments:
[application/gzip] schema-variables-20181201-01.patch.gz (65.7K, ../../CAFj8pRA27EuadmJP=Df-PdYxSJ_x5ySkaRHZzfbjY_Ph7Yc7CQ@mail.gmail.com/3-schema-variables-20181201-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-12-31 13:23 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 15:40 ` Re: [HACKERS] proposal: schema variables Erik Rijkers <er@xs4all.nl>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-12-31 13:23 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
st 21. 11. 2018 v 8:24 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> just rebase
>
>
rebase
Regards
Pavel
>
> Regards
>
> Pavel
>
Attachments:
[application/gzip] schema-variables-20181231-01.patch.gz (65.4K, ../../CAFj8pRAOB0UMA_FJy6dOHLSDEZDN6B9zV33LQFebSEgHQKBY4A@mail.gmail.com/3-schema-variables-20181231-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2018-12-31 15:40 ` Erik Rijkers <er@xs4all.nl>
2018-12-31 17:33 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Erik Rijkers @ 2018-12-31 15:40 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
On 2018-12-31 14:23, Pavel Stehule wrote:
> st 21. 11. 2018 v 8:24 odesílatel Pavel Stehule
> <pavel.stehule@gmail.com>
> [schema-variables-20181231-01.patch.gz]
Hi Pavel,
I gave this a quick try-out with the script I had from previous
versions,
and found these two errors:
------------
drop schema if exists schema1 cascade;
create schema if not exists schema1;
drop variable if exists schema1.myvar1; --> error 49
create variable schema1.myvar1 as text ;
select schema1.myvar1;
let schema1.myvar1 = 'variable value ""';
select schema1.myvar1;
alter variable schema1.myvar1 rename to myvar2;
select schema1.myvar2;
create variable schema1.myvar1 as text ;
let schema1.myvar1 = 'variable value ""';
select schema1.myvar1;
alter variable schema1.myvar1 rename to myvar2; --> error 4287
select schema1.myvar2;
------------
The above, ran with psql -qXa gives the following output:
drop schema if exists schema1 cascade;
create schema if not exists schema1;
drop variable if exists schema1.myvar1; --> error 49
ERROR: unrecognized object type: 49
create variable schema1.myvar1 as text ;
select schema1.myvar1;
myvar1
--------
(1 row)
let schema1.myvar1 = 'variable value ""';
select schema1.myvar1;
myvar1
-------------------
variable value ""
(1 row)
alter variable schema1.myvar1 rename to myvar2;
select schema1.myvar2;
myvar2
-------------------
variable value ""
(1 row)
create variable schema1.myvar1 as text ;
let schema1.myvar1 = 'variable value ""';
select schema1.myvar1;
myvar1
-------------------
variable value ""
(1 row)
alter variable schema1.myvar1 rename to myvar2; --> error 4287
ERROR: unsupported object class 4287
select schema1.myvar2;
myvar2
-------------------
variable value ""
(1 row)
thanks,
Erik Rijkers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 15:40 ` Re: [HACKERS] proposal: schema variables Erik Rijkers <er@xs4all.nl>
@ 2018-12-31 17:33 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2018-12-31 17:33 UTC (permalink / raw)
To: Erik Rijkers <er@xs4all.nl>; +Cc: Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
po 31. 12. 2018 v 16:40 odesílatel Erik Rijkers <er@xs4all.nl> napsal:
> On 2018-12-31 14:23, Pavel Stehule wrote:
> > st 21. 11. 2018 v 8:24 odesílatel Pavel Stehule
> > <pavel.stehule@gmail.com>
>
> > [schema-variables-20181231-01.patch.gz]
>
> Hi Pavel,
>
> I gave this a quick try-out with the script I had from previous
> versions,
> and found these two errors:
>
> ------------
> drop schema if exists schema1 cascade;
> create schema if not exists schema1;
> drop variable if exists schema1.myvar1; --> error 49
> create variable schema1.myvar1 as text ;
> select schema1.myvar1;
> let schema1.myvar1 = 'variable value ""';
> select schema1.myvar1;
> alter variable schema1.myvar1 rename to myvar2;
> select schema1.myvar2;
> create variable schema1.myvar1 as text ;
> let schema1.myvar1 = 'variable value ""';
> select schema1.myvar1;
> alter variable schema1.myvar1 rename to myvar2; --> error 4287
> select schema1.myvar2;
> ------------
>
>
> The above, ran with psql -qXa gives the following output:
>
> drop schema if exists schema1 cascade;
> create schema if not exists schema1;
> drop variable if exists schema1.myvar1; --> error 49
> ERROR: unrecognized object type: 49
> create variable schema1.myvar1 as text ;
> select schema1.myvar1;
> myvar1
> --------
>
> (1 row)
>
> let schema1.myvar1 = 'variable value ""';
> select schema1.myvar1;
> myvar1
> -------------------
> variable value ""
> (1 row)
>
> alter variable schema1.myvar1 rename to myvar2;
> select schema1.myvar2;
> myvar2
> -------------------
> variable value ""
> (1 row)
>
> create variable schema1.myvar1 as text ;
> let schema1.myvar1 = 'variable value ""';
> select schema1.myvar1;
> myvar1
> -------------------
> variable value ""
> (1 row)
>
> alter variable schema1.myvar1 rename to myvar2; --> error 4287
> ERROR: unsupported object class 4287
> select schema1.myvar2;
> myvar2
> -------------------
> variable value ""
> (1 row)
>
>
Should be fixed now.
Thank you for report
Pavel
>
> thanks,
>
>
> Erik Rijkers
>
>
>
Attachments:
[application/gzip] schema-variables-20181231-02.patch.gz (65.7K, ../../CAFj8pRDxE1uSup-CLZScXd3=iH7JwK2C6VbV4vbjCyYaXU6PRg@mail.gmail.com/3-schema-variables-20181231-02.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-01-22 19:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-01-22 19:32 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
fresh rebased patch, no other changes
Pavel
Attachments:
[application/gzip] schema-variables-20190122-01.patch.gz (65.7K, ../../CAFj8pRCiGW4vF+RZrTC87-44_xf7zALZedQArA0=S7U_dh2qjw@mail.gmail.com/3-schema-variables-20190122-01.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-01-30 16:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-01-30 16:34 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
just rebase
Regards
Pavel
Attachments:
[application/gzip] schema-variables-20190130.patch.gz (65.7K, ../../CAFj8pRC9Xi=C4BXEXLMw_ba8AQZEs4a2ZGMOg6kpwMeo-wXrAg@mail.gmail.com/3-schema-variables-20190130.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-01-31 11:49 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-01-31 11:49 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
just rebase
regards
Pavel
Attachments:
[application/gzip] schema-variables-20190131.patch.gz (65.7K, ../../CAFj8pRAG_u19iL98Pt7Qidqoq-mfKhof6gK4kOT_EBvqnHdrOg@mail.gmail.com/3-schema-variables-20190131.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-03-03 20:27 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-07 06:52 ` Re: Re: [HACKERS] proposal: schema variables David Steele <david@pgmasters.net>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2019-03-03 20:27 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
čt 31. 1. 2019 v 12:49 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> just rebase
>
> regards
>
> Pavel
>
rebase and fix compilation due changes related pg_dump
Regards
Pavel
Attachments:
[application/gzip] schema-variables-20190303.patch.gz (66.1K, ../../CAFj8pRBQuKANoiQpUf5eSqummJ2v33UOePcaM7PTHCd9Szk1xg@mail.gmail.com/3-schema-variables-20190303.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-03-07 06:52 ` David Steele <david@pgmasters.net>
2019-03-07 08:10 ` Re: Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
1 sibling, 1 reply; 433+ messages in thread
From: David Steele @ 2019-03-07 06:52 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Peter Eisentraut <peter.eisentraut@2ndquadrant.com>
On 3/3/19 10:27 PM, Pavel Stehule wrote:
>
> rebase and fix compilation due changes related pg_dump
This patch hasn't receive any review in a while and I'm not sure if
that's because nobody is interested or the reviewers think it does not
need any more review.
It seems to me that this patch as implemented does not quite satisfy any
one.
I think we need to hear something from the reviewers soon or I'll push
this patch to PG13 as Andres recommends [1].
--
-David
david@pgmasters.net
[1]
https://www.postgresql.org/message-id/20190216054526.zss2cufdxfeudr4i%40alap3.anarazel.de
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-07 06:52 ` Re: Re: [HACKERS] proposal: schema variables David Steele <david@pgmasters.net>
@ 2019-03-07 08:10 ` Fabien COELHO <coelho@cri.ensmp.fr>
2019-03-07 08:32 ` Re: Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-07 09:26 ` Re: [HACKERS] proposal: schema variables David Steele <david@pgmasters.net>
0 siblings, 2 replies; 433+ messages in thread
From: Fabien COELHO @ 2019-03-07 08:10 UTC (permalink / raw)
To: David Steele <david@pgmasters.net>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Peter Eisentraut <peter.eisentraut@2ndquadrant.com>
Hello David,
> This patch hasn't receive any review in a while and I'm not sure if that's
> because nobody is interested or the reviewers think it does not need any more
> review.
>
> It seems to me that this patch as implemented does not quite satisfy any one.
>
> I think we need to hear something from the reviewers soon or I'll push this
> patch to PG13 as Andres recommends [1].
I have discussed the feature extensively with Pavel on the initial thread.
My strong opinion based on the underlying use case is that it that such
session variables should be transactional by default, and Pavel strong
opinion is that they should not, to be closer to Oracle comparable
feature.
According to the documentation, the current implementation does provide a
transactional feature. However, it is not the default behavior, so I'm in
disagreement on a key feature, although I do really appreciate that Pavel
implemented the transactional behavior.
Otherwise, ISTM that they could be named "SESSION VARIABLE" because the
variable only exists in memory, in a session, and we could thing of adding
other kind of variables later on.
I do intend to review it in depth when it is transactional by default.
Anyway, the patch is non trivial and very large, so targetting v12 now is
indeed out of reach.
--
Fabien.
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-07 06:52 ` Re: Re: [HACKERS] proposal: schema variables David Steele <david@pgmasters.net>
2019-03-07 08:10 ` Re: Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
@ 2019-03-07 08:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-07 08:37 ` Re: Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-03-07 08:32 UTC (permalink / raw)
To: Fabien COELHO <coelho@cri.ensmp.fr>; +Cc: David Steele <david@pgmasters.net>; Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Peter Eisentraut <peter.eisentraut@2ndquadrant.com>
čt 7. 3. 2019 v 9:10 odesílatel Fabien COELHO <coelho@cri.ensmp.fr> napsal:
>
> Hello David,
>
> > This patch hasn't receive any review in a while and I'm not sure if
> that's
> > because nobody is interested or the reviewers think it does not need any
> more
> > review.
> >
> > It seems to me that this patch as implemented does not quite satisfy any
> one.
> >
> > I think we need to hear something from the reviewers soon or I'll push
> this
> > patch to PG13 as Andres recommends [1].
>
> I have discussed the feature extensively with Pavel on the initial thread.
>
> My strong opinion based on the underlying use case is that it that such
> session variables should be transactional by default, and Pavel strong
> opinion is that they should not, to be closer to Oracle comparable
> feature.
>
> According to the documentation, the current implementation does provide a
> transactional feature. However, it is not the default behavior, so I'm in
> disagreement on a key feature, although I do really appreciate that Pavel
> implemented the transactional behavior.
>
> Otherwise, ISTM that they could be named "SESSION VARIABLE" because the
> variable only exists in memory, in a session, and we could thing of adding
> other kind of variables later on.
>
> I do intend to review it in depth when it is transactional by default.
>
I am sorry. I cannot to support this request. Variables are not
transactional. My opinion is strong in this part.
I would not to repeat this discussion from start. I am sorry.
Regards
Pavel
> Anyway, the patch is non trivial and very large, so targetting v12 now is
> indeed out of reach.
>
> --
> Fabien.
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-07 06:52 ` Re: Re: [HACKERS] proposal: schema variables David Steele <david@pgmasters.net>
2019-03-07 08:10 ` Re: Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2019-03-07 08:32 ` Re: Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-03-07 08:37 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2019-03-07 08:37 UTC (permalink / raw)
To: Fabien COELHO <coelho@cri.ensmp.fr>; +Cc: David Steele <david@pgmasters.net>; Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Peter Eisentraut <peter.eisentraut@2ndquadrant.com>
Hi
>> My strong opinion based on the underlying use case is that it that such
>> session variables should be transactional by default, and Pavel strong
>> opinion is that they should not, to be closer to Oracle comparable
>> feature.
>
>
It is closer to any known database Oracle, DB2, Firebird, MSSQL, MySQL,
Regards
Pavel
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-07 06:52 ` Re: Re: [HACKERS] proposal: schema variables David Steele <david@pgmasters.net>
2019-03-07 08:10 ` Re: Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
@ 2019-03-07 09:26 ` David Steele <david@pgmasters.net>
1 sibling, 0 replies; 433+ messages in thread
From: David Steele @ 2019-03-07 09:26 UTC (permalink / raw)
To: pgsql-hackers@lists.postgresql.org
On 3/7/19 10:10 AM, Fabien COELHO wrote:
>
> Anyway, the patch is non trivial and very large, so targetting v12 now
> is indeed out of reach.
Agreed. I have set the target version to PG13.
Regards,
--
-David
david@pgmasters.net
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-03-24 05:57 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 09:25 ` Re: [HACKERS] proposal: schema variables Erik Rijkers <er@xs4all.nl>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2019-03-24 05:57 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
rebase against current master
Regards
Pavel
Attachments:
[application/gzip] schema-variables-20190324.patch.gz (66.1K, ../../CAFj8pRA=9H4ve18H_hqS0OqnBQaxeA7ssP67pOkH86Ax53wU4g@mail.gmail.com/3-schema-variables-20190324.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-03-24 09:25 ` Erik Rijkers <er@xs4all.nl>
2019-03-24 09:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Erik Rijkers @ 2019-03-24 09:25 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
On 2019-03-24 06:57, Pavel Stehule wrote:
> Hi
>
> rebase against current master
>
I ran into this:
(schema 'varschema2' does not exist):
drop variable varschema2.testv cascade;
ERROR: schema "varschema2" does not exist
create variable if not exists testv as text;
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
connection to server was lost
(both statements are needed to force the crash)
thanks,
Erik Rijkers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 09:25 ` Re: [HACKERS] proposal: schema variables Erik Rijkers <er@xs4all.nl>
@ 2019-03-24 09:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-25 19:40 ` Re: [HACKERS] proposal: schema variables Erik Rijkers <er@xs4all.nl>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-03-24 09:32 UTC (permalink / raw)
To: Erik Rijkers <er@xs4all.nl>; +Cc: Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
ne 24. 3. 2019 v 10:25 odesílatel Erik Rijkers <er@xs4all.nl> napsal:
> On 2019-03-24 06:57, Pavel Stehule wrote:
> > Hi
> >
> > rebase against current master
> >
>
> I ran into this:
>
> (schema 'varschema2' does not exist):
>
> drop variable varschema2.testv cascade;
> ERROR: schema "varschema2" does not exist
> create variable if not exists testv as text;
> server closed the connection unexpectedly
> This probably means the server terminated abnormally
> before or while processing the request.
> connection to server was lost
>
>
> (both statements are needed to force the crash)
>
I cannot to reproduce it.
please, try compilation with "make distclean"; configure ..
or if the problem persists, please send test case, or backtrace
Regards
Pavel
>
>
> thanks,
>
> Erik Rijkers
>
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 09:25 ` Re: [HACKERS] proposal: schema variables Erik Rijkers <er@xs4all.nl>
2019-03-24 09:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-03-25 19:40 ` Erik Rijkers <er@xs4all.nl>
2019-03-26 05:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Erik Rijkers @ 2019-03-25 19:40 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
On 2019-03-24 10:32, Pavel Stehule wrote:
> ne 24. 3. 2019 v 10:25 odesílatel Erik Rijkers <er@xs4all.nl> napsal:
>
>> On 2019-03-24 06:57, Pavel Stehule wrote:
>> > Hi
>> >
>> > rebase against current master
>>
>> I ran into this:
>>
>> (schema 'varschema2' does not exist):
>>
>> drop variable varschema2.testv cascade;
>> ERROR: schema "varschema2" does not exist
>> create variable if not exists testv as text;
>> server closed the connection unexpectedly
>> This probably means the server terminated abnormally
>> before or while processing the request.
>> connection to server was lost
>>
>>
>> (both statements are needed to force the crash)
>>
>
> I cannot to reproduce it.
> [backtrace and stuff]
Sorry, I don't have the wherewithal to get more info but I have repeated
this now on 4 different machines (debian jessie/stretch; centos).
I did notice that sometimes those two offending lines
"
drop variable varschema2.testv cascade;
create variable if not exists testv as text;
"
have to be repeated a few times (never more than 4 or 5 times) before
the crash occurs (signal 11: Segmentation fault).
Erik Rijkers
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 09:25 ` Re: [HACKERS] proposal: schema variables Erik Rijkers <er@xs4all.nl>
2019-03-24 09:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-25 19:40 ` Re: [HACKERS] proposal: schema variables Erik Rijkers <er@xs4all.nl>
@ 2019-03-26 05:41 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2019-03-26 05:41 UTC (permalink / raw)
To: Erik Rijkers <er@xs4all.nl>; +Cc: Artur Zakirov <a.zakirov@postgrespro.ru>; Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
po 25. 3. 2019 v 20:40 odesílatel Erik Rijkers <er@xs4all.nl> napsal:
> On 2019-03-24 10:32, Pavel Stehule wrote:
> > ne 24. 3. 2019 v 10:25 odesílatel Erik Rijkers <er@xs4all.nl> napsal:
> >
> >> On 2019-03-24 06:57, Pavel Stehule wrote:
> >> > Hi
> >> >
> >> > rebase against current master
> >>
> >> I ran into this:
> >>
> >> (schema 'varschema2' does not exist):
> >>
> >> drop variable varschema2.testv cascade;
> >> ERROR: schema "varschema2" does not exist
> >> create variable if not exists testv as text;
> >> server closed the connection unexpectedly
> >> This probably means the server terminated abnormally
> >> before or while processing the request.
> >> connection to server was lost
> >>
> >>
> >> (both statements are needed to force the crash)
> >>
> >
> > I cannot to reproduce it.
> > [backtrace and stuff]
>
> Sorry, I don't have the wherewithal to get more info but I have repeated
> this now on 4 different machines (debian jessie/stretch; centos).
>
> I did notice that sometimes those two offending lines
> "
> drop variable varschema2.testv cascade;
> create variable if not exists testv as text;
> "
> have to be repeated a few times (never more than 4 or 5 times) before
> the crash occurs (signal 11: Segmentation fault).
>
Should be fixed now.
Thank you for report
Pavel
>
> Erik Rijkers
>
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-03-26 05:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-03-26 05:40 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
ne 24. 3. 2019 v 6:57 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> rebase against current master
>
fixed issue IF NOT EXISTS & related regress tests
Regards
Pavel
> Regards
>
> Pavel
>
Attachments:
[application/gzip] schema-variables-20190326.patch.gz (66.1K, ../../CAFj8pRCq6+pFzZ7XvkF=o8D7E3R4UO5H=foKPvhyAzv_K4dADQ@mail.gmail.com/3-schema-variables-20190326.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-04-02 18:02 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-04-02 18:02 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
út 26. 3. 2019 v 6:40 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> ne 24. 3. 2019 v 6:57 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
>
>> Hi
>>
>> rebase against current master
>>
>
>
> fixed issue IF NOT EXISTS & related regress tests
>
another rebase
Regards
Pavel
> Regards
>
> Pavel
>
>
>> Regards
>>
>> Pavel
>>
>
Attachments:
[application/gzip] schema-variables-20190402.patch.gz (66.2K, ../../CAFj8pRB0yi6sZTfB8ODaDWkzriuiSFJvfFn9dKmCHzPsK3bu+A@mail.gmail.com/3-schema-variables-20190402.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-05-09 04:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-05-09 04:34 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
rebased patch
Regards
Pavel
Attachments:
[application/gzip] schema-variables-20190509.patch.gz (65.9K, ../../CAFj8pRD__tbzmkr458vgJb2EhVoLk7_r3o8o6qznJiDut2L7kg@mail.gmail.com/3-schema-variables-20190509.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-05-24 17:12 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-05-24 17:12 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
čt 9. 5. 2019 v 6:34 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> rebased patch
>
rebase after pgindent
Regards
Pavel
>
> Regards
>
> Pavel
>
>
>
Attachments:
[application/gzip] schema-variables-20190524.patch.gz (65.9K, ../../CAFj8pRDNGq2gUzRKZgia6javnjEQhzbQX4i-n+A=pP1JnH0evQ@mail.gmail.com/3-schema-variables-20190524.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-06-30 03:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-06-30 03:10 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
pá 24. 5. 2019 v 19:12 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> čt 9. 5. 2019 v 6:34 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
>
>> Hi
>>
>> rebased patch
>>
>
> rebase after pgindent
>
fresh rebase
Regards
Pavel
> Regards
>
> Pavel
>
>>
>> Regards
>>
>> Pavel
>>
>>
>>
Attachments:
[application/gzip] schema-variables-20190630.patch.gz (65.9K, ../../CAFj8pRDog1b+66-8ZW0_7JmKr_hbu1fs+CKrEkfS+RU=ZJNdGw@mail.gmail.com/3-schema-variables-20190630.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-07-16 12:50 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-07-16 12:50 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
ne 30. 6. 2019 v 5:10 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
>
>
> pá 24. 5. 2019 v 19:12 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
>
>> Hi
>>
>> čt 9. 5. 2019 v 6:34 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
>> napsal:
>>
>>> Hi
>>>
>>> rebased patch
>>>
>>
>> rebase after pgindent
>>
>
> fresh rebase
>
just rebase again
Regards
Pavel
> Regards
>
> Pavel
>
>
>> Regards
>>
>> Pavel
>>
>>>
>>> Regards
>>>
>>> Pavel
>>>
>>>
>>>
Attachments:
[application/gzip] schema-variables-20190716.patch.gz (65.9K, ../../CAFj8pRBrMMQAacfEzS5t0FE8ruNzDRvFAYiJgTsGwwR6NXygcg@mail.gmail.com/3-schema-variables-20190716.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-08-10 07:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-08-10 07:10 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; Gilles Darold <gilles.darold@dalibo.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
just rebase
Regards
Pavel
Attachments:
[application/gzip] schema-variables-rebase-20190810.patch.gz (65.9K, ../../CAFj8pRBmsqOHWJOFx5WSmopHVzTMsp8oRYN5EKnP+5kHBGXekg@mail.gmail.com/3-schema-variables-rebase-20190810.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-10-04 04:12 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-10-04 04:12 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Alvaro Herrera <alvherre@2ndquadrant.com>
Hi
so 10. 8. 2019 v 9:10 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> just rebase
>
fresh rebase
Regards
Pavel
> Regards
>
> Pavel
>
Attachments:
[application/gzip] schema-variables-20191004.patch.gz (65.9K, ../../CAFj8pRA9tK92h8bkaN_S-R1ZuayGfwSzDCpLdzbMRCLZwF_xJw@mail.gmail.com/3-schema-variables-20191004.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-10-10 09:41 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-10-10 09:41 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Alvaro Herrera <alvherre@2ndquadrant.com>
Hi
minor change - replace heap_tuple_fetch_attr by detoast_external_attr.
Regards
Pavel
pá 4. 10. 2019 v 6:12 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> so 10. 8. 2019 v 9:10 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
>
>> Hi
>>
>> just rebase
>>
>
> fresh rebase
>
> Regards
>
> Pavel
>
>
>> Regards
>>
>> Pavel
>>
>
Attachments:
[application/gzip] schema_variables-20191010.patch.gz (65.9K, ../../CAFj8pRA5hjcwvjD0LB3d6d16O0WQKxwg9633XeTeEvsLj-8YUw@mail.gmail.com/3-schema_variables-20191010.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-11-03 16:27 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-11-03 16:27 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Alvaro Herrera <alvherre@2ndquadrant.com>
čt 10. 10. 2019 v 11:41 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> minor change - replace heap_tuple_fetch_attr by detoast_external_attr.
>
>
similar update - heap_open, heap_close was replaced by table_open,
table_close
Regards
Pavel
Attachments:
[application/gzip] schema_variables-20191103.patch.gz (65.9K, ../../CAFj8pRBaAju_Ki3ENZXK=KqOGgo69ZT=hFSG6=Vd8=eaAHcn6A@mail.gmail.com/3-schema_variables-20191103.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-11-18 18:47 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-11-18 18:47 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Alvaro Herrera <alvherre@2ndquadrant.com>
ne 3. 11. 2019 v 17:27 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
>
>
> čt 10. 10. 2019 v 11:41 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
>
>> Hi
>>
>> minor change - replace heap_tuple_fetch_attr by detoast_external_attr.
>>
>>
> similar update - heap_open, heap_close was replaced by table_open,
> table_close
>
fresh rebase
Regards
Pavel
> Regards
>
> Pavel
>
Attachments:
[application/gzip] schema-variables-20191118.patch.gz (65.5K, ../../CAFj8pRCneJ6iEGyrDtCX=MrqNVLXcSxJwnrqWxuyVTEFMqeYSQ@mail.gmail.com/3-schema-variables-20191118.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: [HACKERS] proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-12-14 21:43 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-12-14 21:43 UTC (permalink / raw)
To: Artur Zakirov <a.zakirov@postgrespro.ru>; +Cc: Dean Rasheed <dean.a.rasheed@gmail.com>; Fabien COELHO <coelho@cri.ensmp.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Alvaro Herrera <alvherre@2ndquadrant.com>
po 18. 11. 2019 v 19:47 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
>
>
> ne 3. 11. 2019 v 17:27 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
>
>>
>>
>> čt 10. 10. 2019 v 11:41 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
>> napsal:
>>
>>> Hi
>>>
>>> minor change - replace heap_tuple_fetch_attr by detoast_external_attr.
>>>
>>>
>> similar update - heap_open, heap_close was replaced by table_open,
>> table_close
>>
>
> fresh rebase
>
only rebase
Regards
Pavel
> Regards
>
> Pavel
>
>
>> Regards
>>
>> Pavel
>>
>
Attachments:
[application/gzip] schema-variables-20191214.patch.gz (65.5K, ../../CAFj8pRBNqJ0_mHSFW0ksR90f0eKzk+zABboZ1tmoH6q4T8m0nA@mail.gmail.com/3-schema-variables-20191214.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-12-22 12:03 ` Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-22 18:50 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-25 21:45 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 3 replies; 433+ messages in thread
From: Philippe BEAUDOIN @ 2019-12-22 12:03 UTC (permalink / raw)
To: pgsql-hackers@lists.postgresql.org; +Cc: Pavel Stehule <pavel.stehule@gmail.com>
The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, failed
Spec compliant: not tested
Documentation: tested, failed
Hi Pavel,
First of all, I would like to congratulate you for this great work. This patch is really cool. The lack of package variables is sometimes a blocking issue for Oracle to Postgres migrations, because the usual emulation with GUC is sometimes not enough, in particular when there are security concerns or when the database is used in a public cloud.
As I look forward to having this patch commited, I decided to spend some time to participate to the review, although I am not a C specialist and I have not a good knowledge of the Postgres internals. Here is my report.
A) Installation
The patch applies correctly and the compilation is fine. The "make check" doesn't report any issue.
B) Basic usage
I tried some simple schema variables use cases. No problem.
C) The interface
The SQL changes look good to me.
However, in the CREATE VARIABLE command, I would replace the "TRANSACTION" word by "TRANSACTIONAL".
I have also tried to replace this word by a ON ROLLBACK clause at the end of the statement, like for ON COMMIT, but I have not found a satisfying wording to propose.
D) Behaviour
I am ok with variables not being transactional by default. That's the most simple, the most efficient, it emulates the package variables of other RDBMS and it will probably fit the most common use cases.
Note that I am not strongly opposed to having by default transactional variables. But I don't know whether this change would be a great work. We would have at least to find another keyword in the CREATE VARIABLE statement. Something like "NON-TRANSACTIONAL VARIABLE" ?
It is possible to create a NOT NULL variable without DEFAULT. When trying to read the variable before a LET statement, one gets an error massage saying that the NULL value is not allowed (and the documentation is clear about this case). Just for the records, I wondered whether it wouldn't be better to forbid a NOT NULL variable creation that wouldn't have a DEFAULT value. But finally, I think this behaviour provides a good way to force the variable initialisation before its use. So let's keep it as is.
E) ACL and Rights
I played a little bit with the GRANT and REVOKE statements.
I have got an error (Issue 1). The following statement chain:
create variable public.sv1 int;
grant read on variable sv1 to other_user;
drop owned by other_user;
reports : ERROR: unexpected object class 4287
I then tried to use DEFAULT PRIVILEGES. Despite this is not documented, I successfuly performed:
alter default privileges in schema public grant read on variables to simple_user;
alter default privileges in schema public grant write on variables to simple_user;
When variables are then created, the grants are properly given.
And the psql \ddp command perfectly returns:
Default access privileges
Owner | Schema | Type | Access privileges
----------+--------+------+-------------------------
postgres | public | | simple_user=SW/postgres
(1 row)
So the ALTER DEFAULT PRIVILEGES documentation chapter has to reflect this new syntax (Issue 2).
BTW, in the ACL, the READ privilege is represented by a S letter. A comment in the source reports that the R letter was used in the past for rule privilege. Looking at the postgres sources, I see that this privilege on rules has been suppressed in 8.2, so 13 years ago. As this R letter would be a so much better choice, I wonder whether it couldn't be reused now for this new purpose. Is it important to keep this letter frozen ?
F) Extension
I then created an extension, whose installation script creates a schema variable and functions that use it. The schema variable is correctly linked to the extension, so that dropping the extension drops the variable.
But there is an issue when dumping the database (Issue 3). The script generated by pg_dump includes the CREATE EXTENSION statement as expected but also a redundant CREATE VARIABLE statement for the variable that belongs to the extension. As a result, one of course gets an error at restore time.
G) Row Level Security
I did a test activating RLS on a table and creating a POLICY that references a schema variable in its USING and WITH CHECK clauses. Everything worked fine.
H) psql
A \dV meta-command displays all the created variables.
I would change a little bit the provided view. More precisely I would:
- rename "Constraint" into "Is nullable" and report it as a boolean
- rename "Special behave" into "Is transactional" and report it as a boolean
- change the order of columns so to have:
Schema | Name | Type | Is nullable | Default | Owner | Is transactional | Transaction end action
"Is nullable" being aside "Default"
I) Performance
I just quickly looked at the performance, and didn't notice any issue.
About variables read performance, I have noticed that:
select sum(1) from generate_series(1,10000000);
and
select sum(sv1) from generate_series(1,10000000);
have similar response times.
About planning, a condition with a variable used as a constant is indexable, as if it were a literal.
J) Documentation
There are some wordings to improve in the documentation. But I am not the best person to give advice about english language ;-).
However, aside the already mentionned lack of changes in the ALTER DEFAULT PRIVILEGES chapter, I also noticed :
- line 50 of the patch, the sentence "(hidden attribute; must be explicitly selected)" looks false as the oid column of pg_variable is displayed, as for other tables of the catalog;
- at several places, the word "behave" should be replaced by "behaviour"
- line 433, a get_schema_variable() function is mentionned; is it a function that can really be called by users ?
May be it would be interesting to also add a chapter in the Section V of the documentation, in order to more globally present the schema variables concept, aside the new or the modified statements.
K) Coding
I am not able to appreciate the way the feature has been coded. So I let this for other reviewers ;-)
To conclude, again, thanks a lot for this feature !
And if I may add this. I dream of an additional feature: adding a SHARED clause to the CREATE VARIABLE statement in order to be able to create memory spaces that could be shared by all connections on the database and accessible in SQL and PL, under the protection of ACL. But that's another story ;-)
Best regards. Philippe.
The new status of this patch is: Waiting on Author
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
@ 2019-12-22 18:50 ` Pavel Stehule <pavel.stehule@gmail.com>
2 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2019-12-22 18:50 UTC (permalink / raw)
To: Philippe BEAUDOIN <phb07@apra.asso.fr>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
ne 22. 12. 2019 v 13:04 odesílatel Philippe BEAUDOIN <phb07@apra.asso.fr>
napsal:
> The following review has been posted through the commitfest application:
> make installcheck-world: tested, passed
> Implements feature: tested, failed
> Spec compliant: not tested
> Documentation: tested, failed
>
> Hi Pavel,
>
> First of all, I would like to congratulate you for this great work. This
> patch is really cool. The lack of package variables is sometimes a blocking
> issue for Oracle to Postgres migrations, because the usual emulation with
> GUC is sometimes not enough, in particular when there are security concerns
> or when the database is used in a public cloud.
>
> As I look forward to having this patch commited, I decided to spend some
> time to participate to the review, although I am not a C specialist and I
> have not a good knowledge of the Postgres internals. Here is my report.
>
> A) Installation
>
> The patch applies correctly and the compilation is fine. The "make check"
> doesn't report any issue.
>
> B) Basic usage
>
> I tried some simple schema variables use cases. No problem.
>
> C) The interface
>
> The SQL changes look good to me.
>
> However, in the CREATE VARIABLE command, I would replace the "TRANSACTION"
> word by "TRANSACTIONAL".
>
There is not technical problem - the problem is in introduction new keyword
"transactional" that is near to "transaction". I am not sure if it is
practical to have two "similar" keyword and how much the CREATE statement
has to use correct English grammar.
I am not native speaker, so I am not able to see how bad is using
"TRANSACTION" instead "TRANSACTIONAL" in this context. So I see a risk to
have two important (it is not syntactic sugar) similar keywords.
Just I afraid so using TRANSACTIONAL instead just TRANSACTION is not too
user friendly. I have not strong opinion about this - and the
implementation is easy, but I am not feel comfortable with introduction
this keyword.
> I have also tried to replace this word by a ON ROLLBACK clause at the end
> of the statement, like for ON COMMIT, but I have not found a satisfying
> wording to propose.
>
>
> D) Behaviour
>
> I am ok with variables not being transactional by default. That's the most
> simple, the most efficient, it emulates the package variables of other
> RDBMS and it will probably fit the most common use cases.
>
> Note that I am not strongly opposed to having by default transactional
> variables. But I don't know whether this change would be a great work. We
> would have at least to find another keyword in the CREATE VARIABLE
> statement. Something like "NON-TRANSACTIONAL VARIABLE" ?
>
Variables almost everywhere (global user settings - GUC is only one planet
exception) are non transactional by default. I don't see any reason
introduce new different design than is wide used.
> It is possible to create a NOT NULL variable without DEFAULT. When trying
> to read the variable before a LET statement, one gets an error massage
> saying that the NULL value is not allowed (and the documentation is clear
> about this case). Just for the records, I wondered whether it wouldn't be
> better to forbid a NOT NULL variable creation that wouldn't have a DEFAULT
> value. But finally, I think this behaviour provides a good way to force the
> variable initialisation before its use. So let's keep it as is.
>
This is a question - and there are two possibilities
postgres=# do $$
declare x int not null;
begin
raise notice '%', x;
end;
$$ ;
ERROR: variable "x" must have a default value, since it's declared NOT NULL
LINE 2: declare x int not null;
^
PLpgSQL requires it. But there is not a possibility to enforce future
setting.
So I know so behave of schema variables is little bit different, but I
think so this difference has interesting use case. You can check if the
variable was modified somewhere or not.
> E) ACL and Rights
>
> I played a little bit with the GRANT and REVOKE statements.
>
> I have got an error (Issue 1). The following statement chain:
> create variable public.sv1 int;
> grant read on variable sv1 to other_user;
> drop owned by other_user;
> reports : ERROR: unexpected object class 4287
>
this is bug and should be fixed
> I then tried to use DEFAULT PRIVILEGES. Despite this is not documented, I
> successfuly performed:
> alter default privileges in schema public grant read on variables to
> simple_user;
> alter default privileges in schema public grant write on variables to
> simple_user;
>
> When variables are then created, the grants are properly given.
> And the psql \ddp command perfectly returns:
> Default access privileges
> Owner | Schema | Type | Access privileges
> ----------+--------+------+-------------------------
> postgres | public | | simple_user=SW/postgres
> (1 row)
>
> So the ALTER DEFAULT PRIVILEGES documentation chapter has to reflect this
> new syntax (Issue 2).
>
> BTW, in the ACL, the READ privilege is represented by a S letter. A
> comment in the source reports that the R letter was used in the past for
> rule privilege. Looking at the postgres sources, I see that this privilege
> on rules has been suppressed in 8.2, so 13 years ago. As this R letter
> would be a so much better choice, I wonder whether it couldn't be reused
> now for this new purpose. Is it important to keep this letter frozen ?
>
I have not a idea why it is. I'll recheck it - but in this moment I prefer
a consistency with existing ACL - it can be in future as one block if it
will be necessary for somebody.
>
> F) Extension
>
> I then created an extension, whose installation script creates a schema
> variable and functions that use it. The schema variable is correctly linked
> to the extension, so that dropping the extension drops the variable.
>
> But there is an issue when dumping the database (Issue 3). The script
> generated by pg_dump includes the CREATE EXTENSION statement as expected
> but also a redundant CREATE VARIABLE statement for the variable that
> belongs to the extension. As a result, one of course gets an error at
> restore time.
>
It is bug and should be fixed
> G) Row Level Security
>
> I did a test activating RLS on a table and creating a POLICY that
> references a schema variable in its USING and WITH CHECK clauses.
> Everything worked fine.
>
> H) psql
>
> A \dV meta-command displays all the created variables.
> I would change a little bit the provided view. More precisely I would:
> - rename "Constraint" into "Is nullable" and report it as a boolean
> - rename "Special behave" into "Is transactional" and report it as a
> boolean
> - change the order of columns so to have:
> Schema | Name | Type | Is nullable | Default | Owner | Is transactional |
> Transaction end action
> "Is nullable" being aside "Default"
>
ok
> I) Performance
>
> I just quickly looked at the performance, and didn't notice any issue.
>
> About variables read performance, I have noticed that:
> select sum(1) from generate_series(1,10000000);
> and
> select sum(sv1) from generate_series(1,10000000);
> have similar response times.
>
> About planning, a condition with a variable used as a constant is
> indexable, as if it were a literal.
>
> J) Documentation
>
> There are some wordings to improve in the documentation. But I am not the
> best person to give advice about english language ;-).
>
> However, aside the already mentionned lack of changes in the ALTER DEFAULT
> PRIVILEGES chapter, I also noticed :
> - line 50 of the patch, the sentence "(hidden attribute; must be
> explicitly selected)" looks false as the oid column of pg_variable is
> displayed, as for other tables of the catalog;
> - at several places, the word "behave" should be replaced by "behaviour"
> - line 433, a get_schema_variable() function is mentionned; is it a
> function that can really be called by users ?
>
> May be it would be interesting to also add a chapter in the Section V of
> the documentation, in order to more globally present the schema variables
> concept, aside the new or the modified statements.
>
> K) Coding
>
> I am not able to appreciate the way the feature has been coded. So I let
> this for other reviewers ;-)
>
>
> To conclude, again, thanks a lot for this feature !
> And if I may add this. I dream of an additional feature: adding a SHARED
> clause to the CREATE VARIABLE statement in order to be able to create
> memory spaces that could be shared by all connections on the database and
> accessible in SQL and PL, under the protection of ACL. But that's another
> story ;-)
>
sure, it is another story :-).
Thank you for review - I'll try to fix bugs this week.
Pavel
> Best regards. Philippe.
>
> The new status of this patch is: Waiting on Author
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
@ 2019-12-25 21:45 ` Pavel Stehule <pavel.stehule@gmail.com>
2 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2019-12-25 21:45 UTC (permalink / raw)
To: Philippe BEAUDOIN <phb07@apra.asso.fr>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
ne 22. 12. 2019 v 13:04 odesílatel Philippe BEAUDOIN <phb07@apra.asso.fr>
napsal:
> The following review has been posted through the commitfest application:
> make installcheck-world: tested, passed
> Implements feature: tested, failed
> Spec compliant: not tested
> Documentation: tested, failed
>
> Hi Pavel,
>
> First of all, I would like to congratulate you for this great work. This
> patch is really cool. The lack of package variables is sometimes a blocking
> issue for Oracle to Postgres migrations, because the usual emulation with
> GUC is sometimes not enough, in particular when there are security concerns
> or when the database is used in a public cloud.
>
> As I look forward to having this patch commited, I decided to spend some
> time to participate to the review, although I am not a C specialist and I
> have not a good knowledge of the Postgres internals. Here is my report.
>
> A) Installation
>
> The patch applies correctly and the compilation is fine. The "make check"
> doesn't report any issue.
>
> B) Basic usage
>
> I tried some simple schema variables use cases. No problem.
>
> C) The interface
>
> The SQL changes look good to me.
>
> However, in the CREATE VARIABLE command, I would replace the "TRANSACTION"
> word by "TRANSACTIONAL".
>
> I have also tried to replace this word by a ON ROLLBACK clause at the end
> of the statement, like for ON COMMIT, but I have not found a satisfying
> wording to propose.
>
I propose compromise solution - I introduced new not reserved keyword
"TRANSACTIONAL". User can use TRANSACTION or TRANSACTIONAL. It is similar
relation like "TEMP" or "TEMPORAL"
>
> D) Behaviour
>
> I am ok with variables not being transactional by default. That's the most
> simple, the most efficient, it emulates the package variables of other
> RDBMS and it will probably fit the most common use cases.
>
> Note that I am not strongly opposed to having by default transactional
> variables. But I don't know whether this change would be a great work. We
> would have at least to find another keyword in the CREATE VARIABLE
> statement. Something like "NON-TRANSACTIONAL VARIABLE" ?
>
> It is possible to create a NOT NULL variable without DEFAULT. When trying
> to read the variable before a LET statement, one gets an error massage
> saying that the NULL value is not allowed (and the documentation is clear
> about this case). Just for the records, I wondered whether it wouldn't be
> better to forbid a NOT NULL variable creation that wouldn't have a DEFAULT
> value. But finally, I think this behaviour provides a good way to force the
> variable initialisation before its use. So let's keep it as is.
>
> E) ACL and Rights
>
> I played a little bit with the GRANT and REVOKE statements.
>
> I have got an error (Issue 1). The following statement chain:
> create variable public.sv1 int;
> grant read on variable sv1 to other_user;
> drop owned by other_user;
> reports : ERROR: unexpected object class 4287
>
should be fixed
> I then tried to use DEFAULT PRIVILEGES. Despite this is not documented, I
> successfuly performed:
> alter default privileges in schema public grant read on variables to
> simple_user;
> alter default privileges in schema public grant write on variables to
> simple_user;
>
should be fixed
> When variables are then created, the grants are properly given.
> And the psql \ddp command perfectly returns:
> Default access privileges
> Owner | Schema | Type | Access privileges
> ----------+--------+------+-------------------------
> postgres | public | | simple_user=SW/postgres
> (1 row)
>
> So the ALTER DEFAULT PRIVILEGES documentation chapter has to reflect this
> new syntax (Issue 2).
>
> BTW, in the ACL, the READ privilege is represented by a S letter. A
> comment in the source reports that the R letter was used in the past for
> rule privilege. Looking at the postgres sources, I see that this privilege
> on rules has been suppressed in 8.2, so 13 years ago. As this R letter
> would be a so much better choice, I wonder whether it couldn't be reused
> now for this new purpose. Is it important to keep this letter frozen ?
>
I use ACL_READ constant in my patch. The value of ACL_READ is defined
elsewhere. So the changing from S to R should be done by separate patch and
by separate discussion.
> F) Extension
>
> I then created an extension, whose installation script creates a schema
> variable and functions that use it. The schema variable is correctly linked
> to the extension, so that dropping the extension drops the variable.
>
> But there is an issue when dumping the database (Issue 3). The script
> generated by pg_dump includes the CREATE EXTENSION statement as expected
> but also a redundant CREATE VARIABLE statement for the variable that
> belongs to the extension. As a result, one of course gets an error at
> restore time.
>
should be fixed now
> G) Row Level Security
>
> I did a test activating RLS on a table and creating a POLICY that
> references a schema variable in its USING and WITH CHECK clauses.
> Everything worked fine.
>
> H) psql
>
> A \dV meta-command displays all the created variables.
> I would change a little bit the provided view. More precisely I would:
> - rename "Constraint" into "Is nullable" and report it as a boolean
> - rename "Special behave" into "Is transactional" and report it as a
> boolean
> - change the order of columns so to have:
> Schema | Name | Type | Is nullable | Default | Owner | Is transactional |
> Transaction end action
> "Is nullable" being aside "Default"
>
I implemented your proposal
> I) Performance
>
> I just quickly looked at the performance, and didn't notice any issue.
>
> About variables read performance, I have noticed that:
> select sum(1) from generate_series(1,10000000);
> and
> select sum(sv1) from generate_series(1,10000000);
> have similar response times.
>
> About planning, a condition with a variable used as a constant is
> indexable, as if it were a literal.
>
> J) Documentation
>
> There are some wordings to improve in the documentation. But I am not the
> best person to give advice about english language ;-).
>
> However, aside the already mentionned lack of changes in the ALTER DEFAULT
> PRIVILEGES chapter, I also noticed :
> - line 50 of the patch, the sentence "(hidden attribute; must be
> explicitly selected)" looks false as the oid column of pg_variable is
> displayed, as for other tables of the catalog;
> - at several places, the word "behave" should be replaced by "behaviour"
> - line 433, a get_schema_variable() function is mentionned; is it a
> function that can really be called by users ?
>
should be fixed
> May be it would be interesting to also add a chapter in the Section V of
> the documentation, in order to more globally present the schema variables
> concept, aside the new or the modified statements.
>
We can finalize documentation little bit later, when will be clear what
related functionality is implemented.
updated patch attached
> K) Coding
>
> I am not able to appreciate the way the feature has been coded. So I let
> this for other reviewers ;-)
>
>
> To conclude, again, thanks a lot for this feature !
> And if I may add this. I dream of an additional feature: adding a SHARED
> clause to the CREATE VARIABLE statement in order to be able to create
> memory spaces that could be shared by all connections on the database and
> accessible in SQL and PL, under the protection of ACL. But that's another
> story ;-)
>
> Best regards. Philippe.
>
Thank you very much for review
>
> The new status of this patch is: Waiting on Author
>
Attachments:
[application/gzip] schema-variables-20191225.patch.gz (66.0K, ../../CAFj8pRAtEUYDnNcRp4TrhbjXKm-An_xgnQ8vqV=YMQbXvrm91Q@mail.gmail.com/3-schema-variables-20191225.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
@ 2019-12-26 18:13 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-12-26 18:13 UTC (permalink / raw)
To: Philippe BEAUDOIN <phb07@apra.asso.fr>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
fresh rebase
Regards
Pavel
Attachments:
[application/gzip] schema-variables-20191226.patch.gz (66.0K, ../../CAFj8pRAcb+PLKJJOGn7POtcTuH8DaKqZNid-7UvB9_BtgshHEQ@mail.gmail.com/3-schema-variables-20191226.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-12-30 16:26 ` Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Philippe BEAUDOIN @ 2019-12-30 16:26 UTC (permalink / raw)
To: pgsql-hackers@lists.postgresql.org; +Cc: Pavel Stehule <pavel.stehule@gmail.com>
The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: not tested
Documentation: tested, failed
Hi Pavel,
I have tested the latest version of your patch.
Both issues I reported are now fixed. And you largely applied my proposals. That's great !
I have also spent some time to review more closely the documentation. I will send you a direct mail with an attached file for some minor comments on this topic.
Except these documentation remarks to come, I haven't any other issue or suggestion to report.
Note that I have not closely looked at the C code itself. But may be some other reviewers have already done that job.
If yes, my feeling is that the patch could soon be set as "Ready for commiter".
Best regards. Philippe.
The new status of this patch is: Waiting on Author
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
@ 2019-12-30 20:05 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2019-12-30 20:05 UTC (permalink / raw)
To: Philippe BEAUDOIN <phb07@apra.asso.fr>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
po 30. 12. 2019 v 17:27 odesílatel Philippe BEAUDOIN <phb07@apra.asso.fr>
napsal:
> The following review has been posted through the commitfest application:
> make installcheck-world: tested, passed
> Implements feature: tested, passed
> Spec compliant: not tested
> Documentation: tested, failed
>
> Hi Pavel,
>
> I have tested the latest version of your patch.
> Both issues I reported are now fixed. And you largely applied my
> proposals. That's great !
>
> I have also spent some time to review more closely the documentation. I
> will send you a direct mail with an attached file for some minor comments
> on this topic.
>
> Except these documentation remarks to come, I haven't any other issue or
> suggestion to report.
> Note that I have not closely looked at the C code itself. But may be some
> other reviewers have already done that job.
> If yes, my feeling is that the patch could soon be set as "Ready for
> commiter".
>
> Best regards. Philippe.
>
> The new status of this patch is: Waiting on Author
>
Thank you very much for your comments, and notes. Updated patch attached.
Regards
Pavel
Attachments:
[application/gzip] schema-variables-20191230.patch.gz (66.1K, ../../CAFj8pRA+SYbegM6f5OTamwhMdsVXB22mbmkscaErrhYge8ji3A@mail.gmail.com/3-schema-variables-20191230.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-01-17 21:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2020-01-17 21:10 UTC (permalink / raw)
To: Philippe BEAUDOIN <phb07@apra.asso.fr>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
po 30. 12. 2019 v 21:05 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> po 30. 12. 2019 v 17:27 odesílatel Philippe BEAUDOIN <phb07@apra.asso.fr>
> napsal:
>
>> The following review has been posted through the commitfest application:
>> make installcheck-world: tested, passed
>> Implements feature: tested, passed
>> Spec compliant: not tested
>> Documentation: tested, failed
>>
>> Hi Pavel,
>>
>> I have tested the latest version of your patch.
>> Both issues I reported are now fixed. And you largely applied my
>> proposals. That's great !
>>
>> I have also spent some time to review more closely the documentation. I
>> will send you a direct mail with an attached file for some minor comments
>> on this topic.
>>
>> Except these documentation remarks to come, I haven't any other issue or
>> suggestion to report.
>> Note that I have not closely looked at the C code itself. But may be some
>> other reviewers have already done that job.
>> If yes, my feeling is that the patch could soon be set as "Ready for
>> commiter".
>>
>> Best regards. Philippe.
>>
>> The new status of this patch is: Waiting on Author
>>
>
> Thank you very much for your comments, and notes. Updated patch attached.
>
rebase
> Regards
>
> Pavel
>
>
Attachments:
[application/gzip] schema-variables-20200117.patch.gz (66.3K, ../../CAFj8pRCAGXK9Wd8FGboFkFyUTWwh0qJUKW0muVAMkAUq6feCdA@mail.gmail.com/3-schema-variables-20200117.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-01-21 23:41 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-24 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 2 replies; 433+ messages in thread
From: Tomas Vondra @ 2020-01-21 23:41 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Philippe BEAUDOIN <phb07@apra.asso.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi,
I did a quick review of this patch today, so let me share a couple of
comments.
Firstly, the patch is pretty large - it has ~270kB. Not the largest
patch ever, but large. I think breaking the patch into smaller pieces
would significantly improve the chnce of it getting committed.
Is it possible to break the patch into smaller pieces that could be
applied incrementally? For example, we could move the "transactional"
behavior into a separate patch, but it's not clear to me how much code
would that actually move to that second patch. Any other parts that
could be moved to a separate patch?
I see one of the main contention points was a rather long discussion
about transactional vs. non-transactional behavior. I agree with Pavel's
position that the non-transactional behavior should be the default,
simply because it's better aligned with what the other databases are
doing (and supporting migrations seems like one of the main use cases
for this feature).
I do understand it may not be suitable for some other use cases,
mentioned by Fabien, but IMHO it's fine to require explicit
specification of transactional behavior. Well, we can't have both as
default, and I don't think there's an obvious reason why it should be
the other way around.
Now, a bunch of comments about the code (some of that nitpicking):
1) This hunk in doc/src/sgml/catalogs.sgml still talks about "table
creation" instead of schema creation:
<row>
<entry><structfield>vartypmod</structfield></entry>
<entry><type>int4</type></entry>
<entry></entry>
<entry>
<structfield>vartypmod</structfield> records type-specific data
supplied at table creation time (for example, the maximum
length of a <type>varchar</type> column). It is passed to
type-specific input functions and length coercion functions.
The value will generally be -1 for types that do not need <structfield>vartypmod</structfield>.
</entry>
</row>
2) This hunk in doc/src/sgml/ref/alter_default_privileges.sgml uses
"role_name" instead of "variable_name"
GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
ON VARIABLES
TO { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
3) I find the syntax in create_variable.sgml a bit too over-complicated:
<synopsis>
CREATE [ { TEMPORARY | TEMP } ] [ { TRANSACTIONAL | TRANSACTION } ] VARIABLE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> ] [ COLLATE <replaceable class="parameter">collation</replaceable> ]
[ NOT NULL ] [ DEFAULT <replaceable class="parameter">default_expr</replaceable> ] [ { ON COMMIT DROP | ON { TRANSACTIONAL | TRANSACTION } END RESET } ]
</synopsis>
Do we really need both TRANSACTION and TRANSACTIONAL? Why not just one
that we already have in the grammar (i.e. TRANSACTION)?
4) I think we should rename schemavariable.h to schema_variable.h.
5) objectaddress.c has extra line after 'break;' in one switch.
6) The comment is wrong:
/*
* Find the ObjectAddress for a type or domain
*/
static ObjectAddress
get_object_address_variable(List *object, bool missing_ok)
7) I think the code/comments are really inconsistent in talking about
"variables" and "schema variables". For example in objectaddress.c we do
these two things:
case OCLASS_VARIABLE:
appendStringInfoString(&buffer, "schema variable");
break;
vs.
case DEFACLOBJ_VARIABLE:
appendStringInfoString(&buffer,
" on variables");
break;
That's going to be confusing for people.
8) I'm rather confused by CMD_PLAN_UTILITY, which seems to be defined
merely to support LET. I'm not sure why that's necessary (Why wouldn't
CMD_UTILITY be sufficient?).
Having to add conditions checking for CMD_PLAN_UTILITY to various places
in planner.c is rather annoying, and I wonder how likely it's this will
unnecessarily break external code in extensions etc.
9) This comment in planner.c seems obsolete (not updated to reflect
addition of the CMD_PLAN_UTILITY check):
/*
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
* inheritance_planner, add the ModifyTable node.
*/
if (parse->commandType != CMD_SELECT && parse->commandType != CMD_PLAN_UTILITY && !inheritance_update)
10) I kinda wonder what happens when a function is used in a WHERE
condition, but it depends on a variable and alsu mutates it on each
call ...
11) I think Query->hasSchemaVariable (in analyze.c) should be renamed to
hasSchemaVariables (which reflects the other fields referring to things
like window functions etc.)
12) I find it rather suspicious that we make decisions in utility.c
solely based on commandType (whether it's CMD_UTILITY or not). IMO
it's pretty strange/ugly that T_LetStmt can be both CMD_UTILITY and
CMD_PLAN_UTILITY:
case T_LetStmt:
{
if (pstmt->commandType == CMD_UTILITY)
doLetStmtReset(pstmt);
else
{
Assert(pstmt->commandType == CMD_PLAN_UTILITY);
doLetStmtEval(pstmt, params, queryEnv, queryString);
}
if (completionTag)
strcpy(completionTag, "LET");
}
break;
13) Not sure why we moved DO_TABLE in addBoundaryDependencies
(pg_dump.c), seems unnecessary:
case DO_CONVERSION:
- case DO_TABLE:
+ case DO_VARIABLE:
case DO_ATTRDEF:
+ case DO_TABLE:
case DO_PROCLANG:
14) namespace.c defines VariableIsVisible twice:
extern bool VariableIsVisible(Oid relid);
...
extern bool VariableIsVisible(Oid varid);
15) I'd say lookup_variable and identify_variable should use camelcase
just like the other functions in the same file.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-01-24 05:08 ` Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2020-01-24 05:08 UTC (permalink / raw)
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Philippe BEAUDOIN <phb07@apra.asso.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
st 22. 1. 2020 v 0:41 odesílatel Tomas Vondra <tomas.vondra@2ndquadrant.com>
napsal:
> Hi,
>
> I did a quick review of this patch today, so let me share a couple of
> comments.
>
> Firstly, the patch is pretty large - it has ~270kB. Not the largest
> patch ever, but large. I think breaking the patch into smaller pieces
> would significantly improve the chnce of it getting committed.
>
> Is it possible to break the patch into smaller pieces that could be
> applied incrementally? For example, we could move the "transactional"
> behavior into a separate patch, but it's not clear to me how much code
> would that actually move to that second patch. Any other parts that
> could be moved to a separate patch?
>
I am sending two patches - 0001 - schema variables, 0002 - transactional
variables
>
> I see one of the main contention points was a rather long discussion
> about transactional vs. non-transactional behavior. I agree with Pavel's
> position that the non-transactional behavior should be the default,
> simply because it's better aligned with what the other databases are
> doing (and supporting migrations seems like one of the main use cases
> for this feature).
>
> I do understand it may not be suitable for some other use cases,
> mentioned by Fabien, but IMHO it's fine to require explicit
> specification of transactional behavior. Well, we can't have both as
> default, and I don't think there's an obvious reason why it should be
> the other way around.
>
> Now, a bunch of comments about the code (some of that nitpicking):
>
>
> 1) This hunk in doc/src/sgml/catalogs.sgml still talks about "table
> creation" instead of schema creation:
>
> <row>
> <entry><structfield>vartypmod</structfield></entry>
> <entry><type>int4</type></entry>
> <entry></entry>
> <entry>
> <structfield>vartypmod</structfield> records type-specific data
> supplied at table creation time (for example, the maximum
> length of a <type>varchar</type> column). It is passed to
> type-specific input functions and length coercion functions.
> The value will generally be -1 for types that do not need
> <structfield>vartypmod</structfield>.
> </entry>
> </row>
>
fixed
>
> 2) This hunk in doc/src/sgml/ref/alter_default_privileges.sgml uses
> "role_name" instead of "variable_name"
>
> GRANT { READ | WRITE | ALL [ PRIVILEGES ] }
> ON VARIABLES
> TO { [ GROUP ] <replaceable class="parameter">role_name</replaceable>
> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
>
I think so this is correct
>
> 3) I find the syntax in create_variable.sgml a bit too over-complicated:
>
> <synopsis>
> CREATE [ { TEMPORARY | TEMP } ] [ { TRANSACTIONAL | TRANSACTION } ]
> VARIABLE [ IF NOT EXISTS ] <replaceable
> class="parameter">name</replaceable> [ AS ] <replaceable
> class="parameter">data_type</replaceable> ] [ COLLATE <replaceable
> class="parameter">collation</replaceable> ]
> [ NOT NULL ] [ DEFAULT <replaceable
> class="parameter">default_expr</replaceable> ] [ { ON COMMIT DROP | ON {
> TRANSACTIONAL | TRANSACTION } END RESET } ]
> </synopsis>
>
> Do we really need both TRANSACTION and TRANSACTIONAL? Why not just one
> that we already have in the grammar (i.e. TRANSACTION)?
>
It was a Philippe's wish - the implementation is simple, and it is similar
like TEMP, TEMPORARY. I have not any opinion about it.
>
> 4) I think we should rename schemavariable.h to schema_variable.h.
>
done
>
> 5) objectaddress.c has extra line after 'break;' in one switch.
>
fixed
>
> 6) The comment is wrong:
>
> /*
> * Find the ObjectAddress for a type or domain
> */
> static ObjectAddress
> get_object_address_variable(List *object, bool missing_ok)
>
fixed
>
> 7) I think the code/comments are really inconsistent in talking about
> "variables" and "schema variables". For example in objectaddress.c we do
> these two things:
>
> case OCLASS_VARIABLE:
> appendStringInfoString(&buffer, "schema variable");
> break;
>
> vs.
>
> case DEFACLOBJ_VARIABLE:
> appendStringInfoString(&buffer,
> " on variables");
> break;
>
> That's going to be confusing for people.
>
>
fixed
>
> 8) I'm rather confused by CMD_PLAN_UTILITY, which seems to be defined
> merely to support LET. I'm not sure why that's necessary (Why wouldn't
> CMD_UTILITY be sufficient?).
>
Currently out utility statements cannot to hold a execution plan, and
cannot be prepared.
so this enhancing is motivated mainly by performance reasons. I would to
allow any SELECT query there, not just expressions only (see a limits of
CALL statement)
> Having to add conditions checking for CMD_PLAN_UTILITY to various places
> in planner.c is rather annoying, and I wonder how likely it's this will
> unnecessarily break external code in extensions etc.
>
>
> 9) This comment in planner.c seems obsolete (not updated to reflect
> addition of the CMD_PLAN_UTILITY check):
>
> /*
> * If this is an INSERT/UPDATE/DELETE, and we're not being called from
> * inheritance_planner, add the ModifyTable node.
> */
> if (parse->commandType != CMD_SELECT && parse->commandType !=
> CMD_PLAN_UTILITY && !inheritance_update)
>
"If this is an INSERT/UPDATE/DELETE," is related to parse->commandType !=
CMD_SELECT && parse->commandType != CMD_PLAN_UTILITY
>
>
> 10) I kinda wonder what happens when a function is used in a WHERE
> condition, but it depends on a variable and alsu mutates it on each
> call ...
>
>
> 11) I think Query->hasSchemaVariable (in analyze.c) should be renamed to
> hasSchemaVariables (which reflects the other fields referring to things
> like window functions etc.)
>
>
done
> 12) I find it rather suspicious that we make decisions in utility.c
> solely based on commandType (whether it's CMD_UTILITY or not). IMO
> it's pretty strange/ugly that T_LetStmt can be both CMD_UTILITY and
> CMD_PLAN_UTILITY:
>
> case T_LetStmt:
> {
> if (pstmt->commandType == CMD_UTILITY)
> doLetStmtReset(pstmt);
> else
> {
> Assert(pstmt->commandType == CMD_PLAN_UTILITY);
> doLetStmtEval(pstmt, params, queryEnv, queryString);
> }
>
> if (completionTag)
> strcpy(completionTag, "LET");
> }
> break;
>
>
> 13) Not sure why we moved DO_TABLE in addBoundaryDependencies
> (pg_dump.c), seems unnecessary:
>
> case DO_CONVERSION:
> - case DO_TABLE:
> + case DO_VARIABLE:
> case DO_ATTRDEF:
> + case DO_TABLE:
> case DO_PROCLANG:
>
fixed
>
> 14) namespace.c defines VariableIsVisible twice:
>
> extern bool VariableIsVisible(Oid relid);
> ...
> extern bool VariableIsVisible(Oid varid);
>
>
fixed
> 15) I'd say lookup_variable and identify_variable should use camelcase
> just like the other functions in the same file.
>
fixed
Regards
Pavel
>
> regards
>
> --
> Tomas Vondra http://www.2ndQuadrant.com
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>
Attachments:
[application/gzip] 0002-transaction-variables.patch.gz (7.1K, ../../CAFj8pRA1NuqD3-JP5ESBA8dUze=6h2YEQoqnBDWgawFo+Z3ONQ@mail.gmail.com/3-0002-transaction-variables.patch.gz)
download
[application/gzip] 0001-schema-variables.patch.gz (64.1K, ../../CAFj8pRA1NuqD3-JP5ESBA8dUze=6h2YEQoqnBDWgawFo+Z3ONQ@mail.gmail.com/4-0001-schema-variables.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-01-26 17:26 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2020-01-26 17:26 UTC (permalink / raw)
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Philippe BEAUDOIN <phb07@apra.asso.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
>
>
> 12) I find it rather suspicious that we make decisions in utility.c
> solely based on commandType (whether it's CMD_UTILITY or not). IMO
> it's pretty strange/ugly that T_LetStmt can be both CMD_UTILITY and
> CMD_PLAN_UTILITY:
>
> case T_LetStmt:
> {
> if (pstmt->commandType == CMD_UTILITY)
> doLetStmtReset(pstmt);
> else
> {
> Assert(pstmt->commandType == CMD_PLAN_UTILITY);
> doLetStmtEval(pstmt, params, queryEnv, queryString);
> }
>
> if (completionTag)
> strcpy(completionTag, "LET");
> }
> break;
>
>
>
It looks strange, but it has sense, because the LET stmt supports reset to
default value.
I can write
1. LET var = DEFAULT;
2. LET var = (query);
In first case I have not any query, that I can assign, and in this case the
LET statement is really only UTILITY.
I did comment there
Regards
Pavel
>
>
> regards
>
> --
> Tomas Vondra http://www.2ndQuadrant.com
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>
Attachments:
[application/gzip] 0001-schema-variables-20200126.patch.gz (64.2K, ../../CAFj8pRDxHCt9_fDQ8cu07jB4KzyU1qmzgH9uA1XsPdqvZN-w6A@mail.gmail.com/3-0001-schema-variables-20200126.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-02-07 16:09 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2020-02-07 16:09 UTC (permalink / raw)
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Philippe BEAUDOIN <phb07@apra.asso.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
rebase
Regards
Pavel
Attachments:
[application/gzip] 0002-transactional-variables-20200207.patch.gz (7.2K, ../../CAFj8pRCK5MaMJATx5BmoRv9TdLgZ4kWpor5yEP1e=V6ohoQv2g@mail.gmail.com/3-0002-transactional-variables-20200207.patch.gz)
download
[application/gzip] 0001-schema-variables-20200207.patch.gz (64.2K, ../../CAFj8pRCK5MaMJATx5BmoRv9TdLgZ4kWpor5yEP1e=V6ohoQv2g@mail.gmail.com/4-0001-schema-variables-20200207.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-02-10 18:47 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2020-02-10 18:47 UTC (permalink / raw)
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Philippe BEAUDOIN <phb07@apra.asso.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
pá 7. 2. 2020 v 17:09 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
> Hi
>
> rebase
>
> Regards
>
> Pavel
>
Hi
another rebase, fix \dV statement (for 0001 patch)
Regards
Pavel
Attachments:
[application/gzip] 0002-transactional-variables-20200210.patch.gz (7.5K, ../../CAFj8pRAUgPscQcT5CV8QwuNkx1HHSkxb-ba-CMsCe7MODvjMbw@mail.gmail.com/3-0002-transactional-variables-20200210.patch.gz)
download
[application/gzip] 0001-schema-variables-20200210.patch.gz (64.2K, ../../CAFj8pRAUgPscQcT5CV8QwuNkx1HHSkxb-ba-CMsCe7MODvjMbw@mail.gmail.com/4-0001-schema-variables-20200210.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-02-15 08:05 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2020-02-15 08:05 UTC (permalink / raw)
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Philippe BEAUDOIN <phb07@apra.asso.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
po 10. 2. 2020 v 19:47 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
>
>
> pá 7. 2. 2020 v 17:09 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
>
>> Hi
>>
>> rebase
>>
>> Regards
>>
>> Pavel
>>
>
> Hi
>
> another rebase, fix \dV statement (for 0001 patch)
>
rebase
Pavel
> Regards
>
> Pavel
>
Attachments:
[application/gzip] 0001-schema-variables-20200215.patch.gz (64.2K, ../../CAFj8pRBQSbOvK94QJCZGY7xqiuT7R7tuGKgci+CXGYONs5PGOw@mail.gmail.com/3-0001-schema-variables-20200215.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-02-26 14:53 ` remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: remi duval @ 2020-02-26 14:53 UTC (permalink / raw)
To: pgsql-hackers@lists.postgresql.org; +Cc: Pavel Stehule <pavel.stehule@gmail.com>
The following review has been posted through the commitfest application:
make installcheck-world: not tested
Implements feature: tested, passed
Spec compliant: tested, failed
Documentation: tested, failed
Hello Pavel
First thanks for working on this patch cause it might be really helpful for those of us trying to migrate PL code between RDBMs.
I tried your patch for migrating an Oracle package body to PL/pgSQL after also testing a solution using set_config and current_setting (which works but I'm not really satisfied by this workaround solution).
So I compiled latest postgres sources from github on Linux (redhat 7.7) using only your patch number 1 (I did not try the second part of the patch).
For my use-case it's working great, performances are excellent (compared to other solution for porting "package variables").
I did not test all the features involved by the patch (especially ALTER variable).
I have some feedback however :
1) Failure when using pg_dump 13 on a 12.1 database
When exporting a 12.1 database using pg_dump from the latest development sources I have an error regarding variables export
[pg12@TST-LINUX-PG-03 ~]$ /opt/postgres12/pg12/bin/pg_dump -h localhost -p 5432 -U postgres -f dump_pg12.sql database1
pg_dump: error: query failed: ERROR: relation "pg_variable" does not exist
LINE 1: ...og.pg_get_expr(v.vardefexpr,0) as vardefexpr FROM pg_variabl...
^
pg_dump: error: query was: SELECT v.tableoid, v.oid, v.varname, v.vareoxaction, v.varnamespace,
(SELECT rolname FROM pg_catalog.pg_roles WHERE oid = varowner) AS rolname
, (SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM (SELECT acl, row_n
FROM pg_catalog.unnest(coalesce(v.varacl,pg_catalog.acldefault('V',v.varowner)))
WITH ORDINALITY AS perm(acl,row_n)
WHERE NOT EXISTS ( SELECT 1 FROM pg_catalog.unnest(coalesce(pip.initprivs,pg_catalog.acldefault('V',v.varowner))) AS init(init_acl)
WHERE acl = init_acl)) as foo) as varacl, ...:
I think that it should have worked anyway cause the documentation states :
https://www.postgresql.org/docs/current/upgrading.html
"It is recommended that you use the pg_dump and pg_dumpall programs from the newer version of PostgreSQL, to take advantage of enhancements that might have been made in these programs." (that's what I did here)
I think there should be a way to avoid dumping the variable if they don't exist, should'nt it ?
2) Displaying the variables + completion
I created 2 variables using :
CREATE VARIABLE my_pkg.g_dat_deb varchar(11);
CREATE VARIABLE my_pkg.g_dat_fin varchar(11);
When I try to display them, I can only see them when prefixing by the schema :
bdd13=> \dV
"Did not find any schema variables."
bdd13=> \dV my_pkg.*
List of variables
Schema | Name | Type | Is nullable | Default | Owner | Transactional end action
------------+----------------+-----------------------+-------------+---------+-------+--------------------------
my_pkg| g_dat_deb | character varying(11) | t | | myowner |
my_pkg| g_dat_fin | character varying(11) | t | | myowner |
(3 rows)
bdd13=> \dV my_pkg
Did not find any schema variable named "my_pck".
NB : Using this template, functions are returned, maybe variables should also be listed ? (here by querying on "my_pkg%")
cts_get13=> \dV my_p [TAB]
=> completion using [TAB] key is not working
Is this normal that I cannot see all the variables when not specifying any schema ?
Also the completion works for functions, but not for variable.
That's just some bonus but it might be good to have it.
I think the way variables are listed using \dV should match with \df for querying functions
3) Any way to define CONSTANTs ?
We already talked a bit about this subject and also Gilles Darold introduces it in this mailing-list topic but I'd like to insist on it.
I think it would be nice to have a way to say that a variable should not be changed once defined.
Maybe it's hard to implement and can be implemented later, but I just want to know if this concern is open.
Otherwise the documentation looks good to me.
Regards
Rémi
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
@ 2020-02-26 20:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2020-02-26 20:40 UTC (permalink / raw)
To: remi duval <remi.duval@cheops.fr>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
st 26. 2. 2020 v 15:54 odesílatel remi duval <remi.duval@cheops.fr> napsal:
> The following review has been posted through the commitfest application:
> make installcheck-world: not tested
> Implements feature: tested, passed
> Spec compliant: tested, failed
> Documentation: tested, failed
>
> Hello Pavel
>
> First thanks for working on this patch cause it might be really helpful
> for those of us trying to migrate PL code between RDBMs.
>
> I tried your patch for migrating an Oracle package body to PL/pgSQL after
> also testing a solution using set_config and current_setting (which works
> but I'm not really satisfied by this workaround solution).
>
> So I compiled latest postgres sources from github on Linux (redhat 7.7)
> using only your patch number 1 (I did not try the second part of the patch).
>
> For my use-case it's working great, performances are excellent (compared
> to other solution for porting "package variables").
> I did not test all the features involved by the patch (especially ALTER
> variable).
>
ALTER VARIABLE is not implemented yet
> I have some feedback however :
>
> 1) Failure when using pg_dump 13 on a 12.1 database
>
> When exporting a 12.1 database using pg_dump from the latest development
> sources I have an error regarding variables export
>
> [pg12@TST-LINUX-PG-03 ~]$ /opt/postgres12/pg12/bin/pg_dump -h localhost
> -p 5432 -U postgres -f dump_pg12.sql database1
> pg_dump: error: query failed: ERROR: relation "pg_variable" does not exist
> LINE 1: ...og.pg_get_expr(v.vardefexpr,0) as vardefexpr FROM pg_variabl...
> ^
> pg_dump: error: query was: SELECT v.tableoid, v.oid, v.varname,
> v.vareoxaction, v.varnamespace,
> (SELECT rolname FROM pg_catalog.pg_roles WHERE oid = varowner) AS rolname
> , (SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM (SELECT acl, row_n
> FROM
> pg_catalog.unnest(coalesce(v.varacl,pg_catalog.acldefault('V',v.varowner)))
> WITH ORDINALITY AS perm(acl,row_n)
> WHERE NOT EXISTS ( SELECT 1 FROM
> pg_catalog.unnest(coalesce(pip.initprivs,pg_catalog.acldefault('V',v.varowner)))
> AS init(init_acl)
> WHERE acl = init_acl)) as foo) as varacl, ...:
>
> I think that it should have worked anyway cause the documentation states :
> https://www.postgresql.org/docs/current/upgrading.html
> "It is recommended that you use the pg_dump and pg_dumpall programs from
> the newer version of PostgreSQL, to take advantage of enhancements that
> might have been made in these programs." (that's what I did here)
>
> I think there should be a way to avoid dumping the variable if they don't
> exist, should'nt it ?
>
There was a protection against dump 11, but now it should be Postgres 12.
Fixed.
>
> 2) Displaying the variables + completion
> I created 2 variables using :
> CREATE VARIABLE my_pkg.g_dat_deb varchar(11);
> CREATE VARIABLE my_pkg.g_dat_fin varchar(11);
> When I try to display them, I can only see them when prefixing by the
> schema :
> bdd13=> \dV
> "Did not find any schema variables."
> bdd13=> \dV my_pkg.*
> List of variables
> Schema | Name | Type | Is nullable |
> Default | Owner | Transactional end action
>
> ------------+----------------+-----------------------+-------------+---------+-------+--------------------------
> my_pkg| g_dat_deb | character varying(11) | t | |
> myowner |
> my_pkg| g_dat_fin | character varying(11) | t | |
> myowner |
> (3 rows)
>
it is ok - it depends on SEARCH_PATH value
> bdd13=> \dV my_pkg
> Did not find any schema variable named "my_pck".
> NB : Using this template, functions are returned, maybe variables should
> also be listed ? (here by querying on "my_pkg%")
> cts_get13=> \dV my_p [TAB]
> => completion using [TAB] key is not working
>
> Is this normal that I cannot see all the variables when not specifying any
> schema ?
> Also the completion works for functions, but not for variable.
> That's just some bonus but it might be good to have it.
>
> I think the way variables are listed using \dV should match with \df for
> querying functions
>
fixed
> 3) Any way to define CONSTANTs ?
> We already talked a bit about this subject and also Gilles Darold
> introduces it in this mailing-list topic but I'd like to insist on it.
> I think it would be nice to have a way to say that a variable should not
> be changed once defined.
> Maybe it's hard to implement and can be implemented later, but I just want
> to know if this concern is open.
>
This topic is open. I tried to play with it. The problem is syntax. When I
try to reproduce syntax from PLpgSQL, then I need to introduce new reserved
keyword. So my initial idea was wrong.
We need to open discussion about implementable syntax. For this moment you
can use a workaround - any schema variable without WRITE right is constant.
Implementation is easy. Design of syntax is harder.
please see attached patch
Regards
Pavel
>
> Otherwise the documentation looks good to me.
>
> Regards
>
> Rémi
Attachments:
[application/gzip] schema-variables-20200226.patch.gz (64.2K, ../../CAFj8pRDj7ss7YNRdF1nD6=DaeAk7YJ=Nz6449-g0DrPEZmxEug@mail.gmail.com/3-schema-variables-20200226.patch.gz)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-02-27 14:37 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:59 ` RE: proposal: schema variables DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
2020-02-28 15:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 2 replies; 433+ messages in thread
From: Pavel Stehule @ 2020-02-27 14:37 UTC (permalink / raw)
To: remi duval <remi.duval@cheops.fr>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Hi
> 3) Any way to define CONSTANTs ?
> We already talked a bit about this subject and also Gilles Darold
> introduces it in this mailing-list topic but I'd like to insist on it.
> I think it would be nice to have a way to say that a variable should not
> be changed once defined.
> Maybe it's hard to implement and can be implemented later, but I just want
> to know if this concern is open.
>
I played little bit with it and I didn't find any nice solution, but maybe
I found the solution. I had ideas about some variants, but almost all time
I had a problem with parser's shifts because all potential keywords are not
reserved.
last variant, but maybe best is using keyword WITH
So the syntax can looks like
CREATE [ TEMP ] VARIABLE varname [ AS ] type [ NOT NULL ] [ DEFAULT
expression ] [ WITH [ OPTIONS ] '(' ... ')' ] ]
What do you think about this syntax? It doesn't need any new keyword, and
it easy to enhance it.
CREATE VARIABLE foo AS int DEFAULT 10 WITH OPTIONS ( CONSTANT);
?
Regards
Pavel
^ permalink raw reply [nested|flat] 433+ messages in thread
* RE: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-02-27 14:59 ` DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
2020-02-27 15:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 433+ messages in thread
From: DUVAL REMI @ 2020-02-27 14:59 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; phb07@apra.asso.fr <phb07@apra.asso.fr>
Hello Pavel.
That looks pretty good to me !
I’m adding Philippe Beaudoin who was also interested in this topic.
Recap : We were looking for a way to separate variable from constants in the “Schema Variables” proposition from Pavel.
Pavel was saying that there are some limitations regarding the keywords we can use, as the community don’t want to introduce too much new keywords in Postgres SQL (PL/pgSQL is a different list of keywords).
“CONSTANT” is not a keyword in SQL for Now (though it is one in PL/pgSQL).
Pavel’s syntax allow to use it as a keyword in the “WITH OPTIONS” clause that is already supported.
… I think it’s a good idea.
The list of keywords is defined in : postgresql\src\include\parser\kwlist.h
Pavel, I saw that in DB2, those variables are called “Global Variables”, is it something we can consider changing, or do you prefer to keep using the “Schema Variable” name ?
De : Pavel Stehule [mailto:pavel.stehule@gmail.com]
Envoyé : jeudi 27 février 2020 15:38
À : DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
Cc : PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Objet : Re: proposal: schema variables
Hi
3) Any way to define CONSTANTs ?
We already talked a bit about this subject and also Gilles Darold introduces it in this mailing-list topic but I'd like to insist on it.
I think it would be nice to have a way to say that a variable should not be changed once defined.
Maybe it's hard to implement and can be implemented later, but I just want to know if this concern is open.
I played little bit with it and I didn't find any nice solution, but maybe I found the solution. I had ideas about some variants, but almost all time I had a problem with parser's shifts because all potential keywords are not reserved.
last variant, but maybe best is using keyword WITH
So the syntax can looks like
CREATE [ TEMP ] VARIABLE varname [ AS ] type [ NOT NULL ] [ DEFAULT expression ] [ WITH [ OPTIONS ] '(' ... ')' ] ]
What do you think about this syntax? It doesn't need any new keyword, and it easy to enhance it.
CREATE VARIABLE foo AS int DEFAULT 10 WITH OPTIONS ( CONSTANT);
?
Regards
Pavel
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:59 ` RE: proposal: schema variables DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
@ 2020-02-27 15:09 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 433+ messages in thread
From: Pavel Stehule @ 2020-02-27 15:09 UTC (permalink / raw)
To: DUVAL REMI <REMI.DUVAL@cheops.fr>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; phb07@apra.asso.fr <phb07@apra.asso.fr>
čt 27. 2. 2020 v 15:59 odesílatel DUVAL REMI <REMI.DUVAL@cheops.fr> napsal:
> Hello Pavel.
>
>
>
> That looks pretty good to me !
>
>
>
> I’m adding Philippe Beaudoin who was also interested in this topic.
>
>
>
> Recap : We were looking for a way to separate variable from constants in
> the “Schema Variables” proposition from Pavel.
>
> Pavel was saying that there are some limitations regarding the keywords we
> can use, as the community don’t want to introduce too much new keywords in
> Postgres SQL (PL/pgSQL is a different list of keywords).
>
> “CONSTANT” is not a keyword in SQL for Now (though it is one in PL/pgSQL).
>
> Pavel’s syntax allow to use it as a keyword in the “WITH OPTIONS” clause
> that is already supported.
>
> … I think it’s a good idea.
>
>
>
> The list of keywords is defined in : postgresql\src\include\parser\kwlist.h
>
>
>
> Pavel, I saw that in DB2, those variables are called “Global Variables”,
> is it something we can consider changing, or do you prefer to keep using
> the “Schema Variable” name ?
>
It is most hard question. Global variables has sense, but when we will use
it in plpgsql, then this name can be little bit confusing. Personally I
prefer "schema variable" although my opinion is not too strong. This name
more signalize so this is more generic, more database related than some
special kind of plpgsql variables. Now, I think so maybe is better to use
schema variables, because there is different syntax then global temp
tables. Variables are global by design. So in this moment I prefer the name
"schema variables". It can be used as global variables in plpgsql, but it
is one case.
Pavel
>
>
>
> *De :* Pavel Stehule [mailto:pavel.stehule@gmail.com]
> *Envoyé :* jeudi 27 février 2020 15:38
> *À :* DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
> *Cc :* PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
> *Objet :* Re: proposal: schema variables
>
>
>
>
>
> Hi
>
>
>
>
> 3) Any way to define CONSTANTs ?
> We already talked a bit about this subject and also Gilles Darold
> introduces it in this mailing-list topic but I'd like to insist on it.
> I think it would be nice to have a way to say that a variable should not
> be changed once defined.
> Maybe it's hard to implement and can be implemented later, but I just want
> to know if this concern is open.
>
>
>
> I played little bit with it and I didn't find any nice solution, but maybe
> I found the solution. I had ideas about some variants, but almost all time
> I had a problem with parser's shifts because all potential keywords are not
> reserved.
>
>
>
> last variant, but maybe best is using keyword WITH
>
>
>
> So the syntax can looks like
>
>
>
> CREATE [ TEMP ] VARIABLE varname [ AS ] type [ NOT NULL ] [ DEFAULT
> expression ] [ WITH [ OPTIONS ] '(' ... ')' ] ]
>
>
>
> What do you think about this syntax? It doesn't need any new keyword, and
> it easy to enhance it.
>
>
>
> CREATE VARIABLE foo AS int DEFAULT 10 WITH OPTIONS ( CONSTANT);
>
>
>
> ?
>
>
>
> Regards
>
>
>
> Pavel
>
>
>
>
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-28 15:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-29 09:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-03-05 14:10 ` Asif Rehman <asifr.rehman@gmail.com>
2020-03-05 17:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Asif Rehman @ 2020-03-05 14:10 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: remi duval <remi.duval@cheops.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
On Sat, Feb 29, 2020 at 2:10 PM Pavel Stehule <pavel.stehule@gmail.com>
wrote:
>
>
> pá 28. 2. 2020 v 16:30 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
>
>>
>>
>> čt 27. 2. 2020 v 15:37 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
>> napsal:
>>
>>>
>>> Hi
>>>
>>>
>>>> 3) Any way to define CONSTANTs ?
>>>> We already talked a bit about this subject and also Gilles Darold
>>>> introduces it in this mailing-list topic but I'd like to insist on it.
>>>> I think it would be nice to have a way to say that a variable should
>>>> not be changed once defined.
>>>> Maybe it's hard to implement and can be implemented later, but I just
>>>> want to know if this concern is open.
>>>>
>>>
>>> I played little bit with it and I didn't find any nice solution, but
>>> maybe I found the solution. I had ideas about some variants, but almost all
>>> time I had a problem with parser's shifts because all potential keywords
>>> are not reserved.
>>>
>>> last variant, but maybe best is using keyword WITH
>>>
>>> So the syntax can looks like
>>>
>>> CREATE [ TEMP ] VARIABLE varname [ AS ] type [ NOT NULL ] [ DEFAULT
>>> expression ] [ WITH [ OPTIONS ] '(' ... ')' ] ]
>>>
>>> What do you think about this syntax? It doesn't need any new keyword,
>>> and it easy to enhance it.
>>>
>>> CREATE VARIABLE foo AS int DEFAULT 10 WITH OPTIONS ( CONSTANT);
>>>
>>
>> After some more thinking and because in other patch I support syntax
>> CREATE TRANSACTION VARIABLE ... I change my opinion and implemented support
>> for
>> syntax CREATE IMMUTABLE VARIABLE for define constants.
>>
>
> second try to fix pg_dump
>
> Regards
>
> Pavel
>
>
>>
>> See attached patch
>>
>> Regards
>>
>> Pavel
>>
>>
>>>
>>> ?
>>>
>>> Regards
>>>
>>> Pavel
>>>
>>>
>>>
Hi Pavel,
I have been reviewing the latest patch (schema-variables-20200229.patch.gz)
and here are few comments:
1- There is a compilation error, when compiled with --with-llvm enabled on
CentOS 7.
llvmjit_expr.c: In function ‘llvm_compile_expr’:
llvmjit_expr.c:1090:5: warning: initialization from incompatible pointer
type [enabled by default]
build_EvalXFunc(b, mod, "ExecEvalParamVariable",
^
llvmjit_expr.c:1090:5: warning: (near initialization for ‘(anonymous)[0]’)
[enabled by default]
llvmjit_expr.c:1090:5: warning: initialization from incompatible pointer
type [enabled by default]
llvmjit_expr.c:1090:5: warning: (near initialization for ‘(anonymous)[0]’)
[enabled by default]
llvmjit_expr.c:1090:5: warning: initialization from incompatible pointer
type [enabled by default]
llvmjit_expr.c:1090:5: warning: (near initialization for ‘(anonymous)[0]’)
[enabled by default]
llvmjit_expr.c:1090:5: warning: passing argument 5 of ‘build_EvalXFuncInt’
from incompatible pointer type [enabled by default]
llvmjit_expr.c:60:21: note: expected ‘struct ExprEvalStep *’ but argument
is of type ‘LLVMValueRef’
static LLVMValueRef build_EvalXFuncInt(LLVMBuilderRef b, LLVMModuleRef mod,
^
llvmjit_expr.c:1092:29: error: ‘i’ undeclared (first use in this function)
LLVMBuildBr(b, opblocks[i + 1]);
^
llvmjit_expr.c:1092:29: note: each undeclared identifier is reported only
once for each function it appears in
make[2]: *** [llvmjit_expr.o] Error 1
After looking into it, it turns out that:
- parameter order was incorrect in build_EvalXFunc()
- LLVMBuildBr() is using the undeclared variable 'i' whereas it should be
using 'opno'.
2- Similarly, If the default expression is referencing a function or object,
dependency should be marked, so if the function is not dropped silently.
otherwise, a cache lookup error will come.
postgres=# create or replace function foofunc() returns timestamp as $$
begin return now(); end; $$ language plpgsql;
CREATE FUNCTION
postgres=# create schema test;
CREATE SCHEMA
postgres=# create variable test.v1 as timestamp default foofunc();
CREATE VARIABLE
postgres=# drop function foofunc();
DROP FUNCTION
postgres=# select test.v1;
ERROR: cache lookup failed for function 16437
3- Variable DEFAULT expression is apparently being evaluated at the time of
first access. whereas I think that It should be at the time of variable
creation. consider the following example:
postgres=# create variable test.v2 as timestamp default now();
CREATE VARIABLE
postgres=# select now();
now
-------------------------------
2020-03-05 12:13:29.775373+00
(1 row)
postgres=# select test.v2;
v2
----------------------------
2020-03-05 12:13:37.192317 -- I was expecting this to be earlier than the
above timestamp.
(1 row)
postgres=# select test.v2;
v2
----------------------------
2020-03-05 12:13:37.192317
(1 row)
postgres=# let test.v2 = default;
LET
postgres=# select test.v2;
v2
----------------------------
2020-03-05 12:14:07.538615
(1 row)
To continue my testing of the patch I made few fixes for the above-mentioned
comments. The patch for those changes is attached if it could be of any use.
--
Asif Rehman
Highgo Software (Canada/China/Pakistan)
URL : www.highgo.ca
Attachments:
[application/octet-stream] sv-fixes.patch (1.1K, ../../CADM=Jej3onf9VK_3BfsuCpRLnXrYKp+cCY2PtahpCXRY4jG1iw@mail.gmail.com/3-sv-fixes.patch)
download | inline diff:
diff --git a/src/backend/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c
index f32d049bd59..bdee121cb57 100644
--- a/src/backend/catalog/pg_variable.c
+++ b/src/backend/catalog/pg_variable.c
@@ -350,6 +350,11 @@ VariableCreate(const char *varName,
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ /* dependency on default expr */
+ if (varDefexpr)
+ recordDependencyOnExpr(&myself, (Node *) varDefexpr,
+ NIL, DEPENDENCY_NORMAL);
+
/* dependency on any roles mentioned in ACL */
if (varacl != NULL)
{
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index dde6a5acbdb..08cf7e63bbf 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -1088,8 +1088,8 @@ llvm_compile_expr(ExprState *state)
case EEOP_PARAM_VARIABLE:
build_EvalXFunc(b, mod, "ExecEvalParamVariable",
- v_state, v_econtext, op);
- LLVMBuildBr(b, opblocks[i + 1]);
+ v_state, op, v_econtext);
+ LLVMBuildBr(b, opblocks[opno + 1]);
break;
case EEOP_PARAM_CALLBACK:
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-28 15:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-29 09:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-05 14:10 ` Re: proposal: schema variables Asif Rehman <asifr.rehman@gmail.com>
2020-03-05 17:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-06 15:44 ` RE: proposal: schema variables DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
2020-03-06 18:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-07 21:15 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-08 18:12 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-13 18:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 07:18 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 08:28 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-22 07:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-04-10 17:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-05-21 10:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 11:34 ` Re: proposal: schema variables Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2020-05-21 10:10 UTC (permalink / raw)
To: DUVAL REMI <REMI.DUVAL@cheops.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; +Cc: phb07@apra.asso.fr <phb07@apra.asso.fr>
Hi
just rebase without any other changes
Regards
Pavel
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-28 15:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-29 09:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-05 14:10 ` Re: proposal: schema variables Asif Rehman <asifr.rehman@gmail.com>
2020-03-05 17:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-06 15:44 ` RE: proposal: schema variables DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
2020-03-06 18:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-07 21:15 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-08 18:12 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-13 18:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 07:18 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 08:28 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-22 07:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-04-10 17:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 10:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-05-21 11:34 ` Amit Kapila <amit.kapila16@gmail.com>
2020-05-21 12:49 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Amit Kapila @ 2020-05-21 11:34 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: DUVAL REMI <REMI.DUVAL@cheops.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; phb07@apra.asso.fr <phb07@apra.asso.fr>
On Thu, May 21, 2020 at 3:41 PM Pavel Stehule <pavel.stehule@gmail.com> wrote:
>
> Hi
>
> just rebase without any other changes
>
You seem to forget attaching the rebased patch.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-28 15:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-29 09:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-05 14:10 ` Re: proposal: schema variables Asif Rehman <asifr.rehman@gmail.com>
2020-03-05 17:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-06 15:44 ` RE: proposal: schema variables DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
2020-03-06 18:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-07 21:15 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-08 18:12 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-13 18:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 07:18 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 08:28 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-22 07:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-04-10 17:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 10:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 11:34 ` Re: proposal: schema variables Amit Kapila <amit.kapila16@gmail.com>
2020-05-21 12:49 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-05 13:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-06 08:17 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-11 04:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-09-24 03:56 ` Michael Paquier <michael@paquier.xyz>
2020-09-24 03:58 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Michael Paquier @ 2020-09-24 03:56 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; DUVAL REMI <REMI.DUVAL@cheops.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
On Sat, Jul 11, 2020 at 06:44:24AM +0200, Pavel Stehule wrote:
> rebase
Per the CF bot, this needs an extra rebase as it does not apply
anymore. This has not attracted much the attention of committers as
well.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (832B, ../../20200924035637.GF28585@paquier.xyz/2-signature.asc)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-28 15:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-29 09:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-05 14:10 ` Re: proposal: schema variables Asif Rehman <asifr.rehman@gmail.com>
2020-03-05 17:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-06 15:44 ` RE: proposal: schema variables DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
2020-03-06 18:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-07 21:15 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-08 18:12 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-13 18:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 07:18 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 08:28 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-22 07:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-04-10 17:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 10:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 11:34 ` Re: proposal: schema variables Amit Kapila <amit.kapila16@gmail.com>
2020-05-21 12:49 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-05 13:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-06 08:17 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-11 04:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-09-24 03:56 ` Re: proposal: schema variables Michael Paquier <michael@paquier.xyz>
@ 2020-09-24 03:58 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-09-24 18:49 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Pavel Stehule @ 2020-09-24 03:58 UTC (permalink / raw)
To: Michael Paquier <michael@paquier.xyz>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; DUVAL REMI <REMI.DUVAL@cheops.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
čt 24. 9. 2020 v 5:56 odesílatel Michael Paquier <michael@paquier.xyz>
napsal:
> On Sat, Jul 11, 2020 at 06:44:24AM +0200, Pavel Stehule wrote:
> > rebase
>
> Per the CF bot, this needs an extra rebase as it does not apply
> anymore. This has not attracted much the attention of committers as
> well.
>
I'll fix it today
--
> Michael
>
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-28 15:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-29 09:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-05 14:10 ` Re: proposal: schema variables Asif Rehman <asifr.rehman@gmail.com>
2020-03-05 17:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-06 15:44 ` RE: proposal: schema variables DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
2020-03-06 18:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-07 21:15 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-08 18:12 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-13 18:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 07:18 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 08:28 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-22 07:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-04-10 17:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 10:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 11:34 ` Re: proposal: schema variables Amit Kapila <amit.kapila16@gmail.com>
2020-05-21 12:49 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-05 13:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-06 08:17 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-11 04:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-09-24 03:56 ` Re: proposal: schema variables Michael Paquier <michael@paquier.xyz>
2020-09-24 03:58 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-09-24 18:49 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-10-01 03:38 ` Michael Paquier <michael@paquier.xyz>
2020-10-01 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Michael Paquier @ 2020-10-01 03:38 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; DUVAL REMI <REMI.DUVAL@cheops.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
On Thu, Sep 24, 2020 at 08:49:50PM +0200, Pavel Stehule wrote:
> fixed patch attached
It looks like there are again conflicts within setrefs.c.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (832B, ../../20201001033824.GC8130@paquier.xyz/2-signature.asc)
download
^ permalink raw reply [nested|flat] 433+ messages in thread
* Re: proposal: schema variables
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Re: proposal: schema variables Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Re: [HACKERS] proposal: schema variables Gilles Darold <gilles.darold@dalibo.com>
2018-08-08 20:29 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Re: [HACKERS] proposal: schema variables Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-04 07:21 ` Re: [HACKERS] proposal: schema variables Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-14 21:31 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Re: [HACKERS] proposal: schema variables Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-20 09:08 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 05:57 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Re: [HACKERS] proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-26 18:13 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Re: proposal: schema variables Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Re: proposal: schema variables Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-26 17:26 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` Re: proposal: schema variables remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-28 15:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-02-29 09:09 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-05 14:10 ` Re: proposal: schema variables Asif Rehman <asifr.rehman@gmail.com>
2020-03-05 17:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-06 15:44 ` RE: proposal: schema variables DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
2020-03-06 18:54 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-07 21:15 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-08 18:12 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-13 18:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 07:18 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 08:28 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-03-22 07:40 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-04-10 17:30 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 10:10 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 11:34 ` Re: proposal: schema variables Amit Kapila <amit.kapila16@gmail.com>
2020-05-21 12:49 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-05 13:33 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-06 08:17 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-07-11 04:44 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-09-24 03:56 ` Re: proposal: schema variables Michael Paquier <michael@paquier.xyz>
2020-09-24 03:58 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-09-24 18:49 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-10-01 03:38 ` Re: proposal: schema variables Michael Paquier <michael@paquier.xyz>
2020-10-01 05:08 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-11-10 18:45 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-12-19 06:57 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2020-12-26 04:52 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-12-26 06:18 ` Erik Rijkers <er@xs4all.nl>
2020-12-26 06:23 ` Re: proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 433+ messages in thread
From: Erik Rijkers @ 2020-12-26 06:18 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Michael Paquier <michael@paquier.xyz>; Zhihong Yu <zyu@yugabyte.com>; Amit Kapila <amit.kapila16@gmail.com>; DUVAL REMI <REMI.DUVAL@cheops.fr>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
On 2020-12-26 05:52, Pavel Stehule wrote:
> so 19. 12. 2020 v 7:57 odesílatel Pavel Stehule
> <pavel.stehule@gmail.com>
> napsal:
> [schema-variables-20201222.patch.gz (~]
>
>> Hi
>>
>> only rebase
>>
>
> rebase and comments fixes
>
Hi Pavel,
This file is the exact same as the file you sent Tuesday. Is it a
mistake?
^ permalink raw reply [nested|flat] 433+ messages in thread
284 further messages in this thread omitted from this page (render size limit).
Use the per-message pages, or the t.mbox.gz byte stream, for the complete thread.
end of thread, other threads:[~2026-04-03 19:10 UTC | newest]
Thread overview: 433+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2017-10-26 07:21 proposal: schema variables Pavel Stehule <pavel.stehule@gmail.com>
2017-10-26 22:07 ` Nico Williams <nico@cryptonector.com>
2017-10-27 05:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-10-30 21:42 ` srielau <serge@rielau.com>
2017-10-31 20:33 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:08 ` Serge Rielau <serge@rielau.com>
2017-10-31 21:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-10-31 21:28 ` srielau <serge@rielau.com>
2017-10-31 22:36 ` Gilles Darold <gilles.darold@dalibo.com>
2017-10-31 23:02 ` Gilles Darold <gilles.darold@dalibo.com>
2017-11-01 04:15 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-11-01 05:07 ` Serge Rielau <serge@rielau.com>
2017-11-01 05:56 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-11-01 22:13 ` Gilles Darold <gilles.darold@dalibo.com>
2017-10-27 05:30 ` Tatsuo Ishii <ishii@sraoss.co.jp>
2017-10-27 05:47 ` Tsunakawa, Takayuki <tsunakawa.takay@jp.fujitsu.com>
2017-10-27 06:16 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-10-27 13:38 ` Gilles Darold <gilles.darold@dalibo.com>
2017-10-27 14:09 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-10-28 14:24 ` Chris Travers <chris.travers@adjust.com>
2017-10-28 14:56 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-10-29 08:51 ` Chris Travers <chris.travers@adjust.com>
2017-10-29 10:47 ` Hannu Krosing <hannu.krosing@2ndquadrant.com>
2017-11-01 18:03 ` Mark Dilger <hornschnorter@gmail.com>
2017-11-01 19:19 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 12:35 ` Robert Haas <robertmhaas@gmail.com>
2017-11-02 15:35 ` Nico Williams <nico@cryptonector.com>
2017-11-02 15:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 15:48 ` Tom Lane <tgl@sss.pgh.pa.us>
2017-11-02 18:52 ` Nico Williams <nico@cryptonector.com>
2017-11-03 12:58 ` Chris Travers <chris.travers@adjust.com>
2017-11-02 17:21 ` Robert Haas <robertmhaas@gmail.com>
2017-11-02 15:49 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-11-02 15:07 ` Craig Ringer <craig@2ndquadrant.com>
2017-11-02 15:42 ` Pavel Stehule <pavel.stehule@gmail.com>
2017-11-13 12:15 ` Pavel Golub <pavel@microolap.com>
2017-11-13 12:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-02-02 22:06 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-02-03 00:48 ` David G. Johnston <david.g.johnston@gmail.com>
2018-02-03 06:58 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-02-07 06:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-08 18:00 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 06:49 ` Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 06:54 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-12 15:38 ` Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-12 16:13 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-13 09:54 ` Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-03-13 18:44 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-20 17:38 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-21 05:24 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-03-23 05:37 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 10:21 ` Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 11:22 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-06-27 17:15 ` Gilles Darold <gilles.darold@dalibo.com>
2018-06-27 17:17 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-08 20:29 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-08 20:35 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 05:39 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-11 18:46 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-12 05:35 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-14 14:38 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-21 17:55 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-21 18:48 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-22 07:00 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 05:35 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 08:17 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 08:44 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-08-23 09:46 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-08-23 12:39 ` Pavel Luzanov <p.luzanov@postgrespro.ru>
2018-08-29 18:10 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-09-04 07:21 ` Dean Rasheed <dean.a.rasheed@gmail.com>
2018-09-04 13:00 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-06 08:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-07 12:34 ` Fabien COELHO <coelho@cri.ensmp.fr>
2018-09-07 14:28 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-14 21:31 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-15 16:06 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-17 19:46 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 08:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 11:23 ` Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-19 12:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-19 12:53 ` Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-19 14:36 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-21 19:46 ` Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-09-22 03:35 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-20 09:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-22 06:00 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 08:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-09-29 22:19 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-10-02 23:01 ` Thomas Munro <thomas.munro@enterprisedb.com>
2018-10-05 01:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-10-07 17:13 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-11-21 07:24 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-11-30 23:17 ` Dmitry Dolgov <9erthalion6@gmail.com>
2018-12-01 06:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 13:23 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-12-31 15:40 ` Erik Rijkers <er@xs4all.nl>
2018-12-31 17:33 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-01-22 19:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-01-30 16:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-01-31 11:49 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-03 20:27 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-07 06:52 ` David Steele <david@pgmasters.net>
2019-03-07 08:10 ` Fabien COELHO <coelho@cri.ensmp.fr>
2019-03-07 08:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-07 08:37 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-07 09:26 ` David Steele <david@pgmasters.net>
2019-03-24 05:57 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-24 09:25 ` Erik Rijkers <er@xs4all.nl>
2019-03-24 09:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-25 19:40 ` Erik Rijkers <er@xs4all.nl>
2019-03-26 05:41 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-03-26 05:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-04-02 18:02 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-05-09 04:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-05-24 17:12 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-06-30 03:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-16 12:50 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-08-10 07:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-10-04 04:12 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-10-10 09:41 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-03 16:27 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-18 18:47 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-12-14 21:43 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-12-22 12:03 ` Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-22 18:50 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-12-25 21:45 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-12-26 18:13 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-12-30 16:26 ` Philippe BEAUDOIN <phb07@apra.asso.fr>
2019-12-30 20:05 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 21:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-21 23:41 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-01-24 05:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-26 17:26 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-07 16:09 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-10 18:47 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-15 08:05 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-26 14:53 ` remi duval <remi.duval@cheops.fr>
2020-02-26 20:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:37 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-27 14:59 ` DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
2020-02-27 15:09 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-28 15:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-02-29 09:09 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-05 14:10 ` Asif Rehman <asifr.rehman@gmail.com>
2020-03-05 17:54 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-06 15:44 ` DUVAL REMI <REMI.DUVAL@CHEOPS.FR>
2020-03-06 18:54 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-07 21:15 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-08 18:12 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-13 18:44 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-18 05:58 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 07:18 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-20 08:28 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-22 07:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-04-10 17:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 10:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-05-21 11:34 ` Amit Kapila <amit.kapila16@gmail.com>
2020-05-21 12:49 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-07-05 13:33 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-07-06 08:17 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-07-11 04:44 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-09-24 03:56 ` Michael Paquier <michael@paquier.xyz>
2020-09-24 03:58 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-09-24 18:49 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-10-01 03:38 ` Michael Paquier <michael@paquier.xyz>
2020-10-01 05:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-11-10 18:45 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-12-19 06:57 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-12-26 04:52 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-12-26 06:18 ` Erik Rijkers <er@xs4all.nl>
2020-12-26 06:23 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-01-01 08:45 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-01-08 06:20 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-01-08 17:54 ` Erik Rijkers <er@xs4all.nl>
2021-01-10 16:54 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-01-14 06:35 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-01-14 09:24 ` Erik Rijkers <er@xs4all.nl>
2021-01-18 09:50 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-01-18 09:59 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-01-18 14:24 ` Erik Rijkers <er@xs4all.nl>
2021-01-18 18:17 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-01-23 09:50 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-02-02 08:43 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-02-16 17:46 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-03-01 07:50 ` Pavel Stehule <pavel.stehule@gmail.com>
2021-03-13 06:01 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-07-19 11:41 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-20 19:48 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-07-22 06:37 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-07-22 08:23 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-22 08:55 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-07-23 14:34 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-23 21:41 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-24 19:03 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-07-24 15:19 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-07-24 17:02 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-24 18:01 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-07-25 13:52 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-25 16:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-07-27 14:19 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-29 09:56 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-30 19:46 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-31 06:41 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-07-31 06:57 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-31 07:04 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-07-31 09:45 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-31 22:05 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-01 06:12 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-01 07:45 ` Erik Rijkers <er@xs4all.nl>
2024-08-01 07:48 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-01 11:22 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-08-01 11:24 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-01 22:02 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-03 05:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-07 10:00 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-08 05:17 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-15 05:55 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-27 06:15 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-08-27 06:52 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-27 14:52 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-08-29 18:17 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-08-29 17:33 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-09-02 14:00 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-09-03 11:41 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-09-12 05:15 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-09-18 06:21 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-09-22 08:43 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-10-09 04:24 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-10-23 04:11 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-10-24 08:29 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-10-25 20:38 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-10-28 07:01 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-10-29 07:16 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-02 05:46 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-11-02 07:36 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-04 09:24 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-11-10 22:41 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-13 14:24 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-11-13 15:06 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-16 15:41 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-06 18:24 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-10 15:24 ` Dmitry Dolgov <9erthalion6@gmail.com>
2024-11-10 16:19 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-10 17:41 ` Dmitry Dolgov <9erthalion6@gmail.com>
2024-11-10 18:04 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-10 17:51 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-10 18:09 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-13 16:34 ` Dmitry Dolgov <9erthalion6@gmail.com>
2024-11-13 18:18 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-14 07:41 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-15 04:45 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-16 06:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-16 14:27 ` Dmitry Dolgov <9erthalion6@gmail.com>
2024-11-16 14:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-16 14:56 ` Wolfgang Walther <walther@technowledgy.de>
2024-11-16 15:36 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-16 17:13 ` Wolfgang Walther <walther@technowledgy.de>
2024-11-16 22:07 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-17 04:53 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-16 22:49 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-17 04:41 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-19 19:14 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-19 21:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-20 07:25 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-26 06:21 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-27 18:14 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-05 06:51 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-07 02:13 ` jian he <jian.universality@gmail.com>
2024-12-08 18:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-09 06:16 ` jian he <jian.universality@gmail.com>
2024-12-09 23:20 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-10 03:32 ` jian he <jian.universality@gmail.com>
2024-12-11 20:51 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-20 13:29 ` Marcos Pegoraro <marcos@f10.com.br>
2024-11-20 13:52 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-20 14:14 ` Marcos Pegoraro <marcos@f10.com.br>
2024-11-20 14:57 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-11-20 20:13 ` Dmitry Dolgov <9erthalion6@gmail.com>
2024-11-21 04:07 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-09 16:54 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-14 15:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-18 03:00 ` jian he <jian.universality@gmail.com>
2024-12-19 07:25 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-20 07:57 ` jian he <jian.universality@gmail.com>
2024-12-20 22:00 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-27 15:19 ` jian he <jian.universality@gmail.com>
2024-12-28 10:34 ` jian he <jian.universality@gmail.com>
2024-12-28 17:29 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-28 15:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-28 16:46 ` jian he <jian.universality@gmail.com>
2024-12-28 21:49 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-29 02:48 ` jian he <jian.universality@gmail.com>
2024-12-29 08:42 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-02 06:46 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-03 07:18 ` jian he <jian.universality@gmail.com>
2025-01-03 22:59 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-04 05:36 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-05 04:52 ` jian he <jian.universality@gmail.com>
2025-01-05 16:10 ` jian he <jian.universality@gmail.com>
2025-01-06 07:58 ` jian he <jian.universality@gmail.com>
2025-01-06 12:21 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-06 19:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-07 09:07 ` jian he <jian.universality@gmail.com>
2025-01-07 21:21 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-08 09:31 ` jian he <jian.universality@gmail.com>
2025-01-08 16:33 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-08 19:00 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-09 05:57 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-15 07:28 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-17 07:18 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-17 13:41 ` Bruce Momjian <bruce@momjian.us>
2025-01-17 13:48 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-17 14:16 ` Bruce Momjian <bruce@momjian.us>
2025-01-17 14:47 ` Álvaro Herrera <alvherre@alvh.no-ip.org>
2025-01-20 08:26 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-20 20:15 ` Bruce Momjian <bruce@momjian.us>
2025-01-20 20:40 ` Laurenz Albe <laurenz.albe@cybertec.at>
2025-01-17 15:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-17 15:35 ` Bruce Momjian <bruce@momjian.us>
2025-01-17 15:55 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-17 16:01 ` Bruce Momjian <bruce@momjian.us>
2025-01-17 16:10 ` Julien Rouhaud <rjuju123@gmail.com>
2025-01-17 16:43 ` Laurenz Albe <laurenz.albe@cybertec.at>
2025-01-17 17:47 ` Dmitry Dolgov <9erthalion6@gmail.com>
2025-01-17 20:20 ` Wolfgang Walther <walther@technowledgy.de>
2025-01-17 21:30 ` Marcos Pegoraro <marcos@f10.com.br>
2025-01-17 21:54 ` Marcos Pegoraro <marcos@f10.com.br>
2025-01-18 07:24 ` Gilles Darold <gilles@darold.net>
2025-02-06 14:49 ` jian he <jian.universality@gmail.com>
2025-02-07 07:24 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-02-07 13:14 ` jian he <jian.universality@gmail.com>
2025-02-11 07:11 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-02-12 04:43 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-02-16 09:13 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-02-20 20:22 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-03-01 07:23 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-03-17 18:32 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-03-17 20:52 ` Marcos Pegoraro <marcos@f10.com.br>
2025-03-18 05:46 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-04-02 06:46 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-04-05 10:33 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-05-15 06:48 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-05-20 14:56 ` Bruce Momjian <bruce@momjian.us>
2025-05-20 16:33 ` Marcos Pegoraro <marcos@f10.com.br>
2025-05-20 16:39 ` Bruce Momjian <bruce@momjian.us>
2025-05-20 18:47 ` Daniel Gustafsson <daniel@yesql.se>
2025-05-20 20:28 ` Bruce Momjian <bruce@momjian.us>
2025-05-20 20:36 ` Laurenz Albe <laurenz.albe@cybertec.at>
2025-05-20 21:06 ` Bruce Momjian <bruce@momjian.us>
2025-05-21 05:15 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-05-21 20:41 ` Bruce Momjian <bruce@momjian.us>
2025-05-21 06:27 ` Laurenz Albe <laurenz.albe@cybertec.at>
2025-05-21 06:36 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-05-20 20:28 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-05-20 21:10 ` Bruce Momjian <bruce@momjian.us>
2025-05-21 00:21 ` Michael Paquier <michael@paquier.xyz>
2025-05-21 05:49 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-05-21 21:23 ` Bruce Momjian <bruce@momjian.us>
2025-02-09 20:56 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-06 10:01 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-01-06 08:39 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-29 06:24 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-12-20 12:53 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-05-21 07:12 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-05-21 21:22 ` Bruce Momjian <bruce@momjian.us>
2025-06-03 07:26 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-06-03 11:43 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-06-04 20:22 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-06-10 14:25 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-06-13 04:53 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-06-25 17:33 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-07-11 19:55 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-07-22 05:53 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-07-23 15:25 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-08-05 05:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-08-13 19:24 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-08-29 07:03 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-09-13 09:28 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-09-15 08:21 ` Jim Jones <jim.jones@uni-muenster.de>
2025-09-15 16:55 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-09-29 10:13 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-10-06 05:54 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-10-12 04:57 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-10-30 16:27 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-11-02 20:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-11-10 06:40 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-11-24 19:59 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-11-24 21:19 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-11-25 04:43 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-11-30 06:01 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-12-03 04:27 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-12-03 13:44 ` Jim Jones <jim.jones@uni-muenster.de>
2025-12-05 06:50 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-12-06 11:29 ` Jim Jones <jim.jones@uni-muenster.de>
2025-12-08 08:15 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-12-08 13:57 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-12-09 05:51 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-12-12 14:46 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-12-15 04:42 ` Pavel Stehule <pavel.stehule@gmail.com>
2025-12-27 07:52 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-01-09 08:45 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-02-12 19:25 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-03-04 10:02 ` Haritabh Gupta <haritabh1992@gmail.com>
2026-03-04 19:15 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-03-05 12:54 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-03-06 09:06 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-03-13 07:54 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-03-17 19:29 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-03-18 06:35 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-03-26 05:18 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-03-31 11:07 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-04-03 05:15 ` Pavel Stehule <pavel.stehule@gmail.com>
2026-04-03 19:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-10-25 05:21 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-10-25 07:41 ` Laurenz Albe <laurenz.albe@cybertec.at>
2024-07-30 20:56 ` Pavel Stehule <pavel.stehule@gmail.com>
2024-10-25 07:58 ` James Pang <jamespang886@gmail.com>
2024-10-25 07:59 ` James Pang <jamespang886@gmail.com>
2021-01-14 10:31 ` Josef Šimánek <josef.simanek@gmail.com>
2021-01-18 09:57 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-10-23 12:50 ` Erik Rijkers <er@xs4all.nl>
2018-10-23 18:05 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-04-17 14:14 ` Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-04-17 16:28 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-04-18 11:37 ` Arthur Zakirov <a.zakirov@postgrespro.ru>
2018-04-18 11:54 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-04-29 12:34 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-04-30 17:28 ` Fabrízio Mello <fabriziomello@gmail.com>
2018-04-20 15:32 ` Robert Haas <robertmhaas@gmail.com>
2018-04-20 17:45 ` Pavel Stehule <pavel.stehule@gmail.com>
2018-05-01 01:56 ` Peter Eisentraut <peter.eisentraut@2ndquadrant.com>
2018-05-01 03:11 ` Pavel Stehule <pavel.stehule@gmail.com>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox