Alvaro Herrera <alvhe...@commandprompt.com> writes:
> The error message wording needs some work; maybe
>       errmsg("file \"%s\" could not be executed", filename)
[...]
> I happened to notice that there are several pieces of code that are
> calling SPI_connect and SPI_finish without checking the return value,
> notably the FTS code and xml.c.

Please find attached pg_execute_from_file.v4.patch with fixes. I've used
the usual error messages for SPI_connect() and finish this time.

Regards,
-- 
Dimitri Fontaine
http://2ndQuadrant.fr     PostgreSQL : Expertise, Formation et Support

*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 13897,13902 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
--- 13897,13909 ----
         <entry><type>record</type></entry>
         <entry>Return information about a file</entry>
        </row>
+       <row>
+        <entry>
+         <literal><function>pg_execute_from_file(<parameter>filename</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>void</type></entry>
+        <entry>Executes the <acronym>SQL</> commands contained in a file</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 13935,13940 **** SELECT (pg_stat_file('filename')).modification;
--- 13942,13956 ----
  </programlisting>
     </para>
  
+    <indexterm>
+     <primary>pg_execute_from_file</primary>
+    </indexterm>
+    <para>
+     <function>pg_execute_from_file</> makes the server
+     execute <acronym>SQL</> commands to be found in a file. This function is
+     reserved to superusers.
+    </para>
+ 
     <para>
      The functions shown in <xref linkend="functions-advisory-locks"> manage
      advisory locks.  For details about proper use of these functions, see
***************
*** 13957,13962 **** SELECT (pg_stat_file('filename')).modification;
--- 13973,13979 ----
         <entry><type>void</type></entry>
         <entry>Obtain exclusive advisory lock</entry>
        </row>
+ 
        <row>
         <entry>
          <literal><function>pg_advisory_lock(<parameter>key1</> <type>int</>, <parameter>key2</> <type>int</>)</function></literal>
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 7,12 ****
--- 7,13 ----
   * Copyright (c) 2004-2010, PostgreSQL Global Development Group
   *
   * Author: Andreas Pflug <pgad...@pse-consulting.de>
+  *         Dimitri Fontaine <dimi...@2ndquadrant.fr>
   *
   * IDENTIFICATION
   *	  src/backend/utils/adt/genfile.c
***************
*** 21,26 ****
--- 22,28 ----
  #include <dirent.h>
  
  #include "catalog/pg_type.h"
+ #include "executor/spi.h"
  #include "funcapi.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 266,340 ----
  
  	SRF_RETURN_DONE(funcctx);
  }
+ 
+ /*
+  * Read a file then execute the SQL commands it contains.
+  */
+ Datum
+ pg_execute_from_file(PG_FUNCTION_ARGS)
+ {
+ 	text	   *filename_t = PG_GETARG_TEXT_P(0);
+ 	char	   *filename;
+ 	FILE       *file;
+ 	int64       fsize = -1, nbytes;
+ 	struct stat fst;
+ 	char       *query_string = NULL;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to get file information"))));
+ 
+ 	/*
+ 	 * Only superuser can call pg_execute_from_file, and CREATE EXTENSION
+ 	 * uses that too. Don't double check the PATH. Also note that
+ 	 * extension's install files are not in $PGDATA but `pg_config
+ 	 * --sharedir`.
+ 	 */
+ 	filename = text_to_cstring(filename_t);
+ 
+ 	if (stat(filename, &fst) < 0)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not stat file \"%s\": %m", filename)));
+ 
+ 	fsize = Int64GetDatum((int64) fst.st_size);
+ 
+ 	if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not open file \"%s\" for reading: %m",
+ 						filename)));
+ 
+ 	if (ferror(file))
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not read file \"%s\": %m", filename)));
+ 
+ 	query_string = (char *)palloc((fsize+1)*sizeof(char));
+ 	memset(query_string, 0, fsize+1);
+ 	nbytes = fread(query_string, 1, (size_t) fsize, file);
+ 	pg_verifymbstr(query_string, nbytes, false);
+ 	FreeFile(file);
+ 
+ 	/*
+ 	 * We abuse some internal knowledge from spi.h here. As we don't know
+ 	 * which queries are going to get executed, we don't know what to expect
+ 	 * as an OK return code from SPI_execute().  We assume that
+ 	 * SPI_OK_CONNECT, SPI_OK_FINISH and SPI_OK_FETCH are quite improbable,
+ 	 * though, and the errors are negatives.  So a valid return code is
+ 	 * considered to be SPI_OK_UTILITY or anything from there.
+ 	 */
+ 	if (SPI_connect() != SPI_OK_CONNECT)
+ 		elog(ERROR, "SPI_connect failed");
+ 
+ 	if (SPI_execute(query_string, false, 0) < SPI_OK_UTILITY)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATA_EXCEPTION),
+ 				 errmsg("File '%s' could not be executed", filename)));
+ 
+ 	if (SPI_finish() != SPI_OK_FINISH)
+ 		elog(ERROR, "SPI_finish failed");
+ 
+ 	PG_RETURN_VOID();
+ }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3386,3399 **** DESCR("reload configuration files");
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file		PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file		PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir			PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
--- 3386,3401 ----
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file			PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir				PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep				PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
+ DATA(insert OID = 3627 ( pg_execute_from_file	PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_from_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 442,447 **** extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
--- 442,448 ----
  extern Datum pg_stat_file(PG_FUNCTION_ARGS);
  extern Datum pg_read_file(PG_FUNCTION_ARGS);
  extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_from_file(PG_FUNCTION_ARGS);
  
  /* misc.c */
  extern Datum current_database(PG_FUNCTION_ARGS);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to