如何从Postgres内部列出文件夹中的文件?

时间:2014-08-20 19:53:14

标签: postgresql

有没有办法列出文件夹中的文件?

类似的东西:

select * from pg_ls_dir('/home/christian')

我尝试了pg_ls_dir,但per documentation

  

仅数据库群集目录中的文件和log_directory   可以访问。使用群集中文件的相对路径   目录,以及与log_directory配置设置匹配的路径   用于日志文件。这些功能的使用仅限于超级用户。

我需要列出postgres目录之外的文件夹中的文件,类似于COPY完成的文件。

3 个答案:

答案 0 :(得分:9)

使用PostgreSQL 9.3,可以避免安装语言扩展的开销:

DROP TABLE IF EXISTS files;
CREATE TABLE files(filename text);
COPY files FROM PROGRAM 'find /usr/bin -maxdepth 1 -type f -printf "%f\n"'; 
SELECT * FROM files ORDER BY filename ASC;

在我的计算机上,从[zip在81毫秒内创建一个包含2,000多行的表格。

通常COPY命令需要超级用户权限。由于文件系统的路径是硬编码的(即,不是来自用户的未经过计算的值),因此首先使用超级用户帐户定义函数不会带来很大的安全风险(例如, postgres)如下:

CREATE OR REPLACE FUNCTION files()
  RETURNS SETOF text AS
$BODY$
BEGIN
  SET client_min_messages TO WARNING;
  DROP TABLE IF EXISTS files;
  CREATE TEMP TABLE files(filename text);
  COPY files FROM PROGRAM 'find /usr/bin -maxdepth 1 -type f -printf "%f\n"';
  RETURN QUERY SELECT * FROM files ORDER BY filename ASC;
END;
$BODY$
  LANGUAGE plpgsql SECURITY DEFINER;

使用非超级用户帐户登录PostgreSQL,然后:

SELECT * FROM files();

应返回相同的结果列表,不会出现任何安全违规错误。

SECURITY DEFINER告诉PostgreSQL在用于创建函数的帐户的角色下运行该函数。由于它是使用超级用户角色创建的,因此无论执行命令的角色如何,它都将以超级用户权限执行。

如果无法删除表,SET client_min_messages TO WARNING;告诉PostgreSQL禁止消息。删除这一行是可以的。

CREATE TEMP TABLE用于创建一个不需要随时间持续存在的表。如果您需要永久表,请删除TEMP修饰符。

'find...'命令(也可以是/usr/bin/find)仅列出文件(type -f)并仅显示文件名,而前导路径每行只分隔一个文件名({{1} })。最后,-printf "%f\n"将文件搜索限制为仅指定的目录,而不搜索任何子目录。有关详细信息,请参阅find's man page

这种方法的一个缺点是似乎没有办法参数化要执行的命令。似乎PostgreSQL要求它是文本字符串,而不是表达式语句。也许这是最好的,因为它阻止允许执行任意命令。你看到的就是你执行的东西。

答案 1 :(得分:5)

它通常对SQL客户端没用。

无论如何,您需要实现它,这是plperlu等脚本语言的典型用例。例如:

CREATE FUNCTION nosecurity_ls(text) RETURNS setof text AS $$
  opendir(my $d, $_[0]) or die $!;
  while (my $f=readdir($d)) {
    return_next($f);
  }
  return undef; 
$$ language plperlu;

除了限制之外,它等同于System Administration Functions中提到的pg_ls_dir(text)功能。


  => select * from nosecurity_ls('/var/lib/postgresql/9.1/main') as ls;
      ls      
-----------------
 pg_subtrans
 pg_serial
 pg_notify
 pg_clog
 pg_multixact
 ..
 base
 pg_twophase
 etc...

答案 2 :(得分:5)

this answer的扩展版本,功能ls_files_extended

-- Unfortunately that variant only allow use hardcoded path
-- To use user parameter we will use dynamic EXECUTE.
-- Return also file size and allow filtering
--
-- @param path text. Filesystem path for read to
-- @param filter text (default null meaning return all). Where condition to filter files. F.e.: $$filename LIKE '0%'$$
-- @param sort text (default filename).
--
-- Examples of use:
-- 1) Simple call, return all files, sort by filename:
-- SELECT * FROM ls_files_extended('/pg_xlog.archive')
-- 2) Return all, sort by filesize:
-- SELECT * FROM ls_files_extended('/pg_xlog.archive', null, 'size ASC')
-- 3) Use filtering and sorting:
-- SELECT * FROM ls_files_extended('/pg_xlog.archive', 'filename LIKE ''0%''', 'size ASC')
-- or use $-quoting for easy readability:
-- SELECT * FROM ls_files_extended('/pg_xlog.archive', $$filename LIKE '0%'$$, 'size ASC')
CREATE OR REPLACE FUNCTION ls_files_extended(path text, filter text default null, sort text default 'filename')
    RETURNS TABLE(filename text, size bigint) AS
$BODY$
BEGIN
  SET client_min_messages TO WARNING;
  CREATE TEMP TABLE _files(filename text, size bigint) ON COMMIT DROP;

  EXECUTE format($$COPY _files FROM PROGRAM 'find %s -maxdepth 1 -type f -printf "%%f\t%%s\n"'$$, path);

  RETURN QUERY EXECUTE format($$SELECT * FROM _files WHERE %s ORDER BY %s $$, concat_ws(' AND ', 'true', filter), sort);
END;
$BODY$ LANGUAGE plpgsql SECURITY DEFINER;