2010/1/28 Robert Haas <[email protected]>:
> On Thu, Jan 28, 2010 at 4:53 AM, Pavel Stehule <[email protected]>
> wrote:
>> 2010/1/27 Robert Haas <[email protected]>:
>>> On Mon, Jan 25, 2010 at 7:36 AM, Pavel Stehule <[email protected]>
>>> wrote:
>>>> I hope, so this version is more readable and more clean. I removed
>>>> some not necessary checks.
>>>
>>> This still seems overly complicated to me. I spent a few hours today
>>> working up the attached patch. Let me know your thoughts.
>>
>> There is serious issue. The "psql_error" only shows some message on
>> output, but do nothing more - you don't set a result status for
>> commands and for statements. So some potential error from parsing is
>> pseudo quietly ignored - without respect to your setting
>> ON_ERROR_STOP. This could be a problem for commands. Execution of
>> broken SQL statements will raise syntax error. But for \set some
>> variable will be a broken and the content can be used. I don't thing
>> so it is good. It is limited.
>
> Well, what you seem to be proposing to do is allow the command to
> execute (on the screwed-up data) and then afterwards pretend that it
> failed by overriding the return status. I think that's unacceptable.
> The root of the problem here is that the parsing and execution stages
> for backslash commands are not cleanly separated. There's no clean
> way for the lexer to return an error that allows the command to finish
> parsing normally but then doesn't execute it. Fixing that is going to
> require an extensive refactoring of commands.c which I don't think it
> makes sense to undertake at this point in the release cycle. Even if
> it did, it seems like material for a separate patch rather than
> something which has to be done before this goes in.
so I removed support for escaping from backslah commands and refactorised code.
I hope so this code is more verbose and clean than previous versions.
Regards
Pavel
>
>> Your version is acceptable only when we don't enable escape syntax for
>> commands. Then we don't need check it. On your version - I am not sure
>> if it is fully compatible, and using a global variables isn't nice.
>
> I'm not adding any new global variables - I'm just using the ones that
> are already there to avoid duplicating the same code four times.
> Referencing them from within the bodies of the lexer rules doesn't
> make the variables not global.
>
> ...Robert
>
*** ./doc/src/sgml/ref/psql-ref.sgml.orig 2009-12-25 00:36:39.000000000 +0100
--- ./doc/src/sgml/ref/psql-ref.sgml 2010-01-28 16:57:15.016331154 +0100
***************
*** 658,664 ****
<para>
If an unquoted argument begins with a colon (<literal>:</literal>),
it is taken as a <application>psql</> variable and the value of the
! variable is used as the argument instead.
</para>
<para>
--- 658,669 ----
<para>
If an unquoted argument begins with a colon (<literal>:</literal>),
it is taken as a <application>psql</> variable and the value of the
! variable is used as the argument instead. If the variable name is
! surrounded by single quotes (e.g. <literal>:'var'</literal>), it
! will be escaped as an SQL literal and the result will be used as
! the argument. If the variable name is surrounded by double quotes,
! it will be escaped as an SQL identifier and the result will be used
! as the argument.
</para>
<para>
***************
*** 2711,2728 ****
<para>
An additional useful feature of <application>psql</application>
variables is that you can substitute (<quote>interpolate</quote>)
! them into regular <acronym>SQL</acronym> statements. The syntax for
! this is again to prepend the variable name with a colon
(<literal>:</literal>):
<programlisting>
testdb=> <userinput>\set foo 'my_table'</userinput>
testdb=> <userinput>SELECT * FROM :foo;</userinput>
</programlisting>
! would then query the table <literal>my_table</literal>. The value of
! the variable is copied literally, so it can even contain unbalanced
! quotes or backslash commands. You must make sure that it makes sense
! where you put it. Variable interpolation will not be performed into
! quoted <acronym>SQL</acronym> entities.
</para>
<para>
--- 2716,2750 ----
<para>
An additional useful feature of <application>psql</application>
variables is that you can substitute (<quote>interpolate</quote>)
! them into regular <acronym>SQL</acronym> statements.
! <application>psql</application> provides special facilities for
! ensuring that values used as SQL literals and identifiers are
! properly escaped. The syntax for interpolating a value without
! any special escaping is again to prepend the variable name with a colon
(<literal>:</literal>):
<programlisting>
testdb=> <userinput>\set foo 'my_table'</userinput>
testdb=> <userinput>SELECT * FROM :foo;</userinput>
</programlisting>
! would then query the table <literal>my_table</literal>. Note that this
! may be unsafe: the value of the variable is copied literally, so it can
! even contain unbalanced quotes or backslash commands. You must make sure
! that it makes sense where you put it.
! </para>
!
! <para>
! When a value is to be used as an SQL literal or identifier, it is
! safest to arrange for it to be escaped. To escape the value of
! a variable as an SQL literal, write a colon followed by the variable
! name in single quotes. To escape the value an SQL identifier, write
! a colon followed by the variable name in double quotes. The previous
! example would be more safely written this way:
! <programlisting>
! testdb=> <userinput>\set foo 'my_table'</userinput>
! testdb=> <userinput>SELECT * FROM :"foo";</userinput>
! </programlisting>
! Variable interpolation will not be performed into quoted
! <acronym>SQL</acronym> entities.
</para>
<para>
***************
*** 2730,2769 ****
copy the contents of a file into a table column. First load the file into a
variable and then proceed as above:
<programlisting>
! testdb=> <userinput>\set content '''' `cat my_file.txt` ''''</userinput>
! testdb=> <userinput>INSERT INTO my_table VALUES (:content);</userinput>
! </programlisting>
! One problem with this approach is that <filename>my_file.txt</filename>
! might contain single quotes. These need to be escaped so that
! they don't cause a syntax error when the second line is processed. This
! could be done with the program <command>sed</command>:
! <programlisting>
! testdb=> <userinput>\set content '''' `sed -e "s/'/''/g" < my_file.txt` ''''</userinput>
! </programlisting>
! If you are using non-standard-conforming strings then you'll also need
! to double backslashes. This is a bit tricky:
! <programlisting>
! testdb=> <userinput>\set content '''' `sed -e "s/'/''/g" -e 's/\\/\\\\/g' < my_file.txt` ''''</userinput>
</programlisting>
! Note the use of different shell quoting conventions so that neither
! the single quote marks nor the backslashes are special to the shell.
! Backslashes are still special to <command>sed</command>, however, so
! we need to double them. (Perhaps
! at one point you thought it was great that all Unix commands use the
! same escape character.)
</para>
<para>
! Since colons can legally appear in SQL commands, the following rule
! applies: the character sequence
! <quote>:name</quote> is not changed unless <quote>name</> is the name
! of a variable that is currently set. In any case you can escape
! a colon with a backslash to protect it from substitution. (The
! colon syntax for variables is standard <acronym>SQL</acronym> for
embedded query languages, such as <application>ECPG</application>.
The colon syntax for array slices and type casts are
<productname>PostgreSQL</productname> extensions, hence the
! conflict.)
</para>
</refsect3>
--- 2752,2777 ----
copy the contents of a file into a table column. First load the file into a
variable and then proceed as above:
<programlisting>
! testdb=> <userinput>\set content `cat my_file.txt`</userinput>
! testdb=> <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
</programlisting>
! (Note that this still won't work if my_file.txt contains NUL bytes.
! psql does not support embedded NUL bytes in variable values.)
</para>
<para>
! Since colons can legally appear in SQL commands, an apparent attempt
! at interpolation (such as <literal>:name</literal>,
! <literal>:'name'</literal>, or <literal>:"name"</literal>) is not
! changed unless the named variable is currently set. In any case you
! can escape a colon with a backslash to protect it from substitution.
! (The colon syntax for variables is standard <acronym>SQL</acronym> for
embedded query languages, such as <application>ECPG</application>.
The colon syntax for array slices and type casts are
<productname>PostgreSQL</productname> extensions, hence the
! conflict. The colon syntax for escaping a variable's value as an
! SQL literal or identifier is a <application>psql</application>
! extension.)
</para>
</refsect3>
*** ./src/bin/psql/psqlscan.l.orig 2010-01-02 17:57:59.000000000 +0100
--- ./src/bin/psql/psqlscan.l 2010-01-28 17:51:40.674834578 +0100
***************
*** 119,124 ****
--- 119,126 ----
static void emit(const char *txt, int len);
static bool is_utf16_surrogate_first(uint32 c);
+ static char *take_variable_name(const char *src, int len);
+
#define ECHO emit(yytext, yyleng)
%}
***************
*** 707,712 ****
--- 709,803 ----
}
}
+ :'[A-Za-z0-9_]+' {
+ /* Possible psql variable substitution */
+ const char *value;
+ char *varname = take_variable_name(yytext, yyleng);
+
+ value = GetVariable(pset.vars, varname);
+
+ if (value)
+ {
+ /* It is a variable, perform substitution */
+ char *escaped_value;
+
+ escaped_value = PQescapeLiteral(pset.db, value, strlen(value));
+ if (escaped_value != NULL)
+ {
+ push_new_buffer(escaped_value);
+ free(escaped_value);
+ }
+ else
+ {
+ const char *error_message = PQerrorMessage(pset.db);
+
+ if (strlen(error_message))
+ psql_error("%s", error_message);
+
+ /*
+ * when we cannot copy escaped string, then copy
+ * the string as as
+ */
+ ECHO;
+ }
+ /* yy_scan_string already made buffer active */
+ }
+ else
+ {
+ /*
+ * if the variable doesn't exist we'll copy the
+ * string as is
+ */
+ ECHO;
+ }
+
+ free(varname);
+ }
+
+ :\"[A-Za-z0-9_]+\" {
+ /* Possible psql variable substitution */
+ const char *value;
+ char *varname = take_variable_name(yytext, yyleng);
+
+ value = GetVariable(pset.vars, varname);
+
+ if (value)
+ {
+ /* It is a variable, perform substitution */
+ char *escaped_value;
+
+ escaped_value = PQescapeIdentifier(pset.db, value, strlen(value));
+ if (escaped_value != NULL)
+ {
+ push_new_buffer(escaped_value);
+ free(escaped_value);
+ }
+ else
+ {
+ const char *error_message = PQerrorMessage(pset.db);
+
+ if (strlen(error_message))
+ psql_error("%s", error_message);
+ /*
+ * when we cannot copy escaped string, then copy
+ * the string as as
+ */
+ ECHO;
+ }
+ /* yy_scan_string already made buffer active */
+ }
+ else
+ {
+ /*
+ * if the variable doesn't exist we'll copy the
+ * string as is
+ */
+ ECHO;
+ }
+
+ free(varname);
+ }
+
/*
* Back to backend-compatible rules.
*/
***************
*** 1740,1742 ****
--- 1831,1851 ----
{
return (c >= 0xD800 && c <= 0xDBFF);
}
+
+ /*
+ * remove colon and outer qoutes from text
+ *
+ */
+ static char *
+ take_variable_name(const char *src, int len)
+ {
+ char *result;
+
+ result = pg_malloc(len - 2);
+
+ /* skip first two chars (colon, quote) and stop before last char (quote) */
+ memcpy(result, src + 2, len - 3);
+ result[len - 3] = '\0';
+
+ return result;
+ }
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers