*** ./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-25 13:27:14.216465115 +0100
***************
*** 2335,2340 ****
--- 2335,2361 ----
      </note>
  
      <para>
+     <application>psql</application> provides two additional syntax for 
+     retrieving the content of variable. This auxilary syntax ensure
+     necessary escaping when we would to use content as sql literal or
+     sql identifier.
+ <programlisting>
+ testdb=&gt; <userinput>\set foo 'hello world'</userinput>
+ testdb=&gt; <userinput>\echo :'foo'</userinput>
+ 'hello world'
+ 
+ testdb=&gt; <userinput>\echo :"foo"</userinput>
+ "hello world"
+ 
+ testdb=&gt; <userinput>SELECT :'foo' AS :"foo";</userinput>
+  hello world 
+ -------------
+  hello world
+ (1 row)
+ </programlisting>
+     </para>
+ 
+     <para>
      If you call <command>\set</command> without a second argument, the
      variable is set, with an empty string as value. To unset (or delete) a
      variable, use the command <command>\unset</command>.
***************
*** 2722,2728 ****
      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>
--- 2743,2755 ----
      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. Identifiers with special chars
!     have to be inserted between double quotes. <application>psql</application>
!     ensure necessary quoting with additional syntax:
! <programlisting>
! testdb=&gt; <userinput>\set foo 'my tab'</userinput>
! testdb=&gt; <userinput>SELECT * FROM :"foo";</userinput>
! </programlisting>
      </para>
  
      <para>
***************
*** 2754,2759 ****
--- 2781,2795 ----
      </para>
  
      <para>
+     With alternative syntax for retrieving content of variables external
+     escaping are not necessary:
+ <programlisting>
+ testdb=&gt; <userinput>\set content `cat my_file.txt`</userinput>
+ testdb=&gt; <userinput>INSERT INTO my_table VALUES(:'content');</userinput>
+ </programlisting>
+     </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
*** ./src/bin/psql/command.c.orig	2010-01-02 17:57:59.000000000 +0100
--- ./src/bin/psql/command.c	2010-01-25 11:40:00.409538294 +0100
***************
*** 99,104 ****
--- 99,110 ----
  	char	   *arg;
  
  	psql_assert(scan_state);
+ 	
+ 	/*
+ 	 * Invalid value cannot infect a database, but we have to trace any
+ 	 * variable expansion.
+ 	 */
+ 	psql_reset_substitution_fault_flag(scan_state);
  
  	/* Parse off the command name */
  	cmd = psql_scan_slash_command(scan_state);
***************
*** 141,146 ****
--- 147,162 ----
  
  	/* some commands write to queryFout, so make sure output is sent */
  	fflush(pset.queryFout);
+ 	
+ 	/*
+ 	 * When some variable substitution wasn't successful, then
+ 	 * change status to PSQL_CMD_ERROR. Error is already reported.
+ 	 */
+ 	if (!psql_scan_is_valid(scan_state))
+ 	{
+ 		psql_reset_substitution_fault_flag(scan_state);
+ 		status = PSQL_CMD_ERROR;
+ 	}
  
  	return status;
  }
***************
*** 1162,1168 ****
  
  	else
  		status = PSQL_CMD_UNKNOWN;
! 
  	if (!success)
  		status = PSQL_CMD_ERROR;
  
--- 1178,1188 ----
  
  	else
  		status = PSQL_CMD_UNKNOWN;
! 	
! 	/*
! 	 * if variables expansion are unsuccessful, then result
! 	 * have to be error too.
! 	 */
  	if (!success)
  		status = PSQL_CMD_ERROR;
  
*** ./src/bin/psql/mainloop.c.orig	2010-01-02 17:57:59.000000000 +0100
--- ./src/bin/psql/mainloop.c	2010-01-25 11:38:10.630538412 +0100
***************
*** 61,66 ****
--- 61,67 ----
  
  	/* Create working state */
  	scan_state = psql_scan_create();
+ 	psql_reset_substitution_fault_flag(scan_state);
  
  	query_buf = createPQExpBuffer();
  	previous_buf = createPQExpBuffer();
***************
*** 105,110 ****
--- 106,112 ----
  			/* reset parsing state */
  			psql_scan_finish(scan_state);
  			psql_scan_reset(scan_state);
+ 			psql_reset_substitution_fault_flag(scan_state);
  			resetPQExpBuffer(query_buf);
  			resetPQExpBuffer(history_buf);
  			count_eof = 0;
***************
*** 218,223 ****
--- 220,226 ----
  		 * Parse line, looking for command separators.
  		 */
  		psql_scan_setup(scan_state, line, strlen(line));
+ 		
  		success = true;
  		line_saved_in_history = false;
  
***************
*** 254,261 ****
  				}
  
  				/* execute query */
! 				success = SendQuery(query_buf->data);
! 				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
  
  				/* transfer query to previous_buf by pointer-swapping */
  				{
--- 257,269 ----
  				}
  
  				/* execute query */
! 				if (psql_scan_is_valid(scan_state))
! 				{
! 					success = SendQuery(query_buf->data);
! 					slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
! 				}
! 				else
! 					slashCmdStatus = PSQL_CMD_ERROR;
  
  				/* transfer query to previous_buf by pointer-swapping */
  				{
***************
*** 265,270 ****
--- 273,279 ----
  					query_buf = swap_buf;
  				}
  				resetPQExpBuffer(query_buf);
+ 				psql_reset_substitution_fault_flag(scan_state);
  
  				added_nl_pos = -1;
  				/* we need not do psql_scan_reset() here */
*** ./src/bin/psql/psqlscan.h.orig	2010-01-02 17:57:59.000000000 +0100
--- ./src/bin/psql/psqlscan.h	2010-01-25 10:47:39.177494376 +0100
***************
*** 61,64 ****
--- 61,67 ----
  
  extern void psql_scan_slash_command_end(PsqlScanState state);
  
+ bool psql_scan_is_valid(PsqlScanState scan_state);
+ void psql_reset_substitution_fault_flag(PsqlScanState scan_state);
+ 
  #endif   /* PSQLSCAN_H */
*** ./src/bin/psql/psqlscan.l.orig	2010-01-02 17:57:59.000000000 +0100
--- ./src/bin/psql/psqlscan.l	2010-01-25 11:40:08.458538821 +0100
***************
*** 93,98 ****
--- 93,99 ----
  	int			paren_depth;	/* depth of nesting in parentheses */
  	int			xcdepth;		/* depth of nesting in slash-star comments */
  	char	   *dolqstart;		/* current $foo$ quote start string */
+ 	bool			substitution_fault;  /* false, when all variable expansion are successful */
  } PsqlScanStateData;
  
  static PsqlScanState cur_state;	/* current state while active */
***************
*** 119,124 ****
--- 120,131 ----
  static void emit(const char *txt, int len);
  static bool is_utf16_surrogate_first(uint32 c);
  
+ static void appendLiteral(PGconn *conn, PsqlScanState scan_state, 
+ 							    PQExpBuffer buf, const char *str);
+ 							    
+ static void appendIdentifier(PGconn *conn, PsqlScanState scan_state,
+ 							    PQExpBuffer buf, const char *str);
+ 
  #define ECHO emit(yytext, yyleng)
  
  %}
***************
*** 707,712 ****
--- 714,783 ----
  					}
  				}
  
+ 				
+ :'[A-Za-z0-9_]+'	{
+ 					/* 
+ 					* Possible psql variable substitution with 
+ 					* literal escaping.
+ 					*/
+ 					const char *value;
+ 				     
+ 					yytext[yyleng - 1] = '\0';
+ 					value = GetVariable(pset.vars, yytext + 2);
+ 					
+ 					if (value)
+ 					{
+ 						/* It is a known variable, perform substitution */
+ 						PQExpBufferData   buf;
+ 						
+ 						initPQExpBuffer(&buf);
+ 						appendLiteral(pset.db, cur_state, &buf, value);
+ 						push_new_buffer(buf.data);
+ 						termPQExpBuffer(&buf);
+ 						
+ 						/* yy_scan_string already made buffer active */
+ 					}
+ 					else
+ 					{
+ 						/*
+ 						 * if the variable doesn't exist we'll copy the
+ 						 * string as is
+ 						 */
+ 						ECHO;
+ 					}
+ 				}
+ 
+ :\"[A-Za-z0-9_]+\"	{
+ 					/* 
+ 					* Possible psql variable substitution with 
+ 					* identifier escaping.
+ 					*/
+ 					const char *value;
+ 				     
+ 					yytext[yyleng - 1] = '\0';
+ 					value = GetVariable(pset.vars, yytext + 2);
+ 					
+ 					if (value)
+ 					{
+ 						/* It is a known variable, perform substitution */
+ 						PQExpBufferData   buf;
+ 						
+ 						initPQExpBuffer(&buf);
+ 						appendIdentifier(pset.db, cur_state, &buf, value);
+ 						push_new_buffer(buf.data);
+ 						termPQExpBuffer(&buf);
+ 						/* yy_scan_string already made buffer active */
+ 					}
+ 					else
+ 					{
+ 						/*
+ 						 * if the variable doesn't exist we'll copy the
+ 						 * string as is
+ 						 */
+ 						ECHO;
+ 					}
+ 				}
+ 
  	/*
  	 * Back to backend-compatible rules.
  	 */
***************
*** 927,932 ****
--- 998,1055 ----
  					return LEXRES_OK;
  				}
  
+ :'[A-Za-z0-9_]+'	{
+ 					/* Possible psql variable substitution with literal escaping */
+ 					if (option_type == OT_VERBATIM)
+ 						ECHO;
+ 					else
+ 					{
+ 						const char *value;
+ 						
+ 						yytext[yyleng - 1] = '\0';
+ 						value = GetVariable(pset.vars, yytext + 2);
+ 
+ 						/*
+ 						 * The variable value is just emitted without any
+ 						 * further examination.  This is consistent with the
+ 						 * pre-8.0 code behavior, if not with the way that
+ 						 * variables are handled outside backslash commands.
+ 						 */
+ 						if (value)
+ 							appendLiteral(pset.db, cur_state, output_buf, value);
+ 					}
+ 
+ 					*option_quote = ':';
+ 
+ 					return LEXRES_OK;
+ 				}
+ 		
+ :\"[A-Za-z0-9_]+\"	{
+ 					/* Possible psql variable substitution with identifier escaping */
+ 					if (option_type == OT_VERBATIM)
+ 						ECHO;
+ 					else
+ 					{
+ 						const char *value;
+ 						
+ 						yytext[yyleng - 1] = '\0';
+ 						value = GetVariable(pset.vars, yytext + 2);
+ 
+ 						/*
+ 						 * The variable value is just emitted without any
+ 						 * further examination.  This is consistent with the
+ 						 * pre-8.0 code behavior, if not with the way that
+ 						 * variables are handled outside backslash commands.
+ 						 */
+ 						if (value)
+ 							appendIdentifier(pset.db, cur_state, output_buf, value);
+ 					}
+ 
+ 					*option_quote = ':';
+ 
+ 					return LEXRES_OK;
+ 				}
+ 
  "|"				{
  					ECHO;
  					if (option_type == OT_FILEPIPE)
***************
*** 1740,1742 ****
--- 1863,1950 ----
  {
  	return (c >= 0xD800 && c <= 0xDBFF);
  }
+ 
+ 
+ /*
+  * Convert a string value to an SQL Literal and append it to
+  * the given buffer. When escape function failed, then doesn't
+  * modify buffer and mark current scan as invalid. So potential
+  * dangerous value or broken values isn't appended and we are 
+  * able to recognize some problems later.
+  */
+ static void
+ appendLiteral(PGconn *conn, PsqlScanState scan_state, 
+ 				PQExpBuffer buf, const char *str)
+ {
+ 	char	*escaped_str;
+ 	size_t		len;
+ 	
+ 	len = strlen(str);
+ 	escaped_str = PQescapeLiteral(conn, str, len);
+ 	
+ 	if (escaped_str == NULL)
+ 	{
+ 		const char *error_message = PQerrorMessage(pset.db);
+ 
+ 		if (strlen(error_message))
+ 			psql_error("%s", error_message);
+ 		scan_state->substitution_fault = true;
+ 	}
+ 	else
+ 	{
+ 		/* only valid value is appended */
+ 		appendPQExpBufferStr(buf, escaped_str);
+ 		free(escaped_str);
+ 	}
+ }
+ 
+ /*
+  * Convert a string value to an SQL identifier and append it to
+  * the given buffer. When escape function failed, then doesn't
+  * modify buffer and mark current scan as invalid. So potential
+  * dangerous value or broken values isn't appended and we are 
+  * able to recognize some problems later.
+  */
+ static void
+ appendIdentifier(PGconn *conn, PsqlScanState scan_state,
+ 				PQExpBuffer buf, const char *str)
+ {
+ 	char	*escaped_str;
+ 	size_t		len;
+ 	
+ 	len = strlen(str);
+ 	escaped_str = PQescapeIdentifier(conn, str, len);
+ 	
+ 	if (escaped_str == NULL)
+ 	{
+ 		const char *error_message = PQerrorMessage(pset.db);
+ 
+ 		if (strlen(error_message))
+ 			psql_error("%s", error_message);
+ 		scan_state->substitution_fault = true;
+ 	}
+ 	else
+ 	{
+ 		/* only valid value is appended */
+ 		appendPQExpBufferStr(buf, escaped_str);
+ 		free(escaped_str);
+ 	}
+ }
+ 
+ /*
+  * Return true when there are no substitution fault
+  */
+ bool
+ psql_scan_is_valid(PsqlScanState scan_state)
+ {
+ 	return !scan_state->substitution_fault;
+ }
+ 
+ /*
+  * Reset substitution fault flag
+  */
+ void
+ psql_reset_substitution_fault_flag(PsqlScanState scan_state)
+ {
+ 	scan_state->substitution_fault = false;
+ }
