3

我创建了两个 postgres 数据库。版本 9.5.4。第一个数据库叫做persist。我正在使用postgrest中的一些示例代码进行身份验证。代码效果很好,这是我的持久数据库定义:

create schema localschema;

grant all on schema localschema to postgres;

create table  localschema.users (
  email    text primary key check ( email ~* '^.+@.+\..+$' ),
  pass     text not null check (length(pass) < 512),
  role     name not null check (length(role) < 512),
  verified boolean not null default false
  -- If you like add more columns, or a json column
);
create or replace function
localschema.encrypt_pass() returns trigger
  language plpgsql
  as $$
declare
  mvar text;
begin
  if tg_op = 'INSERT' or new.pass <> old.pass then
    new.pass = crypt(new.pass, gen_salt('bf'::text)); 
  end if;
  return new;
end
$$;
drop trigger if exists encrypt_pass on localschema.users;
create trigger encrypt_pass
  before insert or update on localschema.users
  for each row
  execute procedure localschema.encrypt_pass();

相当简单,在这里我调用了一个插入到表中,然后是一个选择,你可以看到通过触发器/函数 encrypt_pass 加密的 pass 使用:

persist=# insert into localschema.users values('g@l.com','123123','postgres');
persist=# select * from localschema.users;
  email  |                             pass                             |   role   | verified 
---------+--------------------------------------------------------------+----------+----------
 g@l.com | $2a$06$5p5eM6sJAfxZ4qSv0Jgx..GlflYkNeE7aY.D4kR9K0glRZv2wU7ue | postgres | f
(1 row)

效果很好。下一步是 postgres_fdw,我将外部模式导入到远程数据库到我的另一个数据库:

CREATE EXTENSION IF NOT EXISTS "postgres_fdw";

create server self foreign data wrapper postgres_fdw options(host '127.0.0.1', dbname 'persist', port '5432');
  create user mapping for postgres server self options (user 'postgres');
create  schema "localschema";
  grant all on schema localschema to postgres;
import foreign schema localschema from server self into "localschema" options ( import_default 'true');

然后选择以证明它正在工作:

cache=# select * from localschema.users;
  email  |                             pass                             |   role   | verified 
---------+--------------------------------------------------------------+----------+----------
 g@l.com | $2a$06$5p5eM6sJAfxZ4qSv0Jgx..GlflYkNeE7aY.D4kR9K0glRZv2wU7ue | postgres | f
(1 row)

问题是我不能插入到这个表中。这是操作中的错误:

cache=# insert into localschema.users values ('f@l.com','234234','postgres');
ERROR:  function gen_salt(text) does not exist
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
CONTEXT:  Remote SQL command: INSERT INTO localschema.users(email, pass, role, verified) VALUES ($1, $2, $3, $4)
PL/pgSQL function localschema.encrypt_pass() line 6 at assignment

很奇怪,找不到任何函数,不只是gen_salt。我在持久化端放置了一些引发异常语句,以验证我的 current_user 确实是 postgres。那么,当我通过 postgres_fdw 执行时,为什么我的所有函数都会消失?

我错过了什么。我很确定我已经完成了这项工作,但是在我的所有设置/拆卸过程中,我不知何故失去了正确的咒语。

-G

解析度:

在我发布这个之后,我终于弄清楚了这个问题。当。直接来自 postgres_fdw 手册postgres-fdw

在 postgres_fdw 打开的远程会话中,search_path 参数设置为仅 pg_catalog,因此只有内置对象可见,无需模式限定。这对于 postgres_fdw 本身生成的查询来说不是问题,因为它总是提供这样的限定条件。但是,这可能会对通过远程表上的触发器或规则在远程服务器上执行的功能造成危害。例如,如果远程表实际上是一个视图,则该视图中使用的任何函数都将使用受限搜索路径执行。建议对此类函数中的所有名称进行模式限定,或者将 SET search_path 选项(请参阅 CREATE FUNCTION)附加到此类函数以建立其预期的搜索路径环境。

我更改了函数,以便它使用它调用的完整路径:

create or replace function
localschema.encrypt_pass() returns trigger
  language plpgsql
  as $$
declare
  mvar text;
begin
  if tg_op = 'INSERT' or new.pass <> old.pass then
    new.pass = public.crypt(new.pass, public.gen_salt('bf'::text));
  end if;
  return new;
end
$$;

现在它正在工作!

4

0 回答 0