Hi,

I noticed that there is no postgres_fdw option to control whether check
constraints on remote tables are included in the definitions of foreign
tables imported from a remote PG server when performing IMPORT FOREIGN
SCHEMA, while we now allow check constraints on foreign tables.

Attached is a patch for that.  I'll add this to the next CF.

Best regards,
Etsuro Fujita
*** a/contrib/postgres_fdw/expected/postgres_fdw.out
--- b/contrib/postgres_fdw/expected/postgres_fdw.out
***************
*** 3415,3420 **** CREATE TABLE import_source.t2 (c1 int default 42, c2 varchar NULL, c3 text colla
--- 3415,3421 ----
  CREATE TYPE typ1 AS (m1 int, m2 varchar);
  CREATE TABLE import_source.t3 (c1 timestamptz default now(), c2 typ1);
  CREATE TABLE import_source."x 4" (c1 float8, "C 2" text, c3 varchar(42));
+ ALTER TABLE import_source."x 4" ADD CONSTRAINT c1positive CHECK (c1 >= 0);
  CREATE TABLE import_source."x 5" (c1 float8);
  ALTER TABLE import_source."x 5" DROP COLUMN c1;
  CREATE SCHEMA import_dest1;
***************
*** 3462,3467 **** FDW Options: (schema_name 'import_source', table_name 't3')
--- 3463,3470 ----
   c1     | double precision      |           | (column_name 'c1')
   C 2    | text                  |           | (column_name 'C 2')
   c3     | character varying(42) |           | (column_name 'c3')
+ Check constraints:
+     "c1positive" CHECK (c1 >= 0::double precision)
  Server: loopback
  FDW Options: (schema_name 'import_source', table_name 'x 4')
  
***************
*** 3518,3523 **** FDW Options: (schema_name 'import_source', table_name 't3')
--- 3521,3528 ----
   c1     | double precision      |           | (column_name 'c1')
   C 2    | text                  |           | (column_name 'C 2')
   c3     | character varying(42) |           | (column_name 'c3')
+ Check constraints:
+     "c1positive" CHECK (c1 >= 0::double precision)
  Server: loopback
  FDW Options: (schema_name 'import_source', table_name 'x 4')
  
***************
*** 3529,3535 **** FDW Options: (schema_name 'import_source', table_name 'x 5')
  
  CREATE SCHEMA import_dest3;
  IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest3
!   OPTIONS (import_collate 'false', import_not_null 'false');
  \det+ import_dest3
                                       List of foreign tables
      Schema    | Table |  Server  |                   FDW Options                   | Description 
--- 3534,3540 ----
  
  CREATE SCHEMA import_dest3;
  IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest3
!   OPTIONS (import_collate 'false', import_not_null 'false', import_check 'false');
  \det+ import_dest3
                                       List of foreign tables
      Schema    | Table |  Server  |                   FDW Options                   | Description 
*** a/contrib/postgres_fdw/postgres_fdw.c
--- b/contrib/postgres_fdw/postgres_fdw.c
***************
*** 2584,2589 **** postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
--- 2584,2590 ----
  	bool		import_collate = true;
  	bool		import_default = false;
  	bool		import_not_null = true;
+ 	bool		import_check = true;
  	ForeignServer *server;
  	UserMapping *mapping;
  	PGconn	   *conn;
***************
*** 2604,2609 **** postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
--- 2605,2612 ----
  			import_default = defGetBoolean(def);
  		else if (strcmp(def->defname, "import_not_null") == 0)
  			import_not_null = defGetBoolean(def);
+ 		else if (strcmp(def->defname, "import_check") == 0)
+ 			import_check = defGetBoolean(def);
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
***************
*** 2824,2829 **** postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
--- 2827,2929 ----
  		/* Clean up */
  		PQclear(res);
  		res = NULL;
+ 
+ 		/*
+ 		 * Fetch all table (and column) check constraint data from this schema,
+ 		 * possibly restricted by EXCEPT or LIMIT TO.
+ 		 */
+ 		if (import_check)
+ 		{
+ 			resetStringInfo(&buf);
+ 
+ 			appendStringInfoString(&buf,
+ 								   "SELECT relname, "
+ 								   "  conname, "
+ 								   "  pg_get_constraintdef(r.oid) "
+ 								   "FROM pg_class c "
+ 								   "  JOIN pg_namespace n ON "
+ 								   "    relnamespace = n.oid "
+ 								   "  JOIN pg_constraint r ON "
+ 								   "    conrelid = c.oid AND contype = 'c' ");
+ 
+ 			appendStringInfoString(&buf,
+ 								   "WHERE c.relkind IN ('r', 'f') "
+ 								   "  AND n.nspname = ");
+ 			deparseStringLiteral(&buf, stmt->remote_schema);
+ 
+ 			/* Apply restrictions for LIMIT TO and EXCEPT */
+ 			if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
+ 				stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
+ 			{
+ 				bool		first_item = true;
+ 
+ 				appendStringInfoString(&buf, " AND c.relname ");
+ 				if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
+ 					appendStringInfoString(&buf, "NOT ");
+ 				appendStringInfoString(&buf, "IN (");
+ 
+ 				/* Append list of table names within IN clause */
+ 				foreach(lc, stmt->table_list)
+ 				{
+ 					RangeVar   *rv = (RangeVar *) lfirst(lc);
+ 
+ 					if (first_item)
+ 						first_item = false;
+ 					else
+ 						appendStringInfoString(&buf, ", ");
+ 					deparseStringLiteral(&buf, rv->relname);
+ 				}
+ 				appendStringInfoString(&buf, ")");
+ 			}
+ 
+ 			/* Append ORDER BY at the end of query to ensure output ordering */
+ 			appendStringInfo(&buf, " ORDER BY c.relname, r.conname");
+ 
+ 			/* Fetch the data */
+ 			res = PQexec(conn, buf.data);
+ 			if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ 				pgfdw_report_error(ERROR, res, conn, false, buf.data);
+ 
+ 			/* Process results */
+ 			numrows = PQntuples(res);
+ 			/* note: incrementation of i happens in inner loop's while() test */
+ 			for (i = 0; i < numrows;)
+ 			{
+ 				char	   *tablename = PQgetvalue(res, i, 0);
+ 				bool		first_item = true;
+ 
+ 				resetStringInfo(&buf);
+ 				appendStringInfo(&buf, "ALTER FOREIGN TABLE %s ",
+ 								 quote_identifier(tablename));
+ 
+ 				/* Scan all rows for this table */
+ 				do
+ 				{
+ 					char	   *conname = PQgetvalue(res, i, 1);
+ 					char	   *condef = PQgetvalue(res, i, 2);
+ 
+ 					if (first_item)
+ 						first_item = false;
+ 					else
+ 						appendStringInfoString(&buf, ", ");
+ 
+ 					/* Print constraint name and definition */
+ 					appendStringInfo(&buf, "ADD CONSTRAINT %s %s",
+ 									 quote_identifier(conname),
+ 									 condef);
+ 				}
+ 				while (++i < numrows &&
+ 					   strcmp(PQgetvalue(res, i, 0), tablename) == 0);
+ 
+ 				appendStringInfoString(&buf, ";");
+ 
+ 				commands = lappend(commands, pstrdup(buf.data));
+ 			}
+ 
+ 			/* Clean up */
+ 			PQclear(res);
+ 			res = NULL;
+ 		}
  	}
  	PG_CATCH();
  	{
*** a/contrib/postgres_fdw/sql/postgres_fdw.sql
--- b/contrib/postgres_fdw/sql/postgres_fdw.sql
***************
*** 785,790 **** CREATE TABLE import_source.t2 (c1 int default 42, c2 varchar NULL, c3 text colla
--- 785,791 ----
  CREATE TYPE typ1 AS (m1 int, m2 varchar);
  CREATE TABLE import_source.t3 (c1 timestamptz default now(), c2 typ1);
  CREATE TABLE import_source."x 4" (c1 float8, "C 2" text, c3 varchar(42));
+ ALTER TABLE import_source."x 4" ADD CONSTRAINT c1positive CHECK (c1 >= 0);
  CREATE TABLE import_source."x 5" (c1 float8);
  ALTER TABLE import_source."x 5" DROP COLUMN c1;
  
***************
*** 801,807 **** IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest2
  \d import_dest2.*
  CREATE SCHEMA import_dest3;
  IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest3
!   OPTIONS (import_collate 'false', import_not_null 'false');
  \det+ import_dest3
  \d import_dest3.*
  
--- 802,808 ----
  \d import_dest2.*
  CREATE SCHEMA import_dest3;
  IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest3
!   OPTIONS (import_collate 'false', import_not_null 'false', import_check 'false');
  \det+ import_dest3
  \d import_dest3.*
  
*** a/doc/src/sgml/fdwhandler.sgml
--- b/doc/src/sgml/fdwhandler.sgml
***************
*** 709,715 **** ImportForeignSchema (ImportForeignSchemaStmt *stmt, Oid serverOid);
       called when executing <xref linkend="sql-importforeignschema">, and is
       passed the parse tree for that statement, as well as the OID of the
       foreign server to use.  It should return a list of C strings, each of
!      which must contain a <xref linkend="sql-createforeigntable"> command.
       These strings will be parsed and executed by the core server.
      </para>
  
--- 709,716 ----
       called when executing <xref linkend="sql-importforeignschema">, and is
       passed the parse tree for that statement, as well as the OID of the
       foreign server to use.  It should return a list of C strings, each of
!      which must contain a <xref linkend="sql-createforeigntable"> or
!      <xref linkend="sql-alterforeigntable"> command.
       These strings will be parsed and executed by the core server.
      </para>
  
***************
*** 736,742 **** ImportForeignSchema (ImportForeignSchemaStmt *stmt, Oid serverOid);
       The FDW may ignore the <structfield>local_schema</> field of
       the <structname>ImportForeignSchemaStmt</>, because the core server
       will automatically insert that name into the parsed <command>CREATE
!      FOREIGN TABLE</> commands.
      </para>
  
      <para>
--- 737,743 ----
       The FDW may ignore the <structfield>local_schema</> field of
       the <structname>ImportForeignSchemaStmt</>, because the core server
       will automatically insert that name into the parsed <command>CREATE
!      FOREIGN TABLE</> and <command>ALTER FOREIGN TABLE</> commands.
      </para>
  
      <para>
*** a/doc/src/sgml/postgres-fdw.sgml
--- b/doc/src/sgml/postgres-fdw.sgml
***************
*** 349,360 ****
        </para>
       </listitem>
      </varlistentry>
     </variablelist>
  
     <para>
!     Note that constraints other than <literal>NOT NULL</> will never be
!     imported from the remote tables, since <productname>PostgreSQL</>
!     does not support any other type of constraint on a foreign table.
      Checking other types of constraints is always left to the remote server.
     </para>
    </sect3>
--- 349,371 ----
        </para>
       </listitem>
      </varlistentry>
+     <varlistentry>
+      <term><literal>import_check</literal></term>
+      <listitem>
+       <para>
+        This option controls whether column and table <literal>CHECK</>
+        constraints are included in the definitions of foreign tables imported
+        from a foreign server. The default is <literal>true</>.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
  
     <para>
!     Note that constraints other than <literal>NOT NULL</> and
!     <literal>CHECK</> will never be imported from the remote tables, since
!     <productname>PostgreSQL</> does not support any other type of constraint
!     on a foreign table.
      Checking other types of constraints is always left to the remote server.
     </para>
    </sect3>
*** a/src/backend/commands/foreigncmds.c
--- b/src/backend/commands/foreigncmds.c
***************
*** 1571,1599 **** ImportForeignSchema(ImportForeignSchemaStmt *stmt)
  		 */
  		foreach(lc2, raw_parsetree_list)
  		{
! 			CreateForeignTableStmt *cstmt = lfirst(lc2);
  
  			/*
! 			 * Because we only allow CreateForeignTableStmt, we can skip parse
! 			 * analysis, rewrite, and planning steps here.
  			 */
! 			if (!IsA(cstmt, CreateForeignTableStmt))
  				elog(ERROR,
  					 "foreign-data wrapper \"%s\" returned incorrect statement type %d",
! 					 fdw->fdwname, (int) nodeTag(cstmt));
  
  			/* Ignore commands for tables excluded by filter options */
! 			if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
  				continue;
  
  			/* Enable reporting of current table's name on error */
! 			callback_arg.tablename = cstmt->base.relation->relname;
  
  			/* Ensure creation schema is the one given in IMPORT statement */
! 			cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
  
  			/* Execute statement */
! 			ProcessUtility((Node *) cstmt,
  						   cmd,
  						   PROCESS_UTILITY_SUBCOMMAND, NULL,
  						   None_Receiver, NULL);
--- 1571,1608 ----
  		 */
  		foreach(lc2, raw_parsetree_list)
  		{
! 			Node	   *parsetree = lfirst(lc2);
! 			RangeVar   *rv;
  
  			/*
! 			 * Because we only allow CreateForeignTableStmt and AlterTableStmt,
! 			 * we can skip parse analysis, rewrite, and planning steps here.
  			 */
! 			if (!IsA(parsetree, CreateForeignTableStmt) &&
! 				!(IsA(parsetree, AlterTableStmt) &&
! 				  ((AlterTableStmt *) parsetree)->relkind == OBJECT_FOREIGN_TABLE))
  				elog(ERROR,
  					 "foreign-data wrapper \"%s\" returned incorrect statement type %d",
! 					 fdw->fdwname, (int) nodeTag(parsetree));
! 
! 			/* Get RangeVar */
! 			if (!IsA(parsetree, CreateForeignTableStmt))
! 				rv = ((AlterTableStmt *) parsetree)->relation;
! 			else
! 				rv = ((CreateForeignTableStmt *) parsetree)->base.relation;
  
  			/* Ignore commands for tables excluded by filter options */
! 			if (!IsImportableForeignTable(rv->relname, stmt))
  				continue;
  
  			/* Enable reporting of current table's name on error */
! 			callback_arg.tablename = rv->relname;
  
  			/* Ensure creation schema is the one given in IMPORT statement */
! 			rv->schemaname = pstrdup(stmt->local_schema);
  
  			/* Execute statement */
! 			ProcessUtility(parsetree,
  						   cmd,
  						   PROCESS_UTILITY_SUBCOMMAND, NULL,
  						   None_Receiver, NULL);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to