Dimitri Fontaine <[email protected]> writes:
> Itagaki Takahiro <[email protected]> writes:
>> My suggestion is to introduce pg_read_binary_file() function that can
>> read any files in the server, and make CREATE EXTENSION to use the
>> function. Of course, pg_execute_[sql|from]_file() can simplify queries
>
> It seems like all you're missing in the current patch is the encoding
> option in there. Exposing the internal functions is simple enough and
> solves it, but I much prefer the current simple-case user API. Do you
> want me to add a variant with an extra 'encoding' parameter?
Here's the result:
dim=# \df pg_exe*|replace_*|*binary*
List of functions
Schema | Name | Result data type | Argument data types
| Type
------------+-----------------------+------------------+---------------------------+--------
pg_catalog | pg_execute_sql_file | void | text
| normal
pg_catalog | pg_execute_sql_file | void | text, name
| normal
pg_catalog | pg_execute_sql_file | void | text, name, VARIADIC
text | normal
pg_catalog | pg_execute_sql_string | void | text
| normal
pg_catalog | pg_execute_sql_string | void | text, VARIADIC text
| normal
pg_catalog | pg_read_binary_file | bytea | text, bigint, bigint
| normal
pg_catalog | replace_placeholders | text | text, VARIADIC text
| normal
(7 rows)
I think that answers fine to your concerns, in that you can manipulate
the low-level functions in order to control each step, or you can use
the higher-level functions and just pass them the arguments you want.
Note that the "name" arguments here are the encoding.
The documentation of the pg_execute_sql_string() function should close
the gap noted by Joshua Tolley about how to format placeholder names,
because it gives examples both using '@schema@' and 's' as names.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 1853,1858 ****
--- 1853,1878 ----
<row>
<entry>
<indexterm>
+ <primary>replace_placeholders</primary>
+ </indexterm>
+ <literal><function>replace_placeholders(<parameter>string</parameter> <type>text</type>,
+ <parameter>from</parameter> <type>text</type>,
+ <parameter>to</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
+ <parameter>from</parameter> with substring <parameter>to</parameter>
+ </entry>
+ <entry><literal>replace('abcdefabcdef', 'cd', 'XX', 'ef', 'YY')</literal></entry>
+ <entry><literal>abXXYYabXXYY</literal></entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>reverse</primary>
</indexterm>
<literal><function>reverse(<parameter>str</parameter>)</function></literal>
***************
*** 14456,14466 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
--- 14476,14514 ----
</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>Executes the given <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>Executes the <acronym>SQL</> commands contained in a file,
+ expected in either database encoding or given encoding, and replacing
+ given placeholders, if any.</entry>
+ </row>
</tbody>
</tgroup>
</table>
***************
*** 14478,14487 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
<primary>pg_read_file</primary>
</indexterm>
<para>
! <function>pg_read_file</> returns part of a text file, starting
! 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.
</para>
<indexterm>
--- 14526,14555 ----
<primary>pg_read_file</primary>
</indexterm>
<para>
! <function>pg_read_file</> returns part of a text file, starting 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.
! </para>
!
! <indexterm>
! <primary>pg_read_binary_file</primary>
! </indexterm>
! <para>
! <function>pg_read_binary_file</> returns part of a file, starting 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. If <parameter>bytes_to_read</> is <literal>-1</>, the file is read
! until reaching its end.
! <programlisting>
! SELECT convert_from(pg_read_binary_file('postgresql.conf', -69, -1), 'utf8');
! convert_from
! -------------------------------------------------------------------------------
! #custom_variable_classes = '' # list of custom variable class names+
!
! (1 row)
! </programlisting>
</para>
<indexterm>
***************
*** 14499,14504 **** SELECT (pg_stat_file('filename')).modification;
--- 14567,14628 ----
</programlisting>
</para>
+ <indexterm>
+ <primary>pg_execute_sql_string</primary>
+ </indexterm>
+ <para>
+ <function>pg_execute_sql_string</> makes the server execute
+ given <acronym>SQL</> commands. This function is reserved to superusers.
+ </para>
+ <para>
+ The function accepts an optional list of placeholder variables as
+ arguments, and will replace any given placeholder name by its value,
+ found in the next parameter. 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 <acronym>SQL</> commands to be found in a file. This function is
+ reserved to superusers.
+ </para>
+ <para>
+ When the file is known to be encoded the same way as the database, you
+ can leave
+ the <parameter>encoding</parameter> <literal>NULL</>. Otherwise, you
+ need to give the file encoding and <productname>PostgreSQL</> will
+ convert the file content in the database encoding for you.
+ </para>
+ <para>
+ The script might contain placeholders that will be replaced by the
+ values given in the <literal>VARIADIC</literal> arguments, which must be
+ a pair of variable names and values. No specific formating is required
+ as far as placeholder names are concerned, so that you can follow your
+ own policies.
+ </para>
+
<para>
The functions shown in <xref linkend="functions-advisory-locks"> manage
advisory locks. For details about proper use of these functions, see
***************
*** 14521,14526 **** SELECT (pg_stat_file('filename')).modification;
--- 14645,14651 ----
<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 <[email protected]>
+ * Dimitri Fontaine <[email protected]>
*
* IDENTIFICATION
* src/backend/utils/adt/genfile.c
***************
*** 21,31 ****
--- 22,34 ----
#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"
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 267,564 ----
SRF_RETURN_DONE(funcctx);
}
+
+ /*
+ * Support functions for pg_execute_sql_file and its variant,
+ * pg_execute_sql_file_with_placeholders.
+ */
+ static char *
+ pg_read_binary_file_internal(const char *filename, int64 offset, int64 bytes_to_read)
+ {
+ FILE *file;
+ int64 fsize = -1, nbytes;
+ struct stat fst;
+ char *file_content = NULL;
+
+ /*
+ * Only superuser can call pg_execute_sql_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`.
+ */
+ if (stat(filename, &fst) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", filename)));
+
+ if (bytes_to_read <= -1)
+ fsize = Int64GetDatum((int64) fst.st_size);
+ else
+ fsize = bytes_to_read;
+
+ 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 (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)));
+
+ if (ferror(file))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read file \"%s\": %m", filename)));
+
+ file_content = (char *)palloc0((fsize+1)*sizeof(char));
+ nbytes = fread(file_content, 1, (size_t) fsize, file);
+ FreeFile(file);
+
+ return file_content;
+ }
+
+ /*
+ * Expose pg_read_binary_file() at the SQL level too
+ */
+ Datum
+ pg_read_binary_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 *filename = text_to_cstring(filename_t);
+
+ PG_RETURN_BYTEA_P(
+ cstring_to_text(
+ pg_read_binary_file_internal(filename,
+ seek_offset, bytes_to_read)));
+ }
+
+ /*
+ * Given an array of repeated {variable, value}, replaces the placeholders
+ * by their values in the given query_string, by calling replace_text over
+ * each pair of arguments.
+ */
+ static char *
+ replace_placeholders_internal(const char *query_string,
+ ArrayType *placeholders)
+ {
+ text *src = cstring_to_text(query_string);
+ Datum *replacements;
+ int nrep;
+ int i;
+ char *ret;
+
+ Assert(ARR_ELEMTYPE(placeholders) == TEXTOID);
+
+ deconstruct_array(placeholders, TEXTOID, -1, false, 'i',
+ &replacements, NULL, &nrep);
+
+ if (nrep % 2 != 0)
+ ereport(ERROR,
+ (errmsg("Expected pairs of variable names and values"),
+ errdetail("Please give an even number of replacement parameters")));
+
+ for (i = 0; i < nrep; i+=2)
+ {
+ Datum rep;
+
+ elog(DEBUG1,
+ "pg_execute_sql_file replaces '%s' with '%s'",
+ text_to_cstring(DatumGetTextP(replacements[i])),
+ text_to_cstring(DatumGetTextP(replacements[i+1])));
+
+ rep = DirectFunctionCall3(replace_text,
+ PointerGetDatum(src),
+ replacements[i], replacements[i+1]);
+ src = DatumGetTextP(rep);
+ }
+ ret = text_to_cstring(src);
+ elog(DEBUG2, "pg_execute_sql_file: %s", ret);
+
+ return ret;
+ }
+
+ /*
+ * Have replace_placeholders SQL callable directly
+ */
+ Datum
+ replace_placeholders(PG_FUNCTION_ARGS)
+ {
+ text *src_t = PG_GETARG_TEXT_P(0);
+ char *src = text_to_cstring(src_t);
+ ArrayType *placeholders = PG_GETARG_ARRAYTYPE_P(1);
+ char *replaced = replace_placeholders_internal(src, placeholders);
+
+ PG_RETURN_TEXT_P(cstring_to_text(replaced));
+ }
+
+
+ /*
+ * The bulk of the execute from file functions, just call SPI to do the real
+ * work
+ */
+ static void
+ pg_execute_internal(const char *filename, const char *query_string)
+ {
+ /*
+ * 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),
+ (filename
+ ? 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");
+
+ return;
+ }
+
+ /*
+ * The SQL callable version of it.
+ */
+ Datum
+ pg_execute_sql_string(PG_FUNCTION_ARGS)
+ {
+ text *sql_text = PG_GETARG_TEXT_P(0);
+ pg_execute_internal(NULL, text_to_cstring(sql_text));
+ PG_RETURN_VOID();
+ }
+
+ /*
+ * Execute SQL string containing placeholders.
+ */
+ Datum
+ pg_execute_sql_string_with_placeholders(PG_FUNCTION_ARGS)
+ {
+ text *sql_text = PG_GETARG_TEXT_P(0);
+ ArrayType *placeholders = PG_GETARG_ARRAYTYPE_P(1);
+ char *sql_ph = text_to_cstring(sql_text);
+ char *sql = replace_placeholders_internal(sql_ph, placeholders);
+
+ /*
+ * pg_execute_internal() error reporting includes filename, unless NULL
+ */
+ pg_execute_internal(NULL, sql);
+ PG_RETURN_VOID();
+ }
+
+ /*
+ * Read a file then execute the SQL commands it contains.
+ */
+ Datum
+ pg_execute_sql_file(PG_FUNCTION_ARGS)
+ {
+ text *filename_t = PG_GETARG_TEXT_P(0);
+ char *filename = text_to_cstring(filename_t);
+ char *sql;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to execute sql file"))));
+
+ sql = pg_read_binary_file_internal(filename, 0, -1);
+ pg_verifymbstr(sql, strlen(sql), false);
+ pg_execute_internal(filename, sql);
+ PG_RETURN_VOID();
+ }
+
+ /*
+ * Read a file, convert its encoding to the database encoding, then execute
+ * the SQL commands it contains.
+ */
+ Datum
+ pg_convert_and_execute_sql_file(PG_FUNCTION_ARGS)
+ {
+ text *filename_t = PG_GETARG_TEXT_P(0);
+ char *filename = text_to_cstring(filename_t);
+ char *sql;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to execute sql file"))));
+
+ sql = pg_read_binary_file_internal(filename, 0, -1);
+
+ if (!PG_ARGISNULL(1))
+ {
+ Datum src_encoding_name = PG_GETARG_DATUM(1);
+ Datum dest_encoding_name =
+ DirectFunctionCall1(namein,
+ CStringGetDatum(GetDatabaseEncodingName()));
+
+ sql = text_to_cstring(
+ DatumGetTextP(
+ DirectFunctionCall3(pg_convert, CStringGetDatum(sql),
+ src_encoding_name, dest_encoding_name)));
+ }
+ else
+ pg_verifymbstr(sql, strlen(sql), false);
+
+ pg_execute_internal(filename, sql);
+ PG_RETURN_VOID();
+ }
+
+ /*
+ * Variant accepting a VARIADIC text parameter containing placeholder
+ * variables and values, one after the other (so the variadic array length
+ * must be even).
+ *
+ * The main use case of the replacement facility is for setting the
+ * extension's schema, using @pg_extschema@ variable and a user given
+ * schema.
+ *
+ * This could be implemented in a single function together with the previous
+ * pg_execute_sql_file, if only it was possible to fill in the
+ * proargdefaults pg_proc column from the backend code.
+ */
+ Datum
+ pg_execute_sql_file_with_placeholders(PG_FUNCTION_ARGS)
+ {
+ text *filename_t = PG_GETARG_TEXT_P(0);
+ ArrayType *placeholders = PG_GETARG_ARRAYTYPE_P(2);
+ char *filename = text_to_cstring(filename_t);
+ char *sql;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to execute sql file"))));
+
+ sql = pg_read_binary_file_internal(filename, 0, -1);
+
+ if (!PG_ARGISNULL(1))
+ {
+ Datum src_encoding_name = PG_GETARG_DATUM(1);
+ Datum dest_encoding_name =
+ DirectFunctionCall1(namein,
+ CStringGetDatum(GetDatabaseEncodingName()));
+
+ sql = text_to_cstring(
+ DatumGetTextP(
+ DirectFunctionCall3(pg_convert, CStringGetTextDatum(sql),
+ src_encoding_name, dest_encoding_name)));
+ }
+ else
+ pg_verifymbstr(sql, strlen(sql), false);
+
+ pg_execute_internal(filename,
+ replace_placeholders_internal(sql, placeholders));
+ PG_RETURN_VOID();
+ }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3399,3412 **** 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");
--- 3399,3426 ----
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 = 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 text from a file");
+ DATA(insert OID = 3931 ( replace_placeholders PGNSP PGUID 12 1 0 25 f f f t f v 2 0 25 "25 25" "{25,25}" "{i,v}" _null_ _null_ replace_placeholders _null_ _null_ _null_ ));
+ DESCR("replace placeholders in a text");
+ 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");
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 443,448 **** extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
--- 443,455 ----
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_read_binary_file(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);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers