Tom Lane <t...@sss.pgh.pa.us> writes: > Alvaro Herrera <alvhe...@commandprompt.com> writes: >> Eh, I realize now that the right way to go about this is to use SPI. > > Yeah, that would be one way to go about it. But IMO postgres.c should > be solely concerned with interactions with the client.
I didn't notice it's "possible" to use SPI from within the backend core code, and now see precedent in xml.c where the user can give a query string. I've used SPI_execute() in the new (attached) version of the patch, that's not touching postgres.c at all anymore. The bulk of it is now short enough to be inlined in the mail, and if you have more comments I guess they'll be directed at this portion of the patch, so let's make it easy: /* * 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. */ SPI_connect(); if (SPI_execute(query_string, false, 0) < SPI_OK_UTILITY) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("File '%s' contains invalid query", filename))); SPI_finish(); 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/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,336 ---- 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. + */ + SPI_connect(); + if (SPI_execute(query_string, false, 0) < SPI_OK_UTILITY) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("File '%s' contains invalid query", filename))); + SPI_finish(); + + 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