2016-02-04 00:50:13 +08:00
- - - - - - - - - - - - - - - - - - - - - - - - - --
-- FDW MANAGEMENT FUNCTIONS
--
-- All the FDW settings are read from the `cdb_conf.fdws` entry json file.
- - - - - - - - - - - - - - - - - - - - - - - - - --
2016-02-04 19:06:22 +08:00
CREATE OR REPLACE FUNCTION cartodb . _CDB_Setup_FDW ( fdw_name text , config json )
2016-02-04 00:50:13 +08:00
RETURNS void
AS $ $
DECLARE
row record ;
option record ;
org_role text ;
BEGIN
2016-02-04 19:06:22 +08:00
-- This function tries to be as idempotent as possible, by not creating anything more than once
-- (not even using IF NOT EXIST to avoid throwing warnings)
IF NOT EXISTS ( SELECT * FROM pg_extension WHERE extname = ' postgres_fdw ' ) THEN
2016-02-04 00:50:13 +08:00
CREATE EXTENSION postgres_fdw ;
END IF ;
-- Create FDW first if it does not exist
2016-02-04 19:06:22 +08:00
IF NOT EXISTS ( SELECT * FROM pg_foreign_server WHERE srvname = fdw_name )
2016-02-04 00:50:13 +08:00
THEN
2016-02-04 19:06:22 +08:00
EXECUTE FORMAT ( ' CREATE SERVER %I FOREIGN DATA WRAPPER postgres_fdw ' , fdw_name ) ;
2016-02-04 00:50:13 +08:00
END IF ;
-- Set FDW settings
FOR row IN SELECT p . key , p . value from lateral json_each_text ( config - > ' server ' ) p
LOOP
2016-02-04 19:06:22 +08:00
IF NOT EXISTS ( WITH a AS ( select split_part ( unnest ( srvoptions ) , ' = ' , 1 ) as options from pg_foreign_server where srvname = fdw_name ) SELECT * from a where options = row . key )
2016-02-04 00:50:13 +08:00
THEN
2016-02-04 19:06:22 +08:00
EXECUTE FORMAT ( ' ALTER SERVER %I OPTIONS (ADD %I %L) ' , fdw_name , row . key , row . value ) ;
2016-02-04 00:50:13 +08:00
ELSE
2016-02-04 19:06:22 +08:00
EXECUTE FORMAT ( ' ALTER SERVER %I OPTIONS (SET %I %L) ' , fdw_name , row . key , row . value ) ;
2016-02-04 00:50:13 +08:00
END IF ;
END LOOP ;
-- Create user mappings
2016-02-04 19:06:22 +08:00
FOR row IN SELECT p . key , p . value from lateral json_each ( config - > ' users ' ) p LOOP
2016-02-04 00:50:13 +08:00
-- Check if entry on pg_user_mappings exists
2016-02-04 19:06:22 +08:00
IF NOT EXISTS ( SELECT * FROM pg_user_mappings WHERE srvname = name AND usename = row . key ) THEN
EXECUTE FORMAT ( ' CREATE USER MAPPING FOR %I SERVER %I ' , row . key , fdw_name ) ;
2016-02-04 00:50:13 +08:00
END IF ;
-- Update user mapping settings
2016-02-04 19:06:22 +08:00
FOR option IN SELECT o . key , o . value from lateral json_each_text ( row . value ) o LOOP
IF NOT EXISTS ( WITH a AS ( select split_part ( unnest ( umoptions ) , ' = ' , 1 ) as options from pg_user_mappings WHERE srvname = name AND usename = row . key ) SELECT * from a where options = option . key ) THEN
EXECUTE FORMAT ( ' ALTER USER MAPPING FOR %I SERVER %I OPTIONS (ADD %I %L) ' , row . key , fdw_name , option . key , option . value ) ;
2016-02-04 00:50:13 +08:00
ELSE
2016-02-04 19:06:22 +08:00
EXECUTE FORMAT ( ' ALTER USER MAPPING FOR %I SERVER %I OPTIONS (SET %I %L) ' , row . key , fdw_name , option . key , option . value ) ;
2016-02-04 00:50:13 +08:00
END IF ;
END LOOP ;
END LOOP ;
-- Create schema if it does not exist.
2016-02-04 19:06:22 +08:00
IF NOT EXISTS ( SELECT * from pg_namespace WHERE nspname = fdw_name ) THEN
EXECUTE FORMAT ( ' CREATE SCHEMA %I ' , fdw_name ) ;
2016-02-04 00:50:13 +08:00
END IF ;
-- Give the organization role usage permisions over the schema
SELECT cartodb . CDB_Organization_Member_Group_Role_Member_Name ( ) INTO org_role ;
EXECUTE FORMAT ( ' GRANT USAGE ON SCHEMA %I TO %I ' , name , org_role ) ;
-- Bring here the remote cdb_tablemetadata
2016-02-04 19:06:22 +08:00
IF NOT EXISTS ( SELECT * FROM PG_CLASS WHERE relnamespace = ( SELECT oid FROM pg_namespace WHERE nspname = ' do ' ) and relname = ' cdb_tablemetadata ' ) THEN
EXECUTE FORMAT ( ' IMPORT FOREIGN SCHEMA cartodb LIMIT TO (cdb_tablemetadata) FROM SERVER %I INTO %I; ' , fdw_name , fdw_name , fdw_name ) ;
2016-02-04 00:50:13 +08:00
END IF ;
2016-02-04 19:06:22 +08:00
EXECUTE FORMAT ( ' GRANT SELECT ON %I.cdb_tablemetadata TO %I ' , fdw_name , org_role ) ;
2016-02-04 00:50:13 +08:00
END
$ $
LANGUAGE PLPGSQL ;
2016-02-04 19:06:22 +08:00
CREATE OR REPLACE FUNCTION cartodb . _CDB_Setup_FDWS ( )
2016-02-04 00:50:13 +08:00
RETURNS VOID AS
$ $
DECLARE
row record ;
BEGIN
2016-02-04 19:06:22 +08:00
FOR row IN SELECT p . key , p . value from lateral json_each ( cartodb . CDB_Conf_GetConf ( ' fdws ' ) ) p LOOP
EXECUTE ' SELECT cartodb._CDB_Setup_FDW($1, $2) ' USING row . key , row . value ;
2016-02-04 00:50:13 +08:00
END LOOP ;
END
2016-02-04 19:06:22 +08:00
$ $
LANGUAGE PLPGSQL ;
2016-02-04 00:50:13 +08:00
2016-02-04 01:07:23 +08:00
2016-02-04 19:06:22 +08:00
CREATE OR REPLACE FUNCTION cartodb . _CDB_Setup_FDW ( fdw_name text )
2016-02-04 01:07:23 +08:00
RETURNS void AS
$ BODY $
DECLARE
config json ;
BEGIN
2016-02-04 19:06:22 +08:00
SELECT p . value FROM LATERAL json_each ( cartodb . CDB_Conf_GetConf ( ' fdws ' ) ) p WHERE p . key = fdw_name INTO config ;
EXECUTE ' SELECT cartodb._CDB_Setup_FDW($1, $2) ' USING fdw_name , config ;
2016-02-04 01:07:23 +08:00
END
$ BODY $
2016-02-04 19:06:22 +08:00
LANGUAGE plpgsql VOLATILE ;
2016-02-04 01:07:23 +08:00
2016-02-04 00:50:13 +08:00
CREATE OR REPLACE FUNCTION cartodb . CDB_Add_Remote_Table ( source text , table_name text )
2016-02-04 19:06:22 +08:00
RETURNS void AS
2016-02-04 00:50:13 +08:00
$ $
BEGIN
2016-02-04 19:06:22 +08:00
PERFORM cartodb . _CDB_Setup_FDW ( source ) ;
EXECUTE FORMAT ( ' IMPORT FOREIGN SCHEMA %I LIMIT TO (%I) FROM SERVER %I INTO %I; ' , source , table_name , source , source ) ;
- -- Grant SELECT to publicuser
EXECUTE FORMAT ( ' GRANT SELECT ON %I.%I TO publicuser; ' , source , table_name ) ;
2016-02-04 00:50:13 +08:00
END
$ $
2016-02-04 19:06:22 +08:00
LANGUAGE plpgsql ;
2016-02-05 01:26:43 +08:00
CREATE OR REPLACE FUNCTION cartodb . CDB_Get_Foreign_Updated_At ( foreign_table regclass )
RETURNS timestamp with time zone AS
$ $
DECLARE
remote_table_name text ;
fdw_schema_name text ;
time timestamp with time zone ;
BEGIN
-- This will turn a local foreign table (referenced as regclass) to its fully qualified text remote table reference.
WITH a AS ( SELECT ftoptions FROM pg_foreign_table WHERE ftrelid = foreign_table LIMIT 1 ) ,
b as ( SELECT ( pg_options_to_table ( ftoptions ) ) . * FROM a )
SELECT FORMAT ( ' %I.%I ' , ( SELECT option_value FROM b WHERE option_name = ' schema_name ' ) , ( SELECT option_value FROM b WHERE option_name = ' table_name ' ) )
INTO remote_table_name ;
-- We assume that the remote cdb_tablemetadata is called cdb_tablemetadata and is on the same schema as the queried table.
SELECT nspname FROM pg_class c , pg_namespace n WHERE c . oid = foreign_table AND c . relnamespace = n . oid INTO fdw_schema_name ;
EXECUTE FORMAT ( ' SELECT updated_at FROM %I.cdb_tablemetadata WHERE tabname::text=%L ORDER BY updated_at DESC LIMIT 1 ' , fdw_schema_name , remote_table_name ) INTO time
RETURN time ;
END
$ $
LANGUAGE plpgsql ;