Un-DOS-ify newly added files.

This commit is contained in:
Tom Lane 2006-05-30 21:34:15 +00:00
parent e60cb3a35c
commit e95703eac3
3 changed files with 448 additions and 446 deletions

View File

@ -1,15 +1,15 @@
MODULE_big = adminpack MODULE_big = adminpack
PG_CPPFLAGS = -I$(libpq_srcdir) PG_CPPFLAGS = -I$(libpq_srcdir)
DATA_built = adminpack.sql DATA_built = adminpack.sql
DOCS = README.adminpack DOCS = README.adminpack
OBJS = adminpack.o OBJS = adminpack.o
ifdef USE_PGXS ifdef USE_PGXS
PGXS := $(shell pg_config --pgxs) PGXS := $(shell pg_config --pgxs)
include $(PGXS) include $(PGXS)
else else
subdir = contrib/adminpack subdir = contrib/adminpack
top_builddir = ../.. top_builddir = ../..
include $(top_builddir)/src/Makefile.global include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk include $(top_srcdir)/contrib/contrib-global.mk
endif endif

View File

@ -1,390 +1,392 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* admin81.c * admin81.c
* *
* *
* Copyright (c) 2002 - 2006, PostgreSQL Global Development Group * Copyright (c) 2002 - 2006, PostgreSQL Global Development Group
* *
* Author: Andreas Pflug <pgadmin@pse-consulting.de> * Author: Andreas Pflug <pgadmin@pse-consulting.de>
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/contrib/adminpack/adminpack.c,v 1.1 2006/05/30 12:07:31 momjian Exp $ * $PostgreSQL: pgsql/contrib/adminpack/adminpack.c,v 1.2 2006/05/30 21:34:15 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include <sys/file.h> #include <sys/file.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <dirent.h> #include <dirent.h>
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "funcapi.h" #include "funcapi.h"
#include "utils/datetime.h" #include "utils/datetime.h"
#ifdef WIN32 #ifdef WIN32
#ifdef rename #ifdef rename
#undef rename #undef rename
#endif #endif
#ifdef unlink #ifdef unlink
#undef unlink #undef unlink
#endif #endif
#endif #endif
extern DLLIMPORT char *DataDir; extern DLLIMPORT char *DataDir;
extern DLLIMPORT char *Log_directory; extern DLLIMPORT char *Log_directory;
extern DLLIMPORT char *Log_filename; extern DLLIMPORT char *Log_filename;
Datum pg_file_write(PG_FUNCTION_ARGS); PG_MODULE_MAGIC;
Datum pg_file_rename(PG_FUNCTION_ARGS);
Datum pg_file_unlink(PG_FUNCTION_ARGS); Datum pg_file_write(PG_FUNCTION_ARGS);
Datum pg_logdir_ls(PG_FUNCTION_ARGS); Datum pg_file_rename(PG_FUNCTION_ARGS);
Datum pg_file_unlink(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(pg_file_write); Datum pg_logdir_ls(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(pg_file_rename);
PG_FUNCTION_INFO_V1(pg_file_unlink); PG_FUNCTION_INFO_V1(pg_file_write);
PG_FUNCTION_INFO_V1(pg_logdir_ls); PG_FUNCTION_INFO_V1(pg_file_rename);
PG_FUNCTION_INFO_V1(pg_file_unlink);
typedef struct PG_FUNCTION_INFO_V1(pg_logdir_ls);
{
char *location; typedef struct
DIR *dirdesc; {
} directory_fctx; char *location;
DIR *dirdesc;
/*----------------------- } directory_fctx;
* some helper functions
*/ /*-----------------------
* some helper functions
/* */
* Return an absolute path. Argument may be absolute or
* relative to the DataDir. /*
*/ * Return an absolute path. Argument may be absolute or
static char *absClusterPath(text *arg, bool logAllowed) * relative to the DataDir.
{ */
char *filename; static char *absClusterPath(text *arg, bool logAllowed)
int len=VARSIZE(arg) - VARHDRSZ; {
int dlen = strlen(DataDir); char *filename;
int len=VARSIZE(arg) - VARHDRSZ;
filename = palloc(len+1); int dlen = strlen(DataDir);
memcpy(filename, VARDATA(arg), len);
filename[len] = 0; filename = palloc(len+1);
memcpy(filename, VARDATA(arg), len);
if (strstr(filename, "..") != NULL) filename[len] = 0;
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), if (strstr(filename, "..") != NULL)
(errmsg("No .. allowed in filenames")))); ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
if (is_absolute_path(filename)) (errmsg("No .. allowed in filenames"))));
{
if (logAllowed && !strncmp(filename, Log_directory, strlen(Log_directory))) if (is_absolute_path(filename))
return filename; {
if (strncmp(filename, DataDir, dlen)) if (logAllowed && !strncmp(filename, Log_directory, strlen(Log_directory)))
ereport(ERROR, return filename;
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), if (strncmp(filename, DataDir, dlen))
(errmsg("Absolute path not allowed")))); ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
return filename; (errmsg("Absolute path not allowed"))));
}
else return filename;
{ }
char *absname = palloc(dlen+len+2); else
sprintf(absname, "%s/%s", DataDir, filename); {
pfree(filename); char *absname = palloc(dlen+len+2);
return absname; sprintf(absname, "%s/%s", DataDir, filename);
} pfree(filename);
} return absname;
}
}
/*
* check for superuser, bark if not.
*/ /*
static void * check for superuser, bark if not.
requireSuperuser(void) */
{ static void
if (!superuser()) requireSuperuser(void)
ereport(ERROR, {
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), if (!superuser())
(errmsg("only superuser may access generic file functions")))); ereport(ERROR,
} (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("only superuser may access generic file functions"))));
}
/* ------------------------------------
* generic file handling functions
*/ /* ------------------------------------
* generic file handling functions
Datum pg_file_write(PG_FUNCTION_ARGS) */
{
FILE *f; Datum pg_file_write(PG_FUNCTION_ARGS)
char *filename; {
text *data; FILE *f;
int64 count = 0; char *filename;
text *data;
requireSuperuser(); int64 count = 0;
filename = absClusterPath(PG_GETARG_TEXT_P(0), false); requireSuperuser();
data = PG_GETARG_TEXT_P(1);
filename = absClusterPath(PG_GETARG_TEXT_P(0), false);
if (PG_ARGISNULL(2) || !PG_GETARG_BOOL(2)) data = PG_GETARG_TEXT_P(1);
{
struct stat fst; if (PG_ARGISNULL(2) || !PG_GETARG_BOOL(2))
if (stat(filename, &fst) >= 0) {
ereport(ERROR, struct stat fst;
(ERRCODE_DUPLICATE_FILE, if (stat(filename, &fst) >= 0)
errmsg("file %s exists", filename))); ereport(ERROR,
(ERRCODE_DUPLICATE_FILE,
f = fopen(filename, "wb"); errmsg("file %s exists", filename)));
}
else f = fopen(filename, "wb");
f = fopen(filename, "ab"); }
else
if (!f) f = fopen(filename, "ab");
{
ereport(ERROR, if (!f)
(errcode_for_file_access(), {
errmsg("could open file %s for writing: %m", filename))); ereport(ERROR,
} (errcode_for_file_access(),
errmsg("could open file %s for writing: %m", filename)));
if (VARSIZE(data) != 0) }
{
count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f); if (VARSIZE(data) != 0)
{
if (count != VARSIZE(data) - VARHDRSZ) count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f);
ereport(ERROR,
(errcode_for_file_access(), if (count != VARSIZE(data) - VARHDRSZ)
errmsg("error writing file %s: %m", filename))); ereport(ERROR,
} (errcode_for_file_access(),
fclose(f); errmsg("error writing file %s: %m", filename)));
}
PG_RETURN_INT64(count); fclose(f);
}
PG_RETURN_INT64(count);
}
Datum pg_file_rename(PG_FUNCTION_ARGS)
{
char *fn1, *fn2, *fn3; Datum pg_file_rename(PG_FUNCTION_ARGS)
int rc; {
char *fn1, *fn2, *fn3;
requireSuperuser(); int rc;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) requireSuperuser();
PG_RETURN_NULL();
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
fn1=absClusterPath(PG_GETARG_TEXT_P(0), false); PG_RETURN_NULL();
fn2=absClusterPath(PG_GETARG_TEXT_P(1), false);
if (PG_ARGISNULL(2)) fn1=absClusterPath(PG_GETARG_TEXT_P(0), false);
fn3=0; fn2=absClusterPath(PG_GETARG_TEXT_P(1), false);
else if (PG_ARGISNULL(2))
fn3=absClusterPath(PG_GETARG_TEXT_P(2), false); fn3=0;
else
if (access(fn1, W_OK) < 0) fn3=absClusterPath(PG_GETARG_TEXT_P(2), false);
{
ereport(WARNING, if (access(fn1, W_OK) < 0)
(errcode_for_file_access(), {
errmsg("file %s not accessible: %m", fn1))); ereport(WARNING,
(errcode_for_file_access(),
PG_RETURN_BOOL(false); errmsg("file %s not accessible: %m", fn1)));
}
PG_RETURN_BOOL(false);
if (fn3 && access(fn2, W_OK) < 0) }
{
ereport(WARNING, if (fn3 && access(fn2, W_OK) < 0)
(errcode_for_file_access(), {
errmsg("file %s not accessible: %m", fn2))); ereport(WARNING,
(errcode_for_file_access(),
PG_RETURN_BOOL(false); errmsg("file %s not accessible: %m", fn2)));
}
PG_RETURN_BOOL(false);
}
rc = access(fn3 ? fn3 : fn2, 2);
if (rc >= 0 || errno != ENOENT)
{ rc = access(fn3 ? fn3 : fn2, 2);
ereport(ERROR, if (rc >= 0 || errno != ENOENT)
(ERRCODE_DUPLICATE_FILE, {
errmsg("cannot rename to target file %s", fn3 ? fn3 : fn2))); ereport(ERROR,
} (ERRCODE_DUPLICATE_FILE,
errmsg("cannot rename to target file %s", fn3 ? fn3 : fn2)));
if (fn3) }
{
if (rename(fn2, fn3) != 0) if (fn3)
{ {
ereport(ERROR, if (rename(fn2, fn3) != 0)
(errcode_for_file_access(), {
errmsg("could not rename %s to %s: %m", fn2, fn3))); ereport(ERROR,
} (errcode_for_file_access(),
if (rename(fn1, fn2) != 0) errmsg("could not rename %s to %s: %m", fn2, fn3)));
{ }
ereport(WARNING, if (rename(fn1, fn2) != 0)
(errcode_for_file_access(), {
errmsg("could not rename %s to %s: %m", fn1, fn2))); ereport(WARNING,
(errcode_for_file_access(),
if (rename(fn3, fn2) != 0) errmsg("could not rename %s to %s: %m", fn1, fn2)));
{
ereport(ERROR, if (rename(fn3, fn2) != 0)
(errcode_for_file_access(), {
errmsg("could not rename %s back to %s: %m", fn3, fn2))); ereport(ERROR,
} (errcode_for_file_access(),
else errmsg("could not rename %s back to %s: %m", fn3, fn2)));
{ }
ereport(ERROR, else
(ERRCODE_UNDEFINED_FILE, {
errmsg("renaming %s to %s was reverted", fn2, fn3))); ereport(ERROR,
(ERRCODE_UNDEFINED_FILE,
} errmsg("renaming %s to %s was reverted", fn2, fn3)));
}
} }
else if (rename(fn1, fn2) != 0) }
{ }
ereport(WARNING, else if (rename(fn1, fn2) != 0)
(errcode_for_file_access(), {
errmsg("renaming %s to %s %m", fn1, fn2))); ereport(WARNING,
ereport(ERROR, (errcode_for_file_access(),
(errcode_for_file_access(), errmsg("renaming %s to %s %m", fn1, fn2)));
errmsg("could not rename %s to %s: %m", fn1, fn2))); ereport(ERROR,
} (errcode_for_file_access(),
errmsg("could not rename %s to %s: %m", fn1, fn2)));
PG_RETURN_BOOL(true); }
}
PG_RETURN_BOOL(true);
}
Datum pg_file_unlink(PG_FUNCTION_ARGS)
{
char *filename; Datum pg_file_unlink(PG_FUNCTION_ARGS)
{
requireSuperuser(); char *filename;
filename = absClusterPath(PG_GETARG_TEXT_P(0), false); requireSuperuser();
if (access(filename, W_OK) < 0) filename = absClusterPath(PG_GETARG_TEXT_P(0), false);
{
if (errno == ENOENT) if (access(filename, W_OK) < 0)
PG_RETURN_BOOL(false); {
else if (errno == ENOENT)
ereport(ERROR, PG_RETURN_BOOL(false);
(errcode_for_file_access(), else
errmsg("file %s not accessible: %m", filename))); ereport(ERROR,
(errcode_for_file_access(),
} errmsg("file %s not accessible: %m", filename)));
if (unlink(filename) < 0) }
{
ereport(WARNING, if (unlink(filename) < 0)
(errcode_for_file_access(), {
errmsg("could not unlink file %s: %m", filename))); ereport(WARNING,
(errcode_for_file_access(),
PG_RETURN_BOOL(false); errmsg("could not unlink file %s: %m", filename)));
}
PG_RETURN_BOOL(true); PG_RETURN_BOOL(false);
} }
PG_RETURN_BOOL(true);
}
Datum pg_logdir_ls(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx; Datum pg_logdir_ls(PG_FUNCTION_ARGS)
struct dirent *de; {
directory_fctx *fctx; FuncCallContext *funcctx;
struct dirent *de;
if (!superuser()) directory_fctx *fctx;
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), if (!superuser())
(errmsg("only superuser can list the log directory")))); ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
if (memcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log", 30) != 0) (errmsg("only superuser can list the log directory"))));
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), if (memcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log", 30) != 0)
(errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'")))); ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
if (SRF_IS_FIRSTCALL()) (errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'"))));
{
MemoryContext oldcontext; if (SRF_IS_FIRSTCALL())
TupleDesc tupdesc; {
MemoryContext oldcontext;
funcctx=SRF_FIRSTCALL_INIT(); TupleDesc tupdesc;
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
funcctx=SRF_FIRSTCALL_INIT();
fctx = palloc(sizeof(directory_fctx)); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
if (is_absolute_path(Log_directory))
fctx->location = Log_directory; fctx = palloc(sizeof(directory_fctx));
else if (is_absolute_path(Log_directory))
{ fctx->location = Log_directory;
fctx->location = palloc(strlen(DataDir) + strlen(Log_directory) +2); else
sprintf(fctx->location, "%s/%s", DataDir, Log_directory); {
} fctx->location = palloc(strlen(DataDir) + strlen(Log_directory) +2);
tupdesc = CreateTemplateTupleDesc(2, false); sprintf(fctx->location, "%s/%s", DataDir, Log_directory);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", }
TIMESTAMPOID, -1, 0); tupdesc = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename", TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
TEXTOID, -1, 0); TIMESTAMPOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename",
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); TEXTOID, -1, 0);
fctx->dirdesc = AllocateDir(fctx->location); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
if (!fctx->dirdesc) fctx->dirdesc = AllocateDir(fctx->location);
ereport(ERROR,
(errcode_for_file_access(), if (!fctx->dirdesc)
errmsg("%s is not browsable: %m", fctx->location))); ereport(ERROR,
(errcode_for_file_access(),
funcctx->user_fctx = fctx; errmsg("%s is not browsable: %m", fctx->location)));
MemoryContextSwitchTo(oldcontext);
} funcctx->user_fctx = fctx;
MemoryContextSwitchTo(oldcontext);
funcctx=SRF_PERCALL_SETUP(); }
fctx = (directory_fctx*) funcctx->user_fctx;
funcctx=SRF_PERCALL_SETUP();
if (!fctx->dirdesc) /* not a readable directory */ fctx = (directory_fctx*) funcctx->user_fctx;
SRF_RETURN_DONE(funcctx);
if (!fctx->dirdesc) /* not a readable directory */
while ((de = readdir(fctx->dirdesc)) != NULL) SRF_RETURN_DONE(funcctx);
{
char *values[2]; while ((de = readdir(fctx->dirdesc)) != NULL)
HeapTuple tuple; {
char *values[2];
char *field[MAXDATEFIELDS]; HeapTuple tuple;
char lowstr[MAXDATELEN + 1];
int dtype; char *field[MAXDATEFIELDS];
int nf, ftype[MAXDATEFIELDS]; char lowstr[MAXDATELEN + 1];
fsec_t fsec; int dtype;
int tz = 0; int nf, ftype[MAXDATEFIELDS];
struct pg_tm date; fsec_t fsec;
int tz = 0;
/* struct pg_tm date;
* Default format:
* postgresql-YYYY-MM-DD_HHMMSS.log /*
*/ * Default format:
if (strlen(de->d_name) != 32 * postgresql-YYYY-MM-DD_HHMMSS.log
|| memcmp(de->d_name, "postgresql-", 11) */
|| de->d_name[21] != '_' if (strlen(de->d_name) != 32
|| strcmp(de->d_name + 28, ".log")) || memcmp(de->d_name, "postgresql-", 11)
continue; || de->d_name[21] != '_'
|| strcmp(de->d_name + 28, ".log"))
values[1] = palloc(strlen(fctx->location) + strlen(de->d_name) + 2); continue;
sprintf(values[1], "%s/%s", fctx->location, de->d_name);
values[1] = palloc(strlen(fctx->location) + strlen(de->d_name) + 2);
values[0] = de->d_name + 11; /* timestamp */ sprintf(values[1], "%s/%s", fctx->location, de->d_name);
values[0][17] = 0;
values[0] = de->d_name + 11; /* timestamp */
/* parse and decode expected timestamp */ values[0][17] = 0;
if (ParseDateTime(values[0], lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf))
continue; /* parse and decode expected timestamp */
if (ParseDateTime(values[0], lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf))
if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz)) continue;
continue;
if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz))
/* Seems the format fits the expected format; feed it into the tuple */ continue;
tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); /* Seems the format fits the expected format; feed it into the tuple */
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
}
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
FreeDir(fctx->dirdesc); }
SRF_RETURN_DONE(funcctx);
} FreeDir(fctx->dirdesc);
SRF_RETURN_DONE(funcctx);
}

View File

@ -1,41 +1,41 @@
/* *********************************************** /* ***********************************************
* Administrative functions for PostgreSQL * Administrative functions for PostgreSQL
* *********************************************** */ * *********************************************** */
/* generic file access functions */ /* generic file access functions */
CREATE FUNCTION pg_catalog.pg_file_write(text, text, bool) RETURNS bigint CREATE FUNCTION pg_catalog.pg_file_write(text, text, bool) RETURNS bigint
AS 'MODULE_PATHNAME', 'pg_file_write' AS 'MODULE_PATHNAME', 'pg_file_write'
LANGUAGE C VOLATILE STRICT; LANGUAGE C VOLATILE STRICT;
CREATE FUNCTION pg_catalog.pg_file_rename(text, text, text) RETURNS bool CREATE FUNCTION pg_catalog.pg_file_rename(text, text, text) RETURNS bool
AS 'MODULE_PATHNAME', 'pg_file_rename' AS 'MODULE_PATHNAME', 'pg_file_rename'
LANGUAGE C VOLATILE; LANGUAGE C VOLATILE;
CREATE FUNCTION pg_catalog.pg_file_unlink(text) RETURNS bool CREATE FUNCTION pg_catalog.pg_file_unlink(text) RETURNS bool
AS 'MODULE_PATHNAME', 'pg_file_unlink' AS 'MODULE_PATHNAME', 'pg_file_unlink'
LANGUAGE C VOLATILE STRICT; LANGUAGE C VOLATILE STRICT;
CREATE FUNCTION pg_catalog.pg_file_rename(text, text) RETURNS bool CREATE FUNCTION pg_catalog.pg_file_rename(text, text) RETURNS bool
AS 'SELECT pg_file_rename($1, $2, NULL); ' AS 'SELECT pg_file_rename($1, $2, NULL); '
LANGUAGE SQL VOLATILE STRICT; LANGUAGE SQL VOLATILE STRICT;
CREATE FUNCTION pg_catalog.pg_logdir_ls() RETURNS setof record CREATE FUNCTION pg_catalog.pg_logdir_ls() RETURNS setof record
AS 'MODULE_PATHNAME', 'pg_logdir_ls' AS 'MODULE_PATHNAME', 'pg_logdir_ls'
LANGUAGE C VOLATILE STRICT; LANGUAGE C VOLATILE STRICT;
/* compatibility redefines */ /* compatibility redefines */
CREATE FUNCTION pg_catalog.pg_logfile_rotate() RETURNS int4 CREATE FUNCTION pg_catalog.pg_logfile_rotate() RETURNS int4
AS 'pg_rotate_logfile' AS 'pg_rotate_logfile'
LANGUAGE INTERNAL VOLATILE STRICT; LANGUAGE INTERNAL VOLATILE STRICT;
CREATE FUNCTION pg_catalog.pg_file_read(text, bigint, bigint) RETURNS text CREATE FUNCTION pg_catalog.pg_file_read(text, bigint, bigint) RETURNS text
AS 'pg_read_file' AS 'pg_read_file'
LANGUAGE INTERNAL VOLATILE STRICT; LANGUAGE INTERNAL VOLATILE STRICT;
CREATE FUNCTION pg_catalog.pg_file_length(text) RETURNS bigint CREATE FUNCTION pg_catalog.pg_file_length(text) RETURNS bigint
AS 'SELECT size FROM pg_stat_file($1)' AS 'SELECT size FROM pg_stat_file($1)'
LANGUAGE SQL VOLATILE STRICT; LANGUAGE SQL VOLATILE STRICT;