Dimitri Fontaine <dimi...@2ndquadrant.fr> writes:
> That's very great news. I'm left with moving the bulk of the code away
> from genfile.c and into postgres.c, and have the former be a user
> callable shell around the later, I suppose. Right?

Here it is, looks much better this way.

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

*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 13895,13900 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
--- 13895,13907 ----
         <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>
***************
*** 13933,13938 **** SELECT (pg_stat_file('filename')).modification;
--- 13940,13954 ----
  </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
***************
*** 13955,13960 **** SELECT (pg_stat_file('filename')).modification;
--- 13971,13977 ----
         <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/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 73,79 ****
  #include "utils/snapmgr.h"
  #include "mb/pg_wchar.h"
  
- 
  extern char *optarg;
  extern int	optind;
  
--- 73,78 ----
***************
*** 1139,1144 **** exec_simple_query(const char *query_string)
--- 1138,1277 ----
  }
  
  /*
+  * exec_multiple_queries
+  *
+  * Execute all queries found in the given query_string. Main use is
+  * pg_execute_from_file() function in backend/utils/adt/genfile.c
+  *
+  * Main differences from exec_simple_query are:
+  * - don't override unnamed portal
+  * - don't start nor finish a transaction
+  * - don't set stats or tracing markers
+  */
+ void
+ exec_multiple_queries(const char *portal_name, const char *query_string)
+ {
+ 	CommandDest dest = DestNone;
+ 	MemoryContext oldcontext;
+ 	List	   *parsetree_list;
+ 	ListCell   *parsetree_item;
+ 	bool		save_log_statement_stats = log_statement_stats;
+ 	bool		was_logged = false;
+ 	bool		isTopLevel;
+ 	char		msec_str[32];
+ 
+ 	oldcontext = MemoryContextSwitchTo(MessageContext);
+ 	parsetree_list = pg_parse_query(query_string);
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	isTopLevel = false;
+ 
+ 	foreach(parsetree_item, parsetree_list)
+ 	{
+ 		Node	   *parsetree = (Node *) lfirst(parsetree_item);
+ 		bool		snapshot_set = false;
+ 		const char *commandTag;
+ 		char		completionTag[COMPLETION_TAG_BUFSIZE];
+ 		List	   *querytree_list,
+ 				   *plantree_list;
+ 		Portal		portal;
+ 		DestReceiver *receiver;
+ 		int16		format = 0; /* TEXT */
+ 
+ 		commandTag = CreateCommandTag(parsetree);
+ 
+ 		/* If we got a cancel signal in parsing or prior command, quit */
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		/*
+ 		 * Set up a snapshot if parse analysis/planning will need one.
+ 		 */
+ 		if (analyze_requires_snapshot(parsetree))
+ 		{
+ 			PushActiveSnapshot(GetTransactionSnapshot());
+ 			snapshot_set = true;
+ 		}
+ 
+ 		/*
+ 		 * OK to analyze, rewrite, and plan this query.
+ 		 *
+ 		 * Switch to appropriate context for constructing querytrees (again,
+ 		 * these must outlive the execution context).
+ 		 */
+ 		oldcontext = MemoryContextSwitchTo(MessageContext);
+ 
+ 		querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
+ 												NULL, 0);
+ 
+ 		plantree_list = pg_plan_queries(querytree_list, 0, NULL);
+ 
+ 		/* Done with the snapshot used for parsing/planning */
+ 		if (snapshot_set)
+ 			PopActiveSnapshot();
+ 
+ 		/* If we got a cancel signal in analysis or planning, quit */
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		/*
+ 		 * Create a portal to run the query or queries in. If there already
+ 		 * is one with given portal_name, silently drop it.
+ 		 */
+ 		portal = CreatePortal(portal_name, true, true);
+ 		/* Don't display the portal in pg_cursors */
+ 		portal->visible = false;
+ 
+ 		/*
+ 		 * We don't have to copy anything into the portal, because everything
+ 		 * we are passing here is in MessageContext, which will outlive the
+ 		 * portal anyway.
+ 		 */
+ 		PortalDefineQuery(portal,
+ 						  NULL,
+ 						  query_string,
+ 						  commandTag,
+ 						  plantree_list,
+ 						  NULL);
+ 
+ 		/*
+ 		 * Start the portal.  No parameters here.
+ 		 */
+ 		PortalStart(portal, NULL, InvalidSnapshot);
+ 		PortalSetResultFormat(portal, 1, &format);
+ 
+ 		/*
+ 		 * Now we can create the destination receiver object.
+ 		 */
+ 		receiver = CreateDestReceiver(dest);
+ 		if (dest == DestRemote)
+ 			SetRemoteDestReceiverParams(receiver, portal);
+ 
+ 		/*
+ 		 * Switch back to transaction context for execution.
+ 		 */
+ 		MemoryContextSwitchTo(oldcontext);
+ 
+ 		/*
+ 		 * Run the portal to completion, and then drop it (and the receiver).
+ 		 */
+ 		(void) PortalRun(portal,
+ 						 FETCH_ALL,
+ 						 isTopLevel,
+ 						 receiver,
+ 						 receiver,
+ 						 completionTag);
+ 
+ 		(*receiver->rDestroy) (receiver);
+ 
+ 		PortalDrop(portal, false);
+ 
+ 		if (!IsA(parsetree, TransactionStmt) && lnext(parsetree_item) != NULL)
+ 		{
+ 			CommandCounterIncrement();
+ 		}
+ 	}
+ }
+ 
+ /*
   * exec_parse_message
   *
   * Execute a "Parse" protocol message.
*** 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
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 265,321 ----
  
  	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);
+ 
+ 	exec_multiple_queries(filename, query_string);
+ 	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/postgres.h
--- b/src/include/postgres.h
***************
*** 687,690 **** extern int ExceptionalCondition(const char *conditionName,
--- 687,693 ----
  					 const char *errorType,
  					 const char *fileName, int lineNumber);
  
+ extern void exec_multiple_queries(const char *portal_name,
+ 								  const char *query_string);
+ 
  #endif   /* POSTGRES_H */
*** 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