poll: CHECK TRIGGER?
Hi,
Pavel's patch for CHECK FUNCTION is adding another command besides that
one, which is CHECK TRIGGER. The idea behind this is that you give it
the relation to which the trigger is attached in addition to the trigger
name, and it checks the function being called by that trigger.
IMHO having a separate command for this is not warranted. It seems to
me that we could simply have a variant of CREATE FUNCTION for this; I
proposed CREATE FUNCTION trigfunc() AS TRIGGER ON tabname. Besides,
it's really the function code being checked, not the trigger itself; the
trigger is only providing the tuple descriptor for NEW and OLD.
Pavel didn't like this idea; also, a quick poll on twitter elicited only
two answers: Dimitri Fontaine prefers CHECK TRIGGER, and Guillaume
Lelarge prefers CHECK FUNCTION.
So I still don't know which route to go with this. Thoughts?
One thing to consider is eventual support for triggers that use
anonymous function blocks, without a previous CREATE FUNCTION, which is
being discussed in another thread. Another point is that CHECK TRIGGER
requires a separate documentation page.
--
Álvaro Herrera <alvherre@alvh.no-ip.org>
Alvaro Herrera <alvherre@alvh.no-ip.org> writes:
Pavel's patch for CHECK FUNCTION is adding another command besides that
one, which is CHECK TRIGGER. The idea behind this is that you give it
the relation to which the trigger is attached in addition to the trigger
name, and it checks the function being called by that trigger.
IMHO having a separate command for this is not warranted. It seems to
me that we could simply have a variant of CREATE FUNCTION for this; I
proposed CREATE FUNCTION trigfunc() AS TRIGGER ON tabname.
You mean "CHECK FUNCTION ..." right?
In principle the CHECK TRIGGER command could apply more checks than
are possible with the proposed CHECK FUNCTION syntax: in particular,
AFAICS "AS TRIGGER ON tabname" doesn't provide enough info to know
whether the function should expect new and/or old rows to be provided,
nor what it ought to return (which is different for BEFORE/AFTER cases,
STATEMENT cases, etc). We could add all that info to the CHECK FUNCTION
syntax, but there's definitely some merit to defining the check as
occurring against an existing trigger definition instead.
Now, if there's no intention of ever making the code actually apply
checks of the sort I'm thinking of, maybe CHECK FUNCTION AS TRIGGER
is fine.
One thing to consider is eventual support for triggers that use
anonymous function blocks, without a previous CREATE FUNCTION, which is
being discussed in another thread.
Yeah, that angle seems to be another reason to support CHECK TRIGGER.
regards, tom lane
Excerpts from Tom Lane's message of sáb mar 03 23:00:26 -0300 2012:
Alvaro Herrera <alvherre@alvh.no-ip.org> writes:
Pavel's patch for CHECK FUNCTION is adding another command besides that
one, which is CHECK TRIGGER. The idea behind this is that you give it
the relation to which the trigger is attached in addition to the trigger
name, and it checks the function being called by that trigger.IMHO having a separate command for this is not warranted. It seems to
me that we could simply have a variant of CREATE FUNCTION for this; I
proposed CREATE FUNCTION trigfunc() AS TRIGGER ON tabname.You mean "CHECK FUNCTION ..." right?
Yeah, sorry.
In principle the CHECK TRIGGER command could apply more checks than
are possible with the proposed CHECK FUNCTION syntax: in particular,
AFAICS "AS TRIGGER ON tabname" doesn't provide enough info to know
whether the function should expect new and/or old rows to be provided,
nor what it ought to return (which is different for BEFORE/AFTER cases,
STATEMENT cases, etc). We could add all that info to the CHECK FUNCTION
syntax, but there's definitely some merit to defining the check as
occurring against an existing trigger definition instead.
Uh! Now that I read this I realize that what you're supposed to give to
CHECK TRIGGER is the trigger name, not the function name! In that
light, using CHECK FUNCTION for this doesn't make a lot of sense.
Okay, CHECK TRIGGER it is.
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On Sat, Mar 3, 2012 at 9:23 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:
Uh! Now that I read this I realize that what you're supposed to give to
CHECK TRIGGER is the trigger name, not the function name! In that
light, using CHECK FUNCTION for this doesn't make a lot of sense.Okay, CHECK TRIGGER it is.
I confess to some bafflement about why we need dedicated syntax for
this, or even any kind of core support at all. What would be wrong
with defining a function that takes regprocedure as an argument and
does whatever? Sure, it's nicer syntax, but we've repeatedly rejected
patches that only provided nicer syntax on the grounds that syntax is
not free, and therefore syntax alone is not a reason to change the
core grammar. What makes this case different?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/3/5 Robert Haas <robertmhaas@gmail.com>:
On Sat, Mar 3, 2012 at 9:23 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:Uh! Now that I read this I realize that what you're supposed to give to
CHECK TRIGGER is the trigger name, not the function name! In that
light, using CHECK FUNCTION for this doesn't make a lot of sense.Okay, CHECK TRIGGER it is.
I confess to some bafflement about why we need dedicated syntax for
this, or even any kind of core support at all. What would be wrong
with defining a function that takes regprocedure as an argument and
does whatever? Sure, it's nicer syntax, but we've repeatedly rejected
patches that only provided nicer syntax on the grounds that syntax is
not free, and therefore syntax alone is not a reason to change the
core grammar. What makes this case different?
Fo checking trigger handler (trigger function) you have to know
trigger definition (only joined relation now), but it can be enhanced
for other tests based on trigger data.
Regards
Pavel
Show quoted text
--
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
Robert Haas <robertmhaas@gmail.com> writes:
I confess to some bafflement about why we need dedicated syntax for
this, or even any kind of core support at all. What would be wrong
with defining a function that takes regprocedure as an argument and
does whatever? Sure, it's nicer syntax, but we've repeatedly rejected
patches that only provided nicer syntax on the grounds that syntax is
not free, and therefore syntax alone is not a reason to change the
core grammar. What makes this case different?
There's definitely something to be said for that, since it entirely
eliminates the problem of providing wildcards and control over which
function(s) to check --- the user could write a SELECT from pg_proc
that slices things however he wants.
The trigger case would presumably take arguments matching pg_trigger's
primary key, viz check_trigger(trig_rel regclass, trigger_name name).
But as for needing "core support", we do need to extend the API for PL
validators, so it's not like this could be done as an external project.
regards, tom lane
On Mon, Mar 5, 2012 at 11:41 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
I confess to some bafflement about why we need dedicated syntax for
this, or even any kind of core support at all. What would be wrong
with defining a function that takes regprocedure as an argument and
does whatever? Sure, it's nicer syntax, but we've repeatedly rejected
patches that only provided nicer syntax on the grounds that syntax is
not free, and therefore syntax alone is not a reason to change the
core grammar. What makes this case different?There's definitely something to be said for that, since it entirely
eliminates the problem of providing wildcards and control over which
function(s) to check --- the user could write a SELECT from pg_proc
that slices things however he wants.
The trigger case would presumably take arguments matching pg_trigger's
primary key, viz check_trigger(trig_rel regclass, trigger_name name).
Yes...
But as for needing "core support", we do need to extend the API for PL
validators, so it's not like this could be done as an external project.
Well, the plpgsql extension could install a function
pg_check_plpgsql_function() that only works on PL/pgsql functions, and
other procedural languages could do the same at their option. I think
we only need to extend the API if we want to provide a dispatch
function so that you can say "check this function, whatever language
it's written in" and have the right checker get called. But since
we've already talked about the possibility of having more than one
checker per language doing different kinds of checks, I'm not even
sure that "the checker" for a language is a concept that we want to
invent.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/3/5 Robert Haas <robertmhaas@gmail.com>:
On Mon, Mar 5, 2012 at 11:41 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
I confess to some bafflement about why we need dedicated syntax for
this, or even any kind of core support at all. What would be wrong
with defining a function that takes regprocedure as an argument and
does whatever? Sure, it's nicer syntax, but we've repeatedly rejected
patches that only provided nicer syntax on the grounds that syntax is
not free, and therefore syntax alone is not a reason to change the
core grammar. What makes this case different?There's definitely something to be said for that, since it entirely
eliminates the problem of providing wildcards and control over which
function(s) to check --- the user could write a SELECT from pg_proc
that slices things however he wants.
The trigger case would presumably take arguments matching pg_trigger's
primary key, viz check_trigger(trig_rel regclass, trigger_name name).Yes...
But as for needing "core support", we do need to extend the API for PL
validators, so it's not like this could be done as an external project.Well, the plpgsql extension could install a function
pg_check_plpgsql_function() that only works on PL/pgsql functions, and
other procedural languages could do the same at their option. I think
we only need to extend the API if we want to provide a dispatch
function so that you can say "check this function, whatever language
it's written in" and have the right checker get called. But since
we've already talked about the possibility of having more than one
checker per language doing different kinds of checks, I'm not even
sure that "the checker" for a language is a concept that we want to
invent.
There is not multiple PL checker function - or I don't know about it.
Pavel
Show quoted text
--
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
2012/3/5 Robert Haas <robertmhaas@gmail.com>:
On Sat, Mar 3, 2012 at 9:23 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:Uh! Now that I read this I realize that what you're supposed to give to
CHECK TRIGGER is the trigger name, not the function name! In that
light, using CHECK FUNCTION for this doesn't make a lot of sense.Okay, CHECK TRIGGER it is.
I confess to some bafflement about why we need dedicated syntax for
this, or even any kind of core support at all. What would be wrong
with defining a function that takes regprocedure as an argument and
does whatever? Sure, it's nicer syntax, but we've repeatedly rejected
patches that only provided nicer syntax on the grounds that syntax is
not free, and therefore syntax alone is not a reason to change the
core grammar. What makes this case different?
before argumentation for CHECK TRIGGER I show a proposed PL checker function:
FUNCTION checker_function(regprocedure, regclass, options text[],
fatal_errors boolen)
RETURNS TABLE (functionoid oid, lineno int, statement text, sqlstate
text, message text, detail text, hint text, position int, query)
this function is worker for CHECK FUNCTION and CHECK TRIGGER
statements. The possibility to call this function directly can enable
thousands combinations - all functions, all functions from schema, all
functions that has name starts with, ...
for user friendly there are interface: CHECK FUNCTION and CHECK TRIGGER
* provides more practical reports with caret positioning than SRF function
* support often used combinations of requests - all functions from one
language, all functions from schema, all functions by one user
CHECK FUNCTION is clear - and there are no disagreement
There are two possibilities for checking triggers
a) some like CHECK FUNCTION trgfunc() ON table_name
b) existing CHECK TRIGGER t1_f1 ON table_name;
these forms are almost equal, although CREATE TRIGGER can provide more
unique information for checking. And joining table_name to TRIGGER has
bigger sense then to FUNCTION (in one statement).
When I try to look on some multicheck form:
a) CHECK FUNCTION ALL ON table_name
b) CHECK TRIGGER ALL ON table_name
then more natural form is @b (for me). Personally, I can live with
one, both or second form, although I prefer CHECK TRIGGER.
notes?
Regards
Pavel
Show quoted text
--
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
Hello
When I try to look on some multicheck form:
a) CHECK FUNCTION ALL ON table_name
b) CHECK TRIGGER ALL ON table_namethen more natural form is @b (for me). Personally, I can live with
one, both or second form, although I prefer CHECK TRIGGER.
I though some time more.
if somebody would to check all custom function, then he can write
CHECK FUNCTION ALL
what about triggers?
CHECK TRIGGER ALL
but if we don't implement CHECK TRIGGER, then this statement will look like
CHECK FUNCTION ALL ON ALL ???
and this is unclean - probably it doesn't mean - check trigger
function with any table. So this is other argument for CREATE TRIGGER.
Nice a day
Pavel
Show quoted text
notes?
Regards
Pavel
--
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
Hello
Robert, please, can you comment to this issue? And other, please. I am
able to fix syntax to any form where we will have agreement.
Regards
Pavel
2012/3/6 Pavel Stehule <pavel.stehule@gmail.com>:
Show quoted text
Hello
When I try to look on some multicheck form:
a) CHECK FUNCTION ALL ON table_name
b) CHECK TRIGGER ALL ON table_namethen more natural form is @b (for me). Personally, I can live with
one, both or second form, although I prefer CHECK TRIGGER.I though some time more.
if somebody would to check all custom function, then he can write
CHECK FUNCTION ALL
what about triggers?
CHECK TRIGGER ALL
but if we don't implement CHECK TRIGGER, then this statement will look like
CHECK FUNCTION ALL ON ALL ???
and this is unclean - probably it doesn't mean - check trigger
function with any table. So this is other argument for CREATE TRIGGER.Nice a day
Pavel
notes?
Regards
Pavel
--
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
On Wed, Mar 7, 2012 at 12:17 AM, Pavel Stehule <pavel.stehule@gmail.com> wrote:
Robert, please, can you comment to this issue? And other, please. I am
able to fix syntax to any form where we will have agreement.
Well, so far I don't see that anyone has offered a compelling reason
why this needs core syntax support. If we just define a function
called plpgsql_checker() or plpgsql_lint() or whatever, someone can
check one function like this:
SELECT plpgsql_checker('myfunc(int)'::regprocedure);
If they want to check all the functions in a schema, they can do this:
SELECT plpgsql_checker(oid) FROM pg_proc WHERE prolang = (SELECT oid
FROM pg_language WHERE lanname = 'plpgsql') AND pronamespace = (SELECT
oid FROM pg_namespace WHERE nspname = 'myschema');
If they want to check all functions they own, they can do this:
SELECT plpgsql_checker(oid) FROM pg_proc WHERE prolang = (SELECT oid
FROM pg_language WHERE lanname = 'plpgsql') AND pronamespace = (SELECT
oid FROM pg_authid WHERE rolname = 'myrole');
Any other combination of things that they want to check can be
accommodated by varying the WHERE clause. If we think there are
common cases like the above that we want to make easy, we can do that
by creating wrapper functions called, e.g.
plpgsql_check_namespace(text) and plpgsql_check_role(text) that are
just SQL functions that execute the query mentioned above. If we find
that the convenience functions we've added don't cover all the cases
we care about, it's a lot easier and less invasive to just add a few
more than it is to invent new syntax. Moreover, it doesn't require a
major release cycle: using functional notation, a customer who wants
to check all security definer functions owned by roles that are
members of the dev group can do it just by adjusting the queries shown
above. If they want an extra convenience function for that, they can
easily define one. Even with dedicated syntax it's still possible to
define convenience functions by wrapping the CHECK FUNCTION statement
up in a user-defined function that dynamically generates and executes
SQL and then calling it repeatedly from a query, but that's more work.
As things stand today, the "checker" infrastructure is a very large
percentage of this patch. Ripping all that out and just exposing this
as a function, or a couple of functions, will make the patch much
smaller and simpler, and allow us to focus on what I think we really
should be worrying about here, which is the quality and value of the
tests being performed. I don't necessarily say that it could *never*
make sense to add any syntax support here, but I do think there's no
real clear need for it and that, inasmuch as this is a new feature, it
doesn't really make sense for us to commit ourselves to more than
necessary. tsearch lived without core support for several releases
until it became clear that it was a sufficiently important feature to
merit tighter integration. Furthermore, the functional syntax being
proposed here is exactly the same kind of syntax that we use for many
other things that are arguably far more important. If
pg_start_backup() isn't important enough to be implemented as an SQL
command rather than a function, then I don't think this is either.
Just to be clear, I am not proposing that we get rid of CHECK TRIGGER
and keep CHECK FUNCTION. I'm proposing that we get rid of all of the
dedicated syntax support, and expose it all through one or more
SQL-callable functions. If we need both
plpgsql_check_function(procoid) and plpgsql_check_trigger(tgoid), no
problem.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
Just to be clear, I am not proposing that we get rid of CHECK TRIGGER
and keep CHECK FUNCTION. I'm proposing that we get rid of all of the
dedicated syntax support, and expose it all through one or more
SQL-callable functions.
This seems entirely reasonable to me. The syntax support is not the
value-add in this patch, and it's been clear since day one that it would
be difficult for the syntax to cover all the likely permutations of
"which functions do you want to check?". A function-based interface
seems like both less work and more functionality. Yes, it's marginally
less convenient for simple cases, but I'm not even sure that we know
which "simple cases" are going to be popular. We can and should
postpone that decision until we have some field experience to base it on.
If we need both
plpgsql_check_function(procoid) and plpgsql_check_trigger(tgoid), no
problem.
FWIW, I would suggest check_trigger(regclass, name) not tgoid, because
we do not have a regtrigger convenience type (and I don't think it's
worth adding one).
More importantly, I do not agree with requiring the user to specify the
language name --- that is, it should be check_function(procoid) and have
that look up a language-specific checker. Otherwise, scenarios like
"check all my functions regardless of language" are too painful.
There is value-added in providing that much infrastructure.
regards, tom lane
2012/3/7 Robert Haas <robertmhaas@gmail.com>:
On Wed, Mar 7, 2012 at 12:17 AM, Pavel Stehule <pavel.stehule@gmail.com> wrote:
Robert, please, can you comment to this issue? And other, please. I am
able to fix syntax to any form where we will have agreement.Well, so far I don't see that anyone has offered a compelling reason
why this needs core syntax support. If we just define a function
called plpgsql_checker() or plpgsql_lint() or whatever, someone can
check one function like this:SELECT plpgsql_checker('myfunc(int)'::regprocedure);
If they want to check all the functions in a schema, they can do this:
SELECT plpgsql_checker(oid) FROM pg_proc WHERE prolang = (SELECT oid
FROM pg_language WHERE lanname = 'plpgsql') AND pronamespace = (SELECT
oid FROM pg_namespace WHERE nspname = 'myschema');If they want to check all functions they own, they can do this:
SELECT plpgsql_checker(oid) FROM pg_proc WHERE prolang = (SELECT oid
FROM pg_language WHERE lanname = 'plpgsql') AND pronamespace = (SELECT
oid FROM pg_authid WHERE rolname = 'myrole');Any other combination of things that they want to check can be
accommodated by varying the WHERE clause. If we think there are
common cases like the above that we want to make easy, we can do that
by creating wrapper functions called, e.g.
plpgsql_check_namespace(text) and plpgsql_check_role(text) that are
just SQL functions that execute the query mentioned above. If we find
that the convenience functions we've added don't cover all the cases
we care about, it's a lot easier and less invasive to just add a few
more than it is to invent new syntax. Moreover, it doesn't require a
major release cycle: using functional notation, a customer who wants
to check all security definer functions owned by roles that are
members of the dev group can do it just by adjusting the queries shown
above. If they want an extra convenience function for that, they can
easily define one. Even with dedicated syntax it's still possible to
define convenience functions by wrapping the CHECK FUNCTION statement
up in a user-defined function that dynamically generates and executes
SQL and then calling it repeatedly from a query, but that's more work.As things stand today, the "checker" infrastructure is a very large
percentage of this patch. Ripping all that out and just exposing this
as a function, or a couple of functions, will make the patch much
smaller and simpler, and allow us to focus on what I think we really
should be worrying about here, which is the quality and value of the
tests being performed. I don't necessarily say that it could *never*
make sense to add any syntax support here, but I do think there's no
real clear need for it and that, inasmuch as this is a new feature, it
doesn't really make sense for us to commit ourselves to more than
necessary. tsearch lived without core support for several releases
until it became clear that it was a sufficiently important feature to
merit tighter integration. Furthermore, the functional syntax being
proposed here is exactly the same kind of syntax that we use for many
other things that are arguably far more important. If
pg_start_backup() isn't important enough to be implemented as an SQL
command rather than a function, then I don't think this is either.Just to be clear, I am not proposing that we get rid of CHECK TRIGGER
and keep CHECK FUNCTION. I'm proposing that we get rid of all of the
dedicated syntax support, and expose it all through one or more
SQL-callable functions. If we need both
plpgsql_check_function(procoid) and plpgsql_check_trigger(tgoid), no
problem.
this is very near to my original design. This design is general so we
really don't special statements - because any action can be processed
in combination query to pg_catalog and calling checker function.
The most expected value of special statements is simplicity for usage
- checking function is common task - it should be called significantly
more often than pg_start_backup() or some tsearch maintaining
procedures.
just: "check function name()" is more shorter than "select
plpgsql_check_function('name()')" and what is important - it is
supported by autocomplete in psql. Other arguments is possibility to
show result.
A special statement has higher possibility to put output more clean
and readable - I am not able do it in function.
I agree, so this patch is relative long, but almost all code in these
statement is related to multiple checking. When I remove it (or when I
simplify) - I can significantly reduce patch (or this part)
But still I think so some reduced CHECK statements is good idea
mainly for:
* autocomplete support
* more readable output in terminal
I don't know if this is enough
Regards
Pavel
Show quoted text
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Wed, Mar 7, 2012 at 12:04 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
If we need both
plpgsql_check_function(procoid) and plpgsql_check_trigger(tgoid), no
problem.FWIW, I would suggest check_trigger(regclass, name) not tgoid, because
we do not have a regtrigger convenience type (and I don't think it's
worth adding one).
I'm OK with either one.
More importantly, I do not agree with requiring the user to specify the
language name --- that is, it should be check_function(procoid) and have
that look up a language-specific checker. Otherwise, scenarios like
"check all my functions regardless of language" are too painful.
There is value-added in providing that much infrastructure.
I might agree with you if we had more than one checker function, but
right now we are proposing to implement this for PL/pgsql and only
PL/pgsql. It seems to me that we can add that when and if a second
checker function shows up, if it still seems like a good idea.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/3/7 Tom Lane <tgl@sss.pgh.pa.us>:
Robert Haas <robertmhaas@gmail.com> writes:
Just to be clear, I am not proposing that we get rid of CHECK TRIGGER
and keep CHECK FUNCTION. I'm proposing that we get rid of all of the
dedicated syntax support, and expose it all through one or more
SQL-callable functions.This seems entirely reasonable to me. The syntax support is not the
value-add in this patch, and it's been clear since day one that it would
be difficult for the syntax to cover all the likely permutations of
"which functions do you want to check?". A function-based interface
seems like both less work and more functionality. Yes, it's marginally
less convenient for simple cases, but I'm not even sure that we know
which "simple cases" are going to be popular. We can and should
postpone that decision until we have some field experience to base it on.If we need both
plpgsql_check_function(procoid) and plpgsql_check_trigger(tgoid), no
problem.FWIW, I would suggest check_trigger(regclass, name) not tgoid, because
we do not have a regtrigger convenience type (and I don't think it's
worth adding one).More importantly, I do not agree with requiring the user to specify the
language name --- that is, it should be check_function(procoid) and have
that look up a language-specific checker. Otherwise, scenarios like
"check all my functions regardless of language" are too painful.
There is value-added in providing that much infrastructure.
I am able to drop all coded functionality and preparing these simple
check function is relative simple work. But I am not sure if we can
design better interface in future. It is relative well designed and
consistent with other statements.
I know so you and Robert are busy, but if you can have a 10 minutes,
try it play with it. There are nice autocomplete and "well" output. It
working well in psql.
Regards
Pavel
Show quoted text
regards, tom lane
Robert Haas <robertmhaas@gmail.com> writes:
On Wed, Mar 7, 2012 at 12:04 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
More importantly, I do not agree with requiring the user to specify the
language name --- that is, it should be check_function(procoid) and have
that look up a language-specific checker. �Otherwise, scenarios like
"check all my functions regardless of language" are too painful.
There is value-added in providing that much infrastructure.
I might agree with you if we had more than one checker function, but
right now we are proposing to implement this for PL/pgsql and only
PL/pgsql. It seems to me that we can add that when and if a second
checker function shows up, if it still seems like a good idea.
That argument is just silly. The only reason there's only one checker
function is that that's all Pavel has bothered to write yet, and all
that he's likely to write since (AFAICT) he doesn't care about the other
PLs. But other people do. There is certainly value in being able to do
checking of other languages, and if we don't set this up properly now,
we're going to have problems with having to change the user-visible API
later.
I said from the beginning that I thought the most important part of this
patch was getting the API for the language-specific validator functions
right, and I remain of that opinion. If we're going to blow that off
then we should forget the patch entirely until we have time to do it
right.
regards, tom lane
2012/3/7 Tom Lane <tgl@sss.pgh.pa.us>:
Robert Haas <robertmhaas@gmail.com> writes:
On Wed, Mar 7, 2012 at 12:04 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
More importantly, I do not agree with requiring the user to specify the
language name --- that is, it should be check_function(procoid) and have
that look up a language-specific checker. Otherwise, scenarios like
"check all my functions regardless of language" are too painful.
There is value-added in providing that much infrastructure.I might agree with you if we had more than one checker function, but
right now we are proposing to implement this for PL/pgsql and only
PL/pgsql. It seems to me that we can add that when and if a second
checker function shows up, if it still seems like a good idea.That argument is just silly. The only reason there's only one checker
function is that that's all Pavel has bothered to write yet, and all
that he's likely to write since (AFAICT) he doesn't care about the other
PLs. But other people do. There is certainly value in being able to do
checking of other languages, and if we don't set this up properly now,
we're going to have problems with having to change the user-visible API
later.I said from the beginning that I thought the most important part of this
patch was getting the API for the language-specific validator functions
right, and I remain of that opinion. If we're going to blow that off
then we should forget the patch entirely until we have time to do it
right.
I believe so with some minimal support for other languages - tj
check_function, there will be other checker functions early. Preparing
plpgsql_check_function instead check_function save 10 lines of code,
and we will close door to other.
I am working on some minimalistic patch
Pavel
Show quoted text
regards, tom lane
On Wed, Mar 7, 2012 at 2:03 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Wed, Mar 7, 2012 at 12:04 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
More importantly, I do not agree with requiring the user to specify the
language name --- that is, it should be check_function(procoid) and have
that look up a language-specific checker. Otherwise, scenarios like
"check all my functions regardless of language" are too painful.
There is value-added in providing that much infrastructure.I might agree with you if we had more than one checker function, but
right now we are proposing to implement this for PL/pgsql and only
PL/pgsql. It seems to me that we can add that when and if a second
checker function shows up, if it still seems like a good idea.That argument is just silly. The only reason there's only one checker
function is that that's all Pavel has bothered to write yet, and all
that he's likely to write since (AFAICT) he doesn't care about the other
PLs. But other people do. There is certainly value in being able to do
checking of other languages, and if we don't set this up properly now,
we're going to have problems with having to change the user-visible API
later.
If we publish plpgsql_check(regproc) now and a year from now we
publish anypl_check(regproc), the former will still work. There's no
need for an API break there.
I said from the beginning that I thought the most important part of this
patch was getting the API for the language-specific validator functions
right, and I remain of that opinion. If we're going to blow that off
then we should forget the patch entirely until we have time to do it
right.
Well, I guess I'm still of the opinion that the real question is
whether the particular lint checks that Pavel's implemented are good
and useful things. Has anyone spent any time looking at *that*? I'm
not going to stand here and hold my breath over the interface, but it
seems to me that if we don't know that we've got a worthwhile set of
underlying functionality, sweating the interface too much is putting
the cart before the horse.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/3/7 Robert Haas <robertmhaas@gmail.com>:
On Wed, Mar 7, 2012 at 2:03 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Wed, Mar 7, 2012 at 12:04 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
More importantly, I do not agree with requiring the user to specify the
language name --- that is, it should be check_function(procoid) and have
that look up a language-specific checker. Otherwise, scenarios like
"check all my functions regardless of language" are too painful.
There is value-added in providing that much infrastructure.I might agree with you if we had more than one checker function, but
right now we are proposing to implement this for PL/pgsql and only
PL/pgsql. It seems to me that we can add that when and if a second
checker function shows up, if it still seems like a good idea.That argument is just silly. The only reason there's only one checker
function is that that's all Pavel has bothered to write yet, and all
that he's likely to write since (AFAICT) he doesn't care about the other
PLs. But other people do. There is certainly value in being able to do
checking of other languages, and if we don't set this up properly now,
we're going to have problems with having to change the user-visible API
later.If we publish plpgsql_check(regproc) now and a year from now we
publish anypl_check(regproc), the former will still work. There's no
need for an API break there.I said from the beginning that I thought the most important part of this
patch was getting the API for the language-specific validator functions
right, and I remain of that opinion. If we're going to blow that off
then we should forget the patch entirely until we have time to do it
right.Well, I guess I'm still of the opinion that the real question is
whether the particular lint checks that Pavel's implemented are good
and useful things. Has anyone spent any time looking at *that*? I'm
not going to stand here and hold my breath over the interface, but it
seems to me that if we don't know that we've got a worthwhile set of
underlying functionality, sweating the interface too much is putting
the cart before the horse.
the core is based over plpgsql_lint - and this is used in some
companies about year
Pavel
Show quoted text
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
Well, I guess I'm still of the opinion that the real question is
whether the particular lint checks that Pavel's implemented are good
and useful things. Has anyone spent any time looking at *that*? I'm
not going to stand here and hold my breath over the interface, but it
seems to me that if we don't know that we've got a worthwhile set of
underlying functionality, sweating the interface too much is putting
the cart before the horse.
No, that's backwards. I have every expectation that the specific set
of checks will be extended and improved in future. But changing the
framework, if we don't bother to get that right to start with, will be
much less pleasant.
Not that having useful checks is not an important part of the patch, of
course. But it's not what everyone has been focusing on, and I do not
believe that we were mistaken to be more worried about the framework.
regards, tom lane
Hello
2012/3/7 Tom Lane <tgl@sss.pgh.pa.us>:
Robert Haas <robertmhaas@gmail.com> writes:
Just to be clear, I am not proposing that we get rid of CHECK TRIGGER
and keep CHECK FUNCTION. I'm proposing that we get rid of all of the
dedicated syntax support, and expose it all through one or more
SQL-callable functions.This seems entirely reasonable to me. The syntax support is not the
value-add in this patch, and it's been clear since day one that it would
be difficult for the syntax to cover all the likely permutations of
"which functions do you want to check?". A function-based interface
seems like both less work and more functionality. Yes, it's marginally
less convenient for simple cases, but I'm not even sure that we know
which "simple cases" are going to be popular. We can and should
postpone that decision until we have some field experience to base it on.If we need both
plpgsql_check_function(procoid) and plpgsql_check_trigger(tgoid), no
problem.FWIW, I would suggest check_trigger(regclass, name) not tgoid, because
we do not have a regtrigger convenience type (and I don't think it's
worth adding one).More importantly, I do not agree with requiring the user to specify the
language name --- that is, it should be check_function(procoid) and have
that look up a language-specific checker. Otherwise, scenarios like
"check all my functions regardless of language" are too painful.
There is value-added in providing that much infrastructure.
here is implementation of reduced patch - it is not final - no doc, no
regress test, just only functional interface
postgres=> \sf fx
CREATE OR REPLACE FUNCTION public.fx()
RETURNS SETOF text
LANGUAGE plpgsql
AS $function$
begin
return next 'In function f1():';
return next 'error:afasdf:asfsafdsgfsgf' || a;
return;
end;
$function$
postgres=> select pg_check_function('fx()');
pg_check_function
-----------------------------------------------------
In function fx():
error:42703:4:RETURN NEXT:column "a" does not exist
Query: SELECT 'error:afasdf:asfsafdsgfsgf' || a
-- ^
(4 rows)
caret is on correct position
I'll prepare check_trigger function tomorrow
Regards
Pavel
Show quoted text
regards, tom lane
Attachments:
Hello
2012/3/7 Tom Lane <tgl@sss.pgh.pa.us>:
Robert Haas <robertmhaas@gmail.com> writes:
Just to be clear, I am not proposing that we get rid of CHECK TRIGGER
and keep CHECK FUNCTION. I'm proposing that we get rid of all of the
dedicated syntax support, and expose it all through one or more
SQL-callable functions.This seems entirely reasonable to me. The syntax support is not the
value-add in this patch, and it's been clear since day one that it would
be difficult for the syntax to cover all the likely permutations of
"which functions do you want to check?". A function-based interface
seems like both less work and more functionality. Yes, it's marginally
less convenient for simple cases, but I'm not even sure that we know
which "simple cases" are going to be popular. We can and should
postpone that decision until we have some field experience to base it on.If we need both
plpgsql_check_function(procoid) and plpgsql_check_trigger(tgoid), no
problem.FWIW, I would suggest check_trigger(regclass, name) not tgoid, because
we do not have a regtrigger convenience type (and I don't think it's
worth adding one).More importantly, I do not agree with requiring the user to specify the
language name --- that is, it should be check_function(procoid) and have
that look up a language-specific checker. Otherwise, scenarios like
"check all my functions regardless of language" are too painful.
There is value-added in providing that much infrastructure.
here is updated patch (with regress tests, with documentation).
I removed a CHECK FUNCTION and CHECK TRIGGER statements and replaced
it by pg_check_function and pg_check_trigger like Tom proposed.
The core of this patch is same - plpgsql checker, only user interface
was reduced.
postgres=> select pg_check_function('f2()');
pg_check_function
---------------------------------------------------------------
error:42703:3:RETURN:column "missing_variable" does not exist
Query: SELECT 'Hello' || missing_variable
-- ^
(3 rows)
postgres=> select pg_check_trigger('t','t1');
pg_check_trigger
--------------------------------------------------------
error:42703:3:assignment:record "new" has no field "b"
Context: SQL statement "SELECT new.b + 1"
(2 rows)
Regards
Pavel
Show quoted text
regards, tom lane
Attachments:
Robert Haas wrote:
Well, I guess I'm still of the opinion that the real question is
whether the particular lint checks that Pavel's implemented are good
and useful things. Has anyone spent any time looking at *that*?
Actually, I did when I reviewed the patch the first time round.
I think that the checks implemented are clearly good and useful,
since any problem reported will lead to an error at runtime if
a certain code path in the function is taken. And if the code path
is never taken, that's valuable information too.
I don't say that there are no good checks missing, but the ones
that are there are good IMO.
Yours,
Laurenz Albe
On 03/08/2012 08:35 AM, Pavel Stehule wrote:
Here is updated patch (with regress tests, with documentation).
I removed a CHECK FUNCTION and CHECK TRIGGER statements and replaced
it by pg_check_function and pg_check_trigger like Tom proposed.The core of this patch is same - plpgsql checker, only user interface
was reduced.postgres=> select pg_check_function('f2()');
pg_check_function
---------------------------------------------------------------
error:42703:3:RETURN:column "missing_variable" does not exist
Query: SELECT 'Hello' || missing_variable
-- ^
(3 rows)postgres=> select pg_check_trigger('t','t1');
pg_check_trigger
--------------------------------------------------------
error:42703:3:assignment:record "new" has no field "b"
Context: SQL statement "SELECT new.b + 1"
(2 rows)
I did rereview and rechecked with few dozen of real-world functions, and
it still looks good from my point of view. I made bit of adjustment of
english in new comments and Pavel sent me privately fix for proper
handling of languages that don't have checker function. Updated patch
attached.
Regards
Petr Jelinek
Attachments:
reduced_pl_checker_2012-03-08_3.patch.gzapplication/gzip; name=reduced_pl_checker_2012-03-08_3.patch.gzDownload
�B�XO reduced_pl_checker_2012-03-08_3.patch �\{SI��[|�]�i�0~�bY!{�������v��KR/�r?�������������M��F1cZ]YYYYY���*���9�t~�����,��a�����'6����G��������t�������>����N����sg;�?��u�/_�<j�a����+ooG����Y���r'��~��]�7rB��ju�������~��a���]��<��l��%gn��<�Y�����Y���1� ;2x��$�]?V����;� n���x����@?|�z~��Q��<cE�H��� Xu[����8�3������;lg>��{gM%K���8�^'Ic�{E�������-���Ns��P�zSd�=A��/����$����Y�Gw0��
���Y���-������ ����H��q��`E�S���q����<���b���+�
���xO��O�O�F��9�:d�4.��Av�<R����;���N��ap
K��W`p�=���� ��4b�U��t�37�[5��������C�boUZ}PL�gvJ[1�ih���N��B�/�[�T,��MLN�8��T�]�f�I�)_�U)��N{��iO1-g��MIN�4er4���I"V��JU��s)SZ���R2���
�,3��6$��ct���,��z%���$��GW8��h;J���������=��4&��F��u
Lr� ���K��`B�MpIy��������'Z��w��?�����\T�X�SL!#����Kow���ba�g9�,�kg��������f6��n{7����m6�z�M4;�����1y������WGo������(�%�r7N=���C�-��E1�����&t��[v��K�Q�c7����ugl����:b`4Y�Q�h��;�[-������z�p�� |0�����&��!�c�bJ��K�m�&�����[�xE�f���E��I}
���9�U��0D�������
����� �e����Z�~��:<V����f���&:R�}DJL"�~
H����m��I��t#���J���x��5�w���;y�=pk�7�����o��CD���q�p2:�������O�F, 2�(sW>�z���X,�����8�k�q�z�b���)8*7����,�#\�-���N��X]T
����?�-_v���l`���l_��L97����� ?:w�`�b�B ���"��j���� ����p\���f9�e���Hp��%�Q']b?���J�0�9��H~C^�����Z�N�p��
��bo�U��}:b������D�O�`�J#��I!������5<]����o����� O1�Bu<��K��a����.��w��jq�{2#D�Imm�G���mV$I��2���h��r���Ld��w�R�M�bH��8a�n%�D�sxp�>a��/�Go�7F���!m���k������A��lv��C�S��9�[P�48�������E
��(dm�f�� �'x4���F`��� fA ��,�������Y�0Qn'k�����Rp��8���;�)��!
�J���������D��A�#����m�����2in�q�9 �M������' (�I�a����KF�YSw9�Q��� " �%���z�Q.���B�eh�[�NQD�T��;~����
���F+�h�o���-T�d���o�F�$� @�'+jUs�/�Z?���K�����y��V�����Z�hU�
��y�}��]|��Z�q������ ��a�#�ijn_?�6���mt*�����
Xk��������zA����cx��5k�mxJ�S_L��+����:�u_��]M������Bl�X)�^��x���L�q��*���`Q����/_��PY��h�lW��#�2���)�w�{/)J�F�����C�8����C_��� �>���2�_���
���M����� >2=�u��@&�s5�*���T�#7( BG�"���!/�A��8����f��J��&n^ �I
�?�WW��n#�j�RyN&0�b���KA17�:��UR ��;P�`AQh����R����E0�p
c�m�ZB��r�Xg����J�*�F��!|
�<5��x�9�;s���)=��R���?�����h� �/"�bvbT��=���e�l�u�fS��N��o������������?�ne�!+�JKp���
��oD��8� dD�;�J�8�k+�����MF7����
>�L����O��K����)���q9�.rjgM�N��� 4�0�|���.�u0��~9���M�������
4��_}Uar[���~=�_�O���`�U��j'�9��� ������Y�@nn���hS�$�|�\���{�)����(�_�/�|���kGV�t*#?Q� ��|s����T�Z���[��0��}RMYM��oW�iO��j�RHU:�I|����$�8�h@�p�C� ��N95���O3�8�������������iO���_��N�>�&%����*q� a@&�F�����qj�"��������#�s_P�i J_�=�)f�O����+R�\U��
�=i��+Q����[�wsv���L?�WP��D�[�U�3mL�7��H"�%�cUl����IC(�
.�_�3��j'#�.$c% �qR8!O1m��<W�I��i7{���={��&�����Sp�\Y8{�D�=w.��+g�d�uP���9������W�Y% �Y'��h�D��4���z�d��fE�8�-sL�T�i-�1�w��wdR�|=�("c0���"fx��e�$|�*)���]����
�H�4p�,����h��EI�k��&1M���i�~rs\X��s���u�,�U�<)�����M��L0�dH�~�����g���
���B��*������s��r������&GO�#R��P"n<~����?����C��.�`/z���}g����Y!Pl��Rnk��q��oh�1��,*�q������p���3���]:iM���\����#�!���@�T���9����h �6����������p@T��"���������`��E
��.�h\���A��I����Q��<���]Sl|or\k�?N�'�'����?��3�s�(�<�}�^���C�S[(� ^�@�"��/�I�9�"i�����&��c��o���i�sL�e��
z0�U2C��'�@`�U�����
��!���m���#q�C+��@B�(��fJ��#@}R��<���������pf���}Jo��^�� �AW*n�b>R��%����J��'����y�>�����D8g\�m�����2������C0b_�^����n��o��]�����x~��Eh�����p�~��
s�g{5E[��6�����y{4�N
=��F���&�(�����������U�{;�$��������R�}>�p��,��/n������)���iX��
qY����h��4����j
-6w��3��3�/�G"aB�i��a�#����+���q�eN1��<4U�+2kE�1���B��Rs=cW�./�
�O��\�n��Z-��6��5�L�'r� �D�����������dd_]O��O77�����%�1����jB���w��L��Q
�?9[}�2Q�l�T�7[-Z=��OR�������C{�n�O>�A���>����B<�N����Z�y2�n��+���B�tO��T����F\��
�j�v�I�i� Z�����*}pI�O_r�$��P��eA����B��.�������������z�d���I �rh���~���_���wo������C����r�����d�����R��@��
^l6J�������TK��*cRxw�`�Yj�o�HC�P��&
J�[�I�b"����h���d���f1�\l���u���W�:K��e-�����0[|?,mf�[]>��Fc��}gl
�! osZ���t���}c�[���B�C�������G�x,x�X���|AD8���8O�9������F$|���P,��4K�u_�{N�2Z�Q����N�!��0��BS6*r�*��Z��X�[�t��g&6���Z����0 L���}��J�������ei����<��z�A�fY�o��}��@umH(�����?Q�^�����2:F:�^E���1��5�d�X��r:N�����^#,�� ���rX��A>o��:�A� /�m?(n��
t�X�^����O9O����8E%K�<�&�Y��&Vs#�'v��{��d#��
J� ����h���T�tT������+m��x�����p9�V��.���!�F�&�kAI`�����x�h<��}g�'x4�����s��0�x�b��rM_�X�[�&N�����yI��1��|5�
%+<��KU��Z5���4���Yj� �+� �d��������_#�����2��<AS�Yi'
��)ci�)� ��@�3�/#?�5Gl�rdJ�-�6jS2�!�K�JYc�b���h�:��s<��/7��9�4)���es���6D�|e���%��=G�;����l�E���c�5���=�?��y�V�8�'ei��0!���/��l� l������x�#�� ��!��;���\�T�� ����uC1��FK,��&Jt�`u�f\���C�\|�]!&t1$/wQ� }=q�N���;?)/_��+:�4��� ������tpK������b���N'��������=Uf��+���c[U��D�H�<�|�R^yQ��v�J!�Gj�Y:]��'������\G����
Ta5�������Jd�<�b
���&x*��D��F��!`Zx��o�ebE����Z�
��b���K;�+���v���@��i����9�T'��e�%I�;�Zf#��n�@�I^���. ��6�/�#�A�*QC��4 �~v�_��y8%�w���Wx]N�
���~��v�Stw�������L���$�&� ��&�i ����FPlV2�E���u�s�k�b0R���u���~�a���q#���5���6@ uG�F{���@P������W��Zc�������������z!�Hk/�^]��~ZO�[�����<��v���:^����WS
6a`��T�)��'aO�uRH��%�� ��C���s�o�"z���Z��.�������kk�2�:��m����~@v���?8S�g�^@PM��5���_^^oGS�b�������8�����kd����)t���db�b�[���
K���2@*�c���]�\mV�Y!��3��U!; #j� ����;;j/1�����%6�9L����W�hT��QT�� [�M3�������U�;�s�\�x�0�no��JPWmV5�6{��M#�7[h�������&�4�q�!�����?go���bO��[�������������� ��ZId�V���S:������o��OI�(-������_g]
o+2�`���3.��V�p��0������s�X�o�e7����*+�������.���}��d(Ov�=����VE7���]i&��\�K�)I��hm`�$"�{�?�#��n���s6���h2Rp��9dC�
�����;�rt�P��w�l�AGR���� ��x��P��*�Q�8c�Ll�C�-;�u��*3 ?ZS�:��2�o�����+Io���k�K��{������R���{zAw�~�+G�+��'����0g!���w�� ��v(bo�/�_r>;#5������;�VL�X+�������?ABID�����f�������w�2�<��0���T���[�*�-�@���& |����_�y/kV��U��4
�����\�L��\���G':�-����`�����;��*������:���2�"F����}y_W����S�a� @������� ���@@�������������d�$������& ����������S��N-Y���R�7���^b�a5�ln�I�,�zX��f��T0�����%��2����"o=rG�����9��7������E�w�sl����[k/�'9�w��ZV�:��&~JU��s}5���NM�|8���5���T�A9����0�����5Uq���Hh4!���Y���s�x`�r�JE� �g
�J>*\���U�4���p}N���������R�I����b��T��W^�4jd�C|a�f��y4O7�P���i4����c!���?�5� ��*������*%��d��=X����FEt�����<B? ���m,�jYU��\�����XL��f�x�/���Z�F
"K��g����+���=
��O*B��c�t���{��K�3n]_�+?�3���j�B-9��g1p�:8�-��I�P#S*q<U�;���:�7���t��J��E7_�Q���������W��:�g����K�}}&�]���B�df�xM�z1���;J��w���H����l���<73p����l��#n�py��5Q)�����>f�pf�1D3PgTtM�n��K�hL��dR����+��p��>e��u��t�����[��Z�����v�8�Ym8�Y���n�Q�G�p�~��_&�C���V�,���R�PN8��Z�����j:�]�q�}4 7��������MB5��M�
��v�C�h��3�'T�O��~|GAp�+����:W�o��8���/��/�ty�J�
������Rj�dF���������`���������.,q|��7����^����cf)60l��3�T>�����q�;��e\�\g;�x�������f��gg?y%������,_�o�����x05k�\P�;�$ w������T�*�������7��->��^�I�`�����Pk-P�z��SM5l���D<�oo�w��J�
<a��h-��o����<�|�
�/($�o����?��4)����[����=�P]c��;�l* ��6( ���u������(�uxY6pV@i�6E���{���U�]i���kZ�'d��b'��77�E��-z�����.�W������%Z�f���8 =5E�f�[��:K��}���#��u���<�����(b
/�����o-�'h�J���.�EtH���=u��MR^��`��\���Dg1*
��/A]��w��T�t�/v��������������N�Bg"������wvQY��= ��^^~hW������
��5� }����Wi��_������T"G�hA�;�� x����6&���ZE'�X6��FY�������B
$�����"�%�;�����=�]f���wZj�|�q`���s�4/ �f�T�nle�ZU�j��pL$�8R
�����j��K"�D}�O]W�k����%4=� s'������u��t�4����%7���t��
.����Z1)��4]�9�\���[���}|~���8~���:>i�%d�t������
����Z|L�E�['���u�5��7���o��O�s�/4�d!����.��oo��_|qt�Gn~�8��&�����7���l�w�?�����P.�{��'e�]����[���N����[9��]�`{��:v7V���M\j'�W
���j����/���k���-RO��Rf�k��� s6���$�F?W�cU8eY�����?����_J�0�1J3�u-�8�^R���J�K�qB��J�r^~-��������=��{�F�S�J���I��z�ln�����?+3^�k'�VA�1w����1�����~k�0�L�*DU
/������������E������K#�����ij�U���_a(J]��\��)9���)��vS�� �'���~<�9�g��GB5>U�mpm�C�[��������v�1�)7�fA;�0��b �8"��n��T#l���q����'A�nQ}N\�6o#�ncs�Q��w���%N�u��h0�>\����`���oLd��8m]�.N['?z�������\����n���eP����h����D�M�5
�q�p�
����.��A��R�e��u���|�X���$��E�U���B�?�g�^g�����U��.���^����]]��=��e���@T�VC�=K9�^�pf�R�#�,����IW��q �� �������S^���C�Y���;�N�C���x���V�e�
���aA-����7V�Y<\:G���+���M�w���
W�{������4z~��9����O���{'S���v�1}��a�F���f���s�9����l��?���)�ZW���q��w�<�d�WF)w=iB��+O$BdX��
1�"���E?[{L����������V�?������?�[�@>�������<� C�����c�m_���tr��/�{"H�i*B������� �oT�a��.j�������FM5)VVm��iY.�M�����
�9����%����
b�'�����n�ag6
��^�}�na(H���o�;�C&��M4��m��2ax�����t���iN!'�gn*� �F�����.�^D�� �$d��`}�r��][NQicY{=�1K{�"]��D,r���@�U!��mH �v�KmC����������/`JY9V�K��M#G ��yF���
C�5����u� E�Dq��'���e�g�������,k ����d���F�W��lu����U�v����I���t!��[U�%�����Nc���B� '���3�eS1V7������������P�a%���W���?1�|�g6���)���B s�yn�
zd8�����,Q��7����e�z,J����"���=�`�o&�����h�)?��D�C��W_�����#:�ICR*�����x`�J��?�dF�->Og�8�g=�tCK@�6iD��� ���u]P�P�B�NI�H1='�!)S3�R*���
���fc���&}2����P���F��U����r�*�g�G��M��5�C�8�9�'m��Q�����&� ��&p����j���r=hN�;�c1\�i�08to6�O��e��4�����=����A����g������|�����"�M��������$.O�w�G4�fS&-���(��W)t� G�i+�>4�?x����2�)
�s�I��-D�G0��I������Sr-}��8��
�g���e��*�g�p��$�T�,�����h|Lbry)��o]���e\�W���M�(���]��Y����W��~���A�d���N�M#e�V�������ZI��/�<�*���,kx����X���5i!�������zs}�������w������ysy}
���8C�N� N�S�P�>�x������'��1���k����lo���Yzs� 9
�� KQ;�^�~����*J��s�����E�#�N�����5����2����4=��+�(���1�d�W� �E�f}G~-
�������T�E���I[ @`z�q��[�BC ��69=J�bo3hh#���^'_��ZNcZ��R������*k�";��J�R%�1�9�m|�\@���\�Xc��CMh�3".'����� ��m�v��N�?�sxK#Y��B�!���~��0��xD�G�Q1c�������r��&������n9eV���b���#�L$S�(=��!�/�a�ml�N��!b:E>%�*����������y[�X�5�4���9�.�P�E��~����w�]�������u�� Yivs�Q�)�b�
WV:�+���+x�x4��
�7�y��~��[`�i0�������`�P�
xl�IJ��p(��T&81^r�b|cV��2���U��s[���?@n�=I�0���#������?� �������]��u���0v�v����������)��r� J�� ��AF�6Q_C���+���*+������c�`)$B��������L�
�K�o<� )��k?��9�BB�]��u'�������k���Nw�w��w7G��c��/x�c/��!H�U��-�S��~lD5����SKL��G)���XPsE"�8�t\�Pp�N�ea��(�2
�+9����b���t��`����n���{�E)n�,Nq9Z$��
�������V9�������������[ ��\��qV��|I���:-�#'�����N+�198:�6���]5K,��E���w������� ��g�A0���yQ�9�
J�G#4�j�7|�;��J`����� �eS6�W a_
e���v7*���q�H��Q��R7�����q��1_���Xp����/V�ek@q�O���:�������0�>���5b��B��J�Fq)��K�&���+�-����4���ZQG�K;s�D^*��8\�3����pA^`�Ax=�(�w�I�2��D��B�h����p1�+�S�/�L�l�x)�
Q���KWo?��>���5����[eO�����&�i������6��jh�AL��O��z��@����R]��{a��a�`�p��� �{ ���L��{3s���� ��T
`���+g�������g��v���@�EPc�Pt����!���wz;�Z����4:D�v
�Q�d�AjJV�����x��3����u���n�Y��=������C/�o� ����<?��s�:g���xOX��#�`M���5�t�5�.V��]?u���v�L���:�Y�:O�z��q�,>�M���bS��w'�d�� � yM�q*'
J�`���(���l�'����J���E� �q'J�Cv7}�f�D����D��#9�*��WT�>�U� �>��"M��^=�\�$�.v��1�o�����~zH��5��y���Z/�J_�~0����i��Iw?�Z���D�������zs[�A�xl�_���8��0�_ ��Y��9 �f��lM�~f�=��|����w��W������y� �M��ec�A4��h�
��?��3��H3o ���4�\����=,T������
y�V����Zjo����,����s��t��-�=������'����uycy�����<?��K��}Y3v���:o��_�L��������6�����W��^g{��V���ns1��[(���Y{�;��'�BRLE�B����/�b�Q�a�����B�i�������\�n��������+�RsDN��H���
��h�(��/V������5�#�����T������=�Q�P�l�����I>[7l�)��E�S��������q��J
0�� �LEL k�`s��V.86%8�wQ�_��~�4��4�5�2�h�Y,��NG��C��,H*@-L<���|��\@Y�����N�{�m\�x���.hK�O�����#,��,����?��j����r� qg��(�^R��n���Zm�`�`��+�F�2�Q�R�=�E���V���O�D�Tj�u�H���Z�\������
��u���Fh#Z��YjJbW�p����n<&3CUSr����[����@6HN
���v�9����g�H"��$ZI�����4p pJB��,T>G�N������e?N�:������3������m��(�te���j61���N#���M �)D�[{�m��{��������o�������i����E��'e�6�t�%I�l�D�����0��,��������;;;�M�K�Moo��d4�^��B�2��m����;��I��(!s�[�&���p=j2�<�F�&l�p�.B�q�b-���(qd�nF+��VRW���p�C����c��.���<��o�4J?�R��)-:Eo%������}t��i����i���o�%��
�
n�������/q{^����x���7��u��
3k�7�h�_�Ui�e�J��Q��/��f����zm�_���N������_2U�J��z�S�>�� �,�^j��lp���aa����j�F/���gP��r���5��������uu>������v����7�(�%aQ��������c�
a�j����r�;}������s4�#��^����zT��2k�����I!�uI|�R*�K 6� ���$`�B����r�2��������
��@'p�S�� 1��?��h~�B�O9z�?��n���U�o@�pl��#��|��@�5Y�yY%!bA�m�y���G�7K2 '��Z��1`��>�� ������cF���IN�'!�a��8�k�vc����1 �h�1����aM�wa�S�d���v�W84
��Jm?���*��$GJ?�&