diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 21f1ddf..b43e46e 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 1840,1846 ****
          </indexterm>
          <literal><function>replace(<parameter>string</parameter> <type>text</type>,
          <parameter>from</parameter> <type>text</type>,
!         <parameter>to</parameter> <type>text</type>)</function></literal>
         </entry>
         <entry><type>text</type></entry>
         <entry>Replace all occurrences in <parameter>string</parameter> of substring
--- 1840,1846 ----
          </indexterm>
          <literal><function>replace(<parameter>string</parameter> <type>text</type>,
          <parameter>from</parameter> <type>text</type>,
!         <parameter>to</parameter> <type>text</type> [, ...])</function></literal>
         </entry>
         <entry><type>text</type></entry>
         <entry>Replace all occurrences in <parameter>string</parameter> of substring
*************** postgres=# SELECT * FROM pg_xlogfile_nam
*** 14449,14466 ****
        </row>
        <row>
         <entry>
!         <literal><function>pg_read_file(<parameter>filename</> <type>text</>, <parameter>offset</> <type>bigint</>, <parameter>length</> <type>bigint</>)</function></literal>
         </entry>
         <entry><type>text</type></entry>
         <entry>Return the contents of a text file</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_stat_file(<parameter>filename</> <type>text</>)</function></literal>
         </entry>
         <entry><type>record</type></entry>
         <entry>Return information about a file</entry>
        </row>
       </tbody>
      </tgroup>
     </table>
--- 14449,14494 ----
        </row>
        <row>
         <entry>
!         <literal><function>pg_read_file(<parameter>filename</> <type>text</>, <parameter>offset</> <type>bigint</> [, <parameter>length</> <type>bigint</>])</function></literal>
         </entry>
         <entry><type>text</type></entry>
         <entry>Return the contents of a text file</entry>
        </row>
        <row>
         <entry>
+         <literal><function>pg_read_binary_file(<parameter>filename</> <type>text</>, <parameter>offset</> <type>bigint</> [, <parameter>length</> <type>bigint</>])</function></literal>
+        </entry>
+        <entry><type>bytea</type></entry>
+        <entry>Return the contents of a file</entry>
+       </row>
+       <row>
+        <entry>
          <literal><function>pg_stat_file(<parameter>filename</> <type>text</>)</function></literal>
         </entry>
         <entry><type>record</type></entry>
         <entry>Return information about a file</entry>
        </row>
+       <row>
+        <entry>
+         <literal><function>pg_execute_sql_string(<parameter>sql</> <type>text</>
+ [, <parameter>variable</parameter> <type>text</type>, <parameter>value</parameter> <type>text</type>
+ [, ...] ]) )</function></literal>
+        </entry>
+        <entry><type>void</type></entry>
+        <entry>Execute the given string as <acronym>SQL</> commands, replacing placeholders, if any.</entry>
+       </row>
+       <row>
+        <entry>
+         <literal><function>pg_execute_sql_file(<parameter>filename</> <type>text</>
+ [ [, <parameter>encoding</parameter> <type>name</type>]
+   [, <parameter>variable</parameter> <type>text</type>, <parameter>value</parameter> <type>text</type>
+   [, ...] ] ]) )</function></literal>
+        </entry>
+        <entry><type>void</type></entry>
+        <entry>Execute contents of the given file as <acronym>SQL</> commands,
+        expected in either database encoding or given encoding, and replacing
+        given placeholders, if any.</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
*************** postgres=# SELECT * FROM pg_xlogfile_nam
*** 14482,14487 ****
--- 14510,14539 ----
      at the given <parameter>offset</>, returning at most <parameter>length</>
      bytes (less if the end of file is reached first).  If <parameter>offset</>
      is negative, it is relative to the end of the file.
+     When the parameter <parameter>length</> is omitted,
+     <function>pg_read_file</> reads until the end of the file.
+     The part of a file must be a valid text in the server encoding.
+    </para>
+ 
+    <indexterm>
+     <primary>pg_read_binary_file</primary>
+    </indexterm>
+    <para>
+     <function>pg_read_binary_file</> returns part of a file as like as
+     <function>pg_read_file</>, but the result is a bytea value.
+ <programlisting>
+ SELECT convert_from(pg_read_binary_file('postgresql.conf', -69), 'utf8');
+                                  convert_from                                  
+ -------------------------------------------------------------------------------
+  #custom_variable_classes = ''           # list of custom variable class names+
+  
+ (1 row)
+ </programlisting>
+    </para>
+    <para>
+     When the parameter <parameter>length</> is
+     omited, <function>pg_read_binary_file</> reads until the end of the
+     file.
     </para>
  
     <indexterm>
*************** SELECT (pg_stat_file('filename')).modifi
*** 14499,14504 ****
--- 14551,14598 ----
  </programlisting>
     </para>
  
+    <indexterm>
+     <primary>pg_execute_sql_string</primary>
+    </indexterm>
+    <para>
+     <function>pg_execute_sql_string</> makes the server execute the given string
+     as <acronym>SQL</> commands.
+     The script may contain placeholders that will be replaced by the optional
+     parameters, that are pairs of variable names and values. Here's an example:
+ <programlisting>
+ SELECT pg_execute_sql_string('CREATE SCHEMA @schema@;', '@schema@', 'utils');
+  pg_execute_sql_string 
+ -----------------------
+  
+ (1 row)
+ 
+ SELECT pg_execute_sql_string('CREATE SCHEMA s;', 's', 'bar');
+  pg_execute_sql_string 
+ -----------------------
+  
+ (1 row)
+ 
+ SELECT oid, * from pg_namespace where nspname in ('utils', 'bar');
+   oid  | nspname | nspowner | nspacl 
+ -------+---------+----------+--------
+  16387 | utils   |       10 | 
+  16388 | bar     |       10 | 
+ (2 rows)
+ </programlisting>
+    </para>
+ 
+    <indexterm>
+     <primary>pg_execute_sql_file</primary>
+    </indexterm>
+    <para>
+     <function>pg_execute_sql_file</> makes the server execute contents in a file
+     as <acronym>SQL</> commands. You can give an encoding name of the file to the
+     function. If omitted, the server encoding is used.
+     The script may contain placeholders that will be replaced by the optional
+     parameters, that are pairs of variable names and values.
+     Use of the function is restricted 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
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index e8a36ed..3a327b6 100644
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 21,31 ****
--- 21,33 ----
  #include <dirent.h>
  
  #include "catalog/pg_type.h"
+ #include "executor/spi.h"
  #include "funcapi.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "postmaster/syslogger.h"
  #include "storage/fd.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/memutils.h"
  #include "utils/timestamp.h"
*************** convert_and_check_filename(text *arg)
*** 80,104 ****
  
  
  /*
!  * Read a section of a file, returning it as text
   */
! Datum
! pg_read_file(PG_FUNCTION_ARGS)
  {
! 	text	   *filename_t = PG_GETARG_TEXT_P(0);
! 	int64		seek_offset = PG_GETARG_INT64(1);
! 	int64		bytes_to_read = PG_GETARG_INT64(2);
! 	char	   *buf;
! 	size_t		nbytes;
! 	FILE	   *file;
! 	char	   *filename;
  
  	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 (errmsg("must be superuser to read files"))));
  
! 	filename = convert_and_check_filename(filename_t);
  
  	if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
  		ereport(ERROR,
--- 82,126 ----
  
  
  /*
!  * Read a section of a file, returning it as bytea
!  *
!  * Will read all the file if given a nagative bytes_to_read, and a negative
!  * offset is applied from the end of the file (SEEK_END)
   */
! static bytea *
! read_binary_file(const char *filename, int64 offset, int64 bytes_to_read)
  {
! 	FILE       *file;
! 	bytea      *buf;
! 	int64       nbytes;
  
+ 	/* Only superuser can read files. */
  	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 (errmsg("must be superuser to read files"))));
  
! 	if (bytes_to_read >= 0)
! 		nbytes = bytes_to_read;
! 	else if (offset < 0)
! 		nbytes = -offset;
! 	else
! 	{
! 		struct stat fst;
! 
! 		if (stat(filename, &fst) < 0)
! 			ereport(ERROR,
! 					(errcode_for_file_access(),
! 					 errmsg("could not stat file \"%s\": %m", filename)));
! 
! 		nbytes = fst.st_size;
! 	}
! 
! 	/* not sure why anyone thought that int64 length was a good idea */
! 	if (nbytes > (MaxAllocSize - VARHDRSZ))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 				 errmsg("requested length too large")));
  
  	if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
  		ereport(ERROR,
*************** pg_read_file(PG_FUNCTION_ARGS)
*** 106,146 ****
  				 errmsg("could not open file \"%s\" for reading: %m",
  						filename)));
  
! 	if (fseeko(file, (off_t) seek_offset,
! 			   (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
  		ereport(ERROR,
  				(errcode_for_file_access(),
  				 errmsg("could not seek in file \"%s\": %m", filename)));
  
  	if (bytes_to_read < 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				 errmsg("requested length cannot be negative")));
  
! 	/* not sure why anyone thought that int64 length was a good idea */
! 	if (bytes_to_read > (MaxAllocSize - VARHDRSZ))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 				 errmsg("requested length too large")));
  
! 	buf = palloc((Size) bytes_to_read + VARHDRSZ);
  
! 	nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file);
  
! 	if (ferror(file))
! 		ereport(ERROR,
! 				(errcode_for_file_access(),
! 				 errmsg("could not read file \"%s\": %m", filename)));
  
! 	/* Make sure the input is valid */
! 	pg_verifymbstr(VARDATA(buf), nbytes, false);
  
! 	SET_VARSIZE(buf, nbytes + VARHDRSZ);
  
! 	FreeFile(file);
! 	pfree(filename);
  
! 	PG_RETURN_TEXT_P(buf);
  }
  
  /*
--- 128,228 ----
  				 errmsg("could not open file \"%s\" for reading: %m",
  						filename)));
  
! 	if (fseeko(file, (off_t) offset,
! 			   (offset >= 0) ? SEEK_SET : SEEK_END) != 0)
  		ereport(ERROR,
  				(errcode_for_file_access(),
  				 errmsg("could not seek in file \"%s\": %m", filename)));
  
+ 	buf = (bytea *) palloc((Size) nbytes + VARHDRSZ);
+ 	nbytes = fread(VARDATA(buf), 1, (size_t) nbytes, file);
+ 
+ 	if (ferror(file))
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not read file \"%s\": %m", filename)));
+ 
+ 	FreeFile(file);
+ 
+ 	SET_VARSIZE(buf, nbytes + VARHDRSZ);
+ 
+ 	return buf;
+ }
+ 
+ /*
+  * In addition to read_binary_file, verify the contents are encoded in the
+  * database encoding.
+  */
+ static text *
+ read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
+ {
+ 	bytea *content = read_binary_file(filename, seek_offset, bytes_to_read);
+ 
+ 	/* Make sure the input is valid */
+ 	pg_verifymbstr(VARDATA(content), VARSIZE(content) - VARHDRSZ, false);
+ 
+ 	/* Abuse knowledge that we're bytea and text are both varlena */
+ 	return (text *) content;
+ }
+ 
+ /*
+  * Read a section of a file, returning its content as text
+  */
+ Datum
+ pg_read_file(PG_FUNCTION_ARGS)
+ {
+ 	char       *filename = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ 	int64		seek_offset = PG_GETARG_INT64(1);
+ 	int64		bytes_to_read = PG_GETARG_INT64(2);
+ 
  	if (bytes_to_read < 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				 errmsg("requested length cannot be negative")));
  
! 	PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, bytes_to_read));
! }
  
! /*
!  * Read till the end of a file, returning its content as text
!  */
! Datum
! pg_read_whole_file(PG_FUNCTION_ARGS)
! {
! 	char       *filename = text_to_cstring(PG_GETARG_TEXT_PP(0));
! 	int64		seek_offset = PG_GETARG_INT64(1);
  
! 	PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, -1));
! }
  
! /*
!  * Read a section of a file, returning its content as bytea
!  */
! Datum
! pg_read_binary_file(PG_FUNCTION_ARGS)
! {
! 	char       *filename = text_to_cstring(PG_GETARG_TEXT_PP(0));
! 	int64		seek_offset = PG_GETARG_INT64(1);
! 	int64		bytes_to_read = PG_GETARG_INT64(2);
  
! 	if (bytes_to_read < 0)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 				 errmsg("requested length cannot be negative")));
  
! 	PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, bytes_to_read));
! }
  
! /*
!  * Read till the end of a file, returning its content as bytea
!  */
! Datum
! pg_read_whole_binary_file(PG_FUNCTION_ARGS)
! {
! 	char       *filename = text_to_cstring(PG_GETARG_TEXT_PP(0));
! 	int64		seek_offset = PG_GETARG_INT64(1);
  
! 	PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, -1));
  }
  
  /*
*************** pg_read_file(PG_FUNCTION_ARGS)
*** 149,155 ****
  Datum
  pg_stat_file(PG_FUNCTION_ARGS)
  {
! 	text	   *filename_t = PG_GETARG_TEXT_P(0);
  	char	   *filename;
  	struct stat fst;
  	Datum		values[6];
--- 231,237 ----
  Datum
  pg_stat_file(PG_FUNCTION_ARGS)
  {
! 	text	   *filename_t = PG_GETARG_TEXT_PP(0);
  	char	   *filename;
  	struct stat fst;
  	Datum		values[6];
*************** pg_ls_dir(PG_FUNCTION_ARGS)
*** 234,240 ****
  		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
  		fctx = palloc(sizeof(directory_fctx));
! 		fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
  
  		fctx->dirdesc = AllocateDir(fctx->location);
  
--- 316,322 ----
  		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
  		fctx = palloc(sizeof(directory_fctx));
! 		fctx->location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
  
  		fctx->dirdesc = AllocateDir(fctx->location);
  
*************** pg_ls_dir(PG_FUNCTION_ARGS)
*** 264,266 ****
--- 346,492 ----
  
  	SRF_RETURN_DONE(funcctx);
  }
+ 
+ /*
+  * Replace placeholders in the sql text and execute it.
+  */
+ static void
+ execute_sql_string(const char *filename, text *sql, ArrayType *placeholders)
+ {
+ 	/* replace placeholders */
+ 	if (placeholders != NULL)
+ 		sql = DatumGetTextP(DirectFunctionCall2(replace_text_variadic,
+ 								PointerGetDatum(sql),
+ 								PointerGetDatum(placeholders)));
+ 
+ 	/*
+ 	 * 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(text_to_cstring(sql), false, 0) < SPI_OK_UTILITY)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATA_EXCEPTION),
+ 				 (filename != NULL
+ 				  ? errmsg("could not execute sql file: '%s'", filename)
+ 				  : errmsg("could not execute sql string"))));
+ 
+ 	if (SPI_finish() != SPI_OK_FINISH)
+ 		elog(ERROR, "SPI_finish failed");
+ }
+ 
+ static void
+ execute_sql_file(const char *filename,
+ 				 const char *encoding_name,
+ 				 ArrayType *placeholders)
+ {
+ 	int			src_encoding;
+ 	int			dest_encoding = GetDatabaseEncoding();
+ 	bytea      *content;
+ 	text	   *sql;
+ 	const char *src_str;
+ 	char	   *dest_str;
+ 	int			len;
+ 
+ 	/* use database encoding if not given */
+ 	if (encoding_name == NULL)
+ 		src_encoding = dest_encoding;
+ 	else
+ 		src_encoding = pg_char_to_encoding(encoding_name);
+ 
+ 	if (src_encoding < 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("invalid source encoding name \"%s\"",
+ 						encoding_name)));
+ 
+ 	/* superuser checks will be done in reading files */
+ 	content = read_binary_file(filename, 0, -1);
+ 
+ 	/* make sure that source string is valid */
+ 	len = VARSIZE_ANY_EXHDR(content);
+ 	src_str = VARDATA_ANY(content);
+ 	pg_verify_mbstr_len(src_encoding, src_str, len, false);
+ 
+ 	/* convert the encoding to the database encoding */
+ 	dest_str = (char *) pg_do_encoding_conversion(
+ 				(unsigned char *) src_str, len, src_encoding, dest_encoding);
+ 	if (dest_str != src_str)
+ 		sql = cstring_to_text(dest_str);
+ 	else
+ 		sql = (text *) content;
+ 
+ 	execute_sql_string(filename, sql, placeholders);
+ }
+ 
+ /*
+  * Execute SQL string.
+  */
+ Datum
+ pg_execute_sql_string(PG_FUNCTION_ARGS)
+ {
+ 	text	   *sql = PG_GETARG_TEXT_PP(0);
+ 
+ 	execute_sql_string(NULL, sql, NULL);
+ 	PG_RETURN_VOID();
+ }
+ 
+ /*
+  * Execute SQL string containing placeholders.
+  */
+ Datum
+ pg_execute_sql_string_with_placeholders(PG_FUNCTION_ARGS)
+ {
+ 	text	   *sql = PG_GETARG_TEXT_PP(0);
+ 	ArrayType  *placeholders = PG_GETARG_ARRAYTYPE_P(1);
+ 
+ 	execute_sql_string(NULL, sql, placeholders);
+ 	PG_RETURN_VOID();
+ }
+ 
+ /*
+  * Read a file, and execute the contents as SQL commands.
+  */
+ Datum
+ pg_execute_sql_file(PG_FUNCTION_ARGS)
+ {
+ 	char       *filename = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ 
+ 	execute_sql_file(filename, NULL, NULL);
+ 	PG_RETURN_VOID();
+ }
+ 
+ /*
+  * In addition to pg_execute_sql_file, convert the encoding of the contents
+  * to the database encoding.
+  */
+ Datum
+ pg_convert_and_execute_sql_file(PG_FUNCTION_ARGS)
+ {
+ 	char       *filename = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ 	char	   *encoding_name = NameStr(*PG_GETARG_NAME(1));
+ 
+ 	execute_sql_file(filename, encoding_name, NULL);
+ 	PG_RETURN_VOID();
+ }
+ 
+ /*
+  * In addition to pg_convert_and_execute_sql_file, replace placeholder
+  * variables to given values in the contents.
+  */
+ Datum
+ pg_execute_sql_file_with_placeholders(PG_FUNCTION_ARGS)
+ {
+ 	char       *filename = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ 	char	   *encoding_name = NameStr(*PG_GETARG_NAME(1));
+ 	ArrayType  *placeholders = PG_GETARG_ARRAYTYPE_P(2);
+ 
+ 	execute_sql_file(filename, encoding_name, placeholders);
+ 	PG_RETURN_VOID();
+ }
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index ee83e2f..33683e9 100644
*** a/src/backend/utils/adt/varlena.c
--- b/src/backend/utils/adt/varlena.c
***************
*** 24,29 ****
--- 24,30 ----
  #include "miscadmin.h"
  #include "parser/scansup.h"
  #include "regex/regex.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/bytea.h"
  #include "utils/lsyscache.h"
*************** replace_text(PG_FUNCTION_ARGS)
*** 2604,2609 ****
--- 2605,2640 ----
  }
  
  /*
+  * Given an array of repeated {FROM, TO} pairs, replaces each FROM value
+  * in the given string to the corresponding TO value.
+  */
+ Datum
+ replace_text_variadic(PG_FUNCTION_ARGS)
+ {
+ 	Datum		str = PG_GETARG_DATUM(0);
+ 	ArrayType  *arr_pairs = PG_GETARG_ARRAYTYPE_P(1);
+ 	Datum	   *pairs;
+ 	int			len;
+ 	int			i;
+ 
+ 	Assert(ARR_ELEMTYPE(arr_pairs) == TEXTOID);
+ 
+ 	deconstruct_array(arr_pairs, TEXTOID, -1, false, 'i',
+ 					  &pairs, NULL, &len);
+ 
+ 	if (len % 2 != 0)
+ 		ereport(ERROR,
+ 				(errmsg("number of replacement parameters (%d) must be even",
+ 				 len)));
+ 
+ 	/* call replace_text over each pair of arguments */
+ 	for (i = 0; i < len; i += 2)
+ 		str = DirectFunctionCall3(replace_text, str, pairs[i], pairs[i + 1]);
+ 
+ 	PG_RETURN_DATUM(str);
+ }
+ 
+ /*
   * check_replace_text_has_escape_char
   *
   * check whether replace_text contains escape char.
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 611adef..ff640af 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID =  937 (  substring    P
*** 2255,2260 ****
--- 2255,2262 ----
  DESCR("return portion of string");
  DATA(insert OID =  2087 ( replace	   PGNSP PGUID 12 1 0 0 f f f t f i 3 0 25 "25 25 25" _null_ _null_ _null_ _null_	replace_text _null_ _null_ _null_ ));
  DESCR("replace all occurrences in string of old_substr with new_substr");
+ DATA(insert OID =  3931 ( replace	   PGNSP PGUID 12 1 0 25 f f f t f v 2 0 25 "25 25" "{25,25}" "{i,v}" _null_ _null_ replace_text_variadic _null_ _null_ _null_ ));
+ DESCR("replace all occurrences in string of old_substr with new_substr");
  DATA(insert OID =  2284 ( regexp_replace	   PGNSP PGUID 12 1 0 0 f f f t f i 3 0 25 "25 25 25" _null_ _null_ _null_ _null_	textregexreplace_noopt _null_ _null_ _null_ ));
  DESCR("replace text using regexp");
  DATA(insert OID =  2285 ( regexp_replace	   PGNSP PGUID 12 1 0 0 f f f t f i 4 0 25 "25 25 25 25" _null_ _null_ _null_ _null_ textregexreplace _null_ _null_ _null_ ));
*************** DESCR("reload configuration files");
*** 3399,3412 ****
  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");
--- 3401,3430 ----
  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 = 3935 ( pg_read_file			PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 20" _null_ _null_ _null_ _null_ pg_read_whole_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 = 3930 ( pg_read_binary_file	PGNSP PGUID 12 1 0 0 f f f t f v 3 0 17 "25 20 20" _null_ _null_ _null_ _null_ pg_read_binary_file _null_ _null_ _null_ ));
+ DESCR("read bytea from a file");
+ DATA(insert OID = 3936 ( pg_read_binary_file	PGNSP PGUID 12 1 0 0 f f f t f v 2 0 17 "25 20" _null_ _null_ _null_ _null_ pg_read_whole_binary_file _null_ _null_ _null_ ));
+ DESCR("read bytea from a file");
+ DATA(insert OID = 3932 ( pg_execute_sql_string	PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_sql_string _null_ _null_ _null_ ));
+ DESCR("execute queries read from a string");
+ DATA(insert OID = 3933 ( pg_execute_sql_string	PGNSP PGUID 12 1 0 25 f f f t f v 2 0 2278 "25 25" "{25,25}" "{i,v}" _null_ _null_ pg_execute_sql_string_with_placeholders _null_ _null_ _null_ ));
+ DESCR("execute queries read from a string");
+ DATA(insert OID = 3927 ( pg_execute_sql_file	PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_sql_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
+ DATA(insert OID = 3934 ( pg_execute_sql_file	PGNSP PGUID 12 1 0 0 f f f t f v 2 0 2278 "25 19" _null_ _null_ _null_ _null_ pg_convert_and_execute_sql_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
+ DATA(insert OID = 3928 ( pg_execute_sql_file	PGNSP PGUID 12 1 0 25 f f f t f v 3 0 2278 "25 19 25" "{25,19,25}" "{i,i,v}" _null_ _null_ pg_execute_sql_file_with_placeholders _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");
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index a2fb749..0c45546 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum pg_relation_filepath(PG_FUN
*** 442,448 ****
--- 442,457 ----
  /* genfile.c */
  extern Datum pg_stat_file(PG_FUNCTION_ARGS);
  extern Datum pg_read_file(PG_FUNCTION_ARGS);
+ extern Datum pg_read_whole_file(PG_FUNCTION_ARGS);
+ extern Datum pg_read_binary_file(PG_FUNCTION_ARGS);
+ extern Datum pg_read_whole_binary_file(PG_FUNCTION_ARGS);
  extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+ extern Datum replace_placeholders(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_sql_string(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_sql_string_with_placeholders(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_sql_file(PG_FUNCTION_ARGS);
+ extern Datum pg_convert_and_execute_sql_file(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_sql_file_with_placeholders (PG_FUNCTION_ARGS);
  
  /* misc.c */
  extern Datum current_database(PG_FUNCTION_ARGS);
*************** extern List *textToQualifiedNameList(tex
*** 716,721 ****
--- 725,731 ----
  extern bool SplitIdentifierString(char *rawstring, char separator,
  					  List **namelist);
  extern Datum replace_text(PG_FUNCTION_ARGS);
+ extern Datum replace_text_variadic(PG_FUNCTION_ARGS);
  extern text *replace_text_regexp(text *src_text, void *regexp,
  					text *replace_text, bool glob);
  extern Datum split_text(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index 1bd6772..8cd3242 100644
*** a/src/test/regress/expected/strings.out
--- b/src/test/regress/expected/strings.out
*************** SELECT replace('yabadoo', 'bad', '') AS 
*** 1227,1232 ****
--- 1227,1238 ----
   yaoo
  (1 row)
  
+ SELECT replace('yabadabadoo', 'ba', '123', 'a', 'X') AS "yX123dX123doo";
+  yX123dX123doo 
+ ---------------
+  yX123dX123doo
+ (1 row)
+ 
  --
  -- test split_part
  --
*************** SELECT encode(overlay(E'Th\\000omas'::by
*** 1603,1605 ****
--- 1609,1630 ----
   Th\000o\x02\x03
  (1 row)
  
+ --
+ -- test execute
+ --
+ SELECT pg_execute_sql_string($do$ DO $$BEGIN RAISE INFO 'foo'; END;$$ $do$);
+ INFO:  foo
+ CONTEXT:  SQL statement " DO $$BEGIN RAISE INFO 'foo'; END;$$ "
+  pg_execute_sql_string 
+ -----------------------
+  
+ (1 row)
+ 
+ SELECT pg_execute_sql_string($do$ DO $$BEGIN RAISE INFO 'foo'; END;$$ $do$, 'foo', 'bar');
+ INFO:  bar
+ CONTEXT:  SQL statement " DO $$BEGIN RAISE INFO 'bar'; END;$$ "
+  pg_execute_sql_string 
+ -----------------------
+  
+ (1 row)
+ 
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index 6ef4463..aa56739 100644
*** a/src/test/regress/sql/strings.sql
--- b/src/test/regress/sql/strings.sql
*************** SELECT replace('yabadabadoo', 'ba', '123
*** 430,435 ****
--- 430,437 ----
  
  SELECT replace('yabadoo', 'bad', '') AS "yaoo";
  
+ SELECT replace('yabadabadoo', 'ba', '123', 'a', 'X') AS "yX123dX123doo";
+ 
  --
  -- test split_part
  --
*************** SELECT btrim(E'\\000trim\\000'::bytea, '
*** 552,554 ****
--- 554,562 ----
  SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape');
  SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape');
  SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape');
+ 
+ --
+ -- test execute
+ --
+ SELECT pg_execute_sql_string($do$ DO $$BEGIN RAISE INFO 'foo'; END;$$ $do$);
+ SELECT pg_execute_sql_string($do$ DO $$BEGIN RAISE INFO 'foo'; END;$$ $do$, 'foo', 'bar');
