Subject: [pgAdmin4] [Patch]: Foreign Table Module

From: Khushboo Vashi <[email-masked]>
Date: Tue, 23 Feb 2016 18:53:38 +0530

Hi,

Please find attached patch for the Foreign Table Module.

The patch will be modified after Types module implementation as we need to
populate Base Type  and some Type related validations from the Types module.

Please review it and let me know the feedback.

Thanks,
Khushboo
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py
new file mode 100644
index 0000000..8d6727b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py
@@ -0,0 +1,1002 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""The Foreign Table Module."""
+
+import json
+from flask import render_template, make_response, request, jsonify, \
+    current_app
+from flask.ext.babel import gettext
+from pgadmin.utils.ajax import make_json_response, \
+    make_response as ajax_response, internal_server_error
+from pgadmin.browser.utils import PGChildNodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases.schemas as schemas
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+import sys
+import traceback
+
+
+class ForeignTableModule(CollectionNodeModule):
+    """
+    class ForeignTableModule(CollectionNodeModule):
+
+        This class represents The Foreign Table Module.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Initialize the Foreign Table Module.
+
+    * get_nodes(gid, sid, did, scid)
+      - Generate the Foreign Table collection node.
+
+    * node_inode():
+      - Override this property to make the Foreign Table node as leaf node.
+
+    * script_load()
+      - Load the module script for Foreign Table, when schema node is
+        initialized.
+    """
+    NODE_TYPE = 'foreign-table'
+    COLLECTION_LABEL = gettext("Foreign Tables")
+
+    def __init__(self, *args, **kwargs):
+        super(ForeignTableModule, self).__init__(*args, **kwargs)
+        self.min_ver = None
+        self.max_ver = None
+
+    def get_nodes(self, gid, sid, did, scid):
+        """
+        Generate the Foreign Table collection node.
+        """
+        yield self.generate_browser_collection_node(scid)
+
+    @property
+    def node_inode(self):
+        """
+        Override this property to make the node as leaf node.
+        """
+        return False
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for foreign table, when the
+        schema node is initialized.
+        """
+        return schemas.SchemaModule.NODE_TYPE
+
+
+blueprint = ForeignTableModule(__name__)
+
+
+class ForeignTableView(PGChildNodeView):
+    """
+    class ForeignTableView(PGChildNodeView)
+
+    This class inherits PGChildNodeView to get the different routes for
+    the module.
+
+    The class is responsible to Create, Read, Update and Delete operations for
+    the Foreign Table.
+
+    Methods:
+    -------
+    * validate_request(f):
+      - Works as a decorator.
+        Validating request on the request of create, update and modified SQL.
+
+    * module_js():
+      - Overrides this property to define javascript for Foreign Table node.
+
+    * check_precondition(f):
+      - Works as a decorator.
+      - Checks database connection status.
+      - Attach connection object and template path.
+
+    * list(gid, sid, did, scid):
+      - List the Foreign Table.
+
+    * nodes(gid, sid, did, scid):
+      - Returns all the Foreign Table to generate Nodes in the browser.
+
+    * properties(gid, sid, did, scid, foid):
+      - Returns the Foreign Table properties.
+
+    * get_schemas(gid, sid, did, scid, foid=None):
+      - Returns Schemas for the particular database.
+
+    * get_collations(gid, sid, did, scid, foid=None):
+      - Returns Collations.
+
+    * get_types(gid, sid, did, scid, foid=None):
+      - Returns Data Types.
+
+    * get_foreign_servers(gid, sid, did, scid, foid=None):
+      - Returns the Foreign Servers.
+
+    * get_tables(gid, sid, did, scid, foid=None):
+      - Returns the Foreign Tables as well as Plain Tables.
+
+    * get_columns(gid, sid, did, scid, foid=None):
+      - Returns the Table Columns.
+
+    * create(gid, sid, did, scid):
+      - Creates a new Foreign Table object.
+
+    * update(gid, sid, did, scid, foid):
+      - Updates the Foreign Table object.
+
+    * delete(gid, sid, did, scid, foid):
+      - Drops the Foreign Table object.
+
+    * sql(gid, sid, did, scid, foid):
+      - Returns the SQL for the Foreign Table object.
+
+    * msql(gid, sid, did, scid, foid=None):
+      - Returns the modified SQL.
+
+    * get_sql(gid, sid, data, scid, foid=None):
+      - Generates the SQL statements to create/update the Foreign Table object.
+
+    * dependents(gid, sid, did, scid, foid):
+      - Returns the dependents for the Foreign Table object.
+
+    * dependencies(gid, sid, did, scid, foid):
+      - Returns the dependencies for the Foreign Table object.
+
+    * get_properties(gid, sid, did, scid, foid, inherits=False):
+      - Returns the Foreign Table properties which will be used in
+        properties, sql and get_sql functions.
+    """
+
+    node_type = blueprint.node_type
+
+    parent_ids = [
+        {'type': 'int', 'id': 'gid'},
+        {'type': 'int', 'id': 'sid'},
+        {'type': 'int', 'id': 'did'},
+        {'type': 'int', 'id': 'scid'}
+    ]
+    ids = [
+        {'type': 'int', 'id': 'foid'}
+    ]
+
+    operations = dict({
+        'obj': [
+            {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+            {'get': 'list', 'post': 'create'}
+        ],
+        'delete': [{'delete': 'delete'}],
+        'children': [{'get': 'children'}],
+        'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+        'sql': [{'get': 'sql'}],
+        'msql': [{'get': 'msql'}, {'get': 'msql'}],
+        'stats': [{'get': 'statistics'}],
+        'dependency': [{'get': 'dependencies'}],
+        'dependent': [{'get': 'dependents'}],
+        'module.js': [{}, {}, {'get': 'module_js'}],
+        'get_schemas': [{'get': 'get_schemas'}, {'get': 'get_schemas'}],
+        'get_collations': [
+            {'get': 'get_collations'},
+            {'get': 'get_collations'}
+            ],
+        'get_types': [{'get': 'get_types'}, {'get': 'get_types'}],
+        'get_foreign_servers': [{'get': 'get_foreign_servers'},
+                                {'get': 'get_foreign_servers'}],
+        'get_tables': [{'get': 'get_tables'}, {'get': 'get_tables'}],
+        'get_columns': [{'get': 'get_columns'}, {'get': 'get_columns'}]
+    })
+
+    def validate_request(f):
+        """
+        Works as a decorator.
+        Validating request on the request of create, update and modified SQL.
+
+        Required Args:
+                    name: Name of the Foreign Table
+                    ftsrvname: Foreign Server Name
+
+        Above both the arguments will not be validated in the update action.
+        """
+        @wraps(f)
+        def wrap(self, **kwargs):
+
+            data = {}
+            if request.data:
+                req = json.loads(request.data)
+            else:
+                req = request.args or request.form
+
+            if 'foid' not in kwargs:
+                required_args = [
+                    'name',
+                    'ftsrvname'
+                ]
+
+                for arg in required_args:
+                    if arg not in req or req[arg] == '':
+                        return make_json_response(
+                            status=410,
+                            success=0,
+                            errormsg=gettext(
+                                "Couldn't find the required parameter \
+                                (%s)." % arg
+                            )
+                        )
+
+            try:
+                list_params = []
+                if request.method == 'GET':
+                    list_params = ['constraints', 'columns', 'ftoptions',
+                                   'seclabels', 'inherits']
+                else:
+                    list_params = ['inherits']
+
+                for key in req:
+                    if key in list_params and req[key] != '' \
+                            and req[key] is not None:
+                        # Coverts string into python list as expected.
+                        data[key] = json.loads(req[key])
+
+                        if key == 'inherits':
+                            # Convert Table ids from unicode/string to int
+                            # and make tuple for 'IN' query.
+                            inherits = tuple([int(x) for x in data[key]])
+
+                            if len(inherits) == 1:
+                                # Python tupple has , after the first param
+                                # in case of single parameter.
+                                # So, we need to make it tuple explicitly.
+                                inherits = "(" + str(inherits[0]) + ")"
+                            if inherits:
+                                # Fetch Table Names from their respective Ids,
+                                # as we need Table names to generate the SQL.
+                                SQL = render_template(
+                                    "/".join([self.template_path,
+                                              'get_tables.sql']),
+                                    attrelid=inherits)
+                                status, res = self.conn.execute_dict(SQL)
+
+                                if not status:
+                                    return internal_server_error(errormsg=res)
+
+                                if 'inherits' in res['rows'][0]:
+                                    data[key] = res['rows'][0]['inherits']
+                                else:
+                                    data[key] = []
+
+                    elif key == 'typnotnull':
+                        data[key] = True if (req[key] == 'true' or req[key]
+                                             is True) else False if\
+                            (req[key] == 'false' or req[key]) is False else ''
+                    else:
+                        data[key] = req[key]
+
+            except Exception as e:
+                return internal_server_error(errormsg=str(e))
+
+            self.request = data
+            return f(self, **kwargs)
+
+        return wrap
+
+    def module_js(self):
+        """
+        Load JS file (foreign_tables.js) for this module.
+        """
+        return make_response(
+            render_template(
+                "foreign_tables/js/foreign_tables.js",
+                _=gettext
+            ),
+            200, {'Content-Type': 'application/x-javascript'}
+        )
+
+    def check_precondition(f):
+        """
+        Works as a decorator.
+        Checks the database connection status.
+        Attaches the connection object and template path to the class object.
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            self = args[0]
+            driver = get_driver(PG_DEFAULT_DRIVER)
+            self.manager = driver.connection_manager(kwargs['sid'])
+
+            # Get database connection
+            self.conn = self.manager.connection(did=kwargs['did'])
+
+            self.qtIdent = driver.qtIdent
+
+            if not self.conn.connected():
+                return precondition_required(
+                    gettext(
+                        "Connection to the server has been lost!"
+                    )
+                )
+
+            ver = self.manager.version
+            server_type = self.manager.server_type
+
+            # Set template path for sql scripts depending
+            # on the server version.
+
+            if ver >= 90500:
+                self.template_path = 'foreign_tables/sql/9.5_plus'
+            elif ver >= 90300:
+                self.template_path = 'foreign_tables/sql/9.3_plus'
+            else:
+                self.template_path = 'foreign_tables/sql/9.1_plus'
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+    @check_precondition
+    def list(self, gid, sid, did, scid):
+        """
+        List all the Foreign Tables.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+        """
+        SQL = render_template("/".join([self.template_path, 'node.sql']),
+                              scid=scid)
+        status, res = self.conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+        return ajax_response(
+            response=res['rows'],
+            status=200
+        )
+
+    @check_precondition
+    def nodes(self, gid, sid, did, scid):
+        """
+        Returns the Foreign Tables to generate the Nodes.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+        """
+
+        res = []
+        SQL = render_template("/".join([self.template_path,
+                                        'node.sql']), scid=scid)
+        status, rset = self.conn.execute_2darray(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        for row in rset['rows']:
+            res.append(
+                self.blueprint.generate_browser_node(
+                    row['oid'],
+                    scid,
+                    row['name'],
+                    icon="icon-foreign-table"
+                ))
+
+        return make_json_response(
+            data=res,
+            status=200
+        )
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, foid):
+        """
+        Returns the Foreign Table properties.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+        data = self.get_properties(gid, sid, did, scid, foid)
+
+        return ajax_response(
+            response=data,
+            status=200
+        )
+
+    @check_precondition
+    def get_schemas(self, gid, sid, did, scid, foid=None):
+        """
+        Returns Schemas for the particular database.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+
+        res = [{'label': '', 'value': ''}]
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_schemas.sql']), scid=scid)
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            for row in rset['rows']:
+                res.append(
+                    {'label': row['nspname'], 'value': row['nspname']}
+                )
+
+            return make_json_response(
+                data=res,
+                status=200
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_collations(self, gid, sid, did, scid, foid=None):
+        """
+        Returns the Collations.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+
+        res = [{'label': '', 'value': ''}]
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_collations.sql']))
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            for row in rset['rows']:
+                res.append(
+                    {'label': row['copy_collation'],
+                     'value': row['copy_collation']}
+                )
+
+            return make_json_response(
+                data=res,
+                status=200
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_types(self, gid, sid, did, scid, foid=None):
+        """
+        Returns the Types.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+        # TODO: This function should be removed once Types module
+        # will be completed.
+
+        res = [{'label': '', 'value': ''}]
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_types.sql']))
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            for row in rset['rows']:
+                res.append(
+                    {'label': row['typname'], 'value': row['typname']}
+                )
+            return make_json_response(
+                data=res,
+                status=200
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_foreign_servers(self, gid, sid, did, scid, foid=None):
+        """
+        Returns the Foreign Servers.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+        res = [{'label': '', 'value': ''}]
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_foreign_servers.sql']))
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            for row in rset['rows']:
+                res.append(
+                    {'label': row['srvname'], 'value': row['srvname']}
+                )
+            return make_json_response(
+                data=res,
+                status=200
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_tables(self, gid, sid, did, scid, foid=None):
+        """
+        Returns the Foreign Tables as well as Plain Tables.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+        res = []
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_tables.sql']))
+            status, rset = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                data=rset['rows'],
+                status=200
+            )
+
+        except:
+            exc_type, exc_value, exc_traceback = sys.exc_info()
+            current_app.logger.error(
+                traceback.print_exception(exc_type,
+                                          exc_value, exc_traceback, limit=2))
+
+            return internal_server_error(errormsg=str(exc_value))
+
+    @check_precondition
+    def get_columns(self, gid, sid, did, scid, foid=None):
+        """
+        Returns the Table Columns.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+            attrelid: Table oid
+
+        Returns:
+              JSON Array with below parameters.
+              attname: Column Name
+              datatype: Column Data Type
+              inherited_from: Parent Table from which the related column
+                              is inheritted.
+        """
+        res = []
+        data = request.args if request.args else None
+        try:
+            if data and 'attrelid' in data:
+                SQL = render_template("/".join([self.template_path,
+                                                'get_table_columns.sql']),
+                                      attrelid=data['attrelid'])
+                status, res = self.conn.execute_dict(SQL)
+
+                if not status:
+                    return internal_server_error(errormsg=res)
+                return make_json_response(
+                    data=res['rows'],
+                    status=200
+                )
+        except:
+            exc_type, exc_value, exc_traceback = sys.exc_info()
+            current_app.logger.error(traceback.print_exception(
+                exc_type,
+                exc_value,
+                exc_traceback,
+                limit=2
+                )
+            )
+
+            return internal_server_error(errormsg=str(exc_value))
+
+    @check_precondition
+    @validate_request
+    def create(self, gid, sid, did, scid):
+        """
+        Creates a new Foreign Table object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+            name: Foreign Table Name
+            basensp: Schema Name
+            ftsrvname: Foreign Server Name
+
+        Returns:
+            Foreign Table object in json format.
+        """
+        try:
+            # Get SQL to create Foreign Table
+            status, SQL = self.get_sql(gid, sid, did, scid, self.request)
+            if not status:
+                return internal_server_error(errormsg=SQL)
+
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # Need oid to add object in the tree at browser.
+            basensp = self.request['basensp'] if ('basensp' in self.request) \
+                else None
+            SQL = render_template("/".join([self.template_path,
+                                            'get_oid.sql']),
+                                  basensp=basensp,
+                                  name=self.request['name'],
+                                  scid=scid)
+            status, res = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            foid, scid = res['rows'][0]
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    foid,
+                    scid,
+                    self.request['name'],
+                    icon="icon-foreign-table"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, foid):
+        """
+        Drops the Foreign Table.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+
+        try:
+            # Fetch Name and Schema Name to delete the foreign table.
+            SQL = render_template("/".join([self.template_path,
+                                  'delete.sql']), scid=scid, foid=foid)
+            status, res = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            name, basensp = res['rows'][0]
+
+            SQL = render_template("/".join([self.template_path,
+                                            'delete.sql']),
+                                  name=name,
+                                  basensp=basensp,
+                                  cascade=cascade)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=gettext("Foreign Table dropped"),
+                data={
+                    'id': foid,
+                    'scid': scid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    @validate_request
+    def update(self, gid, sid, did, scid, foid):
+        """
+        Updates the Foreign Table.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+        status, SQL = self.get_sql(gid, sid, did, scid, self.request, foid)
+
+        if not status:
+            return internal_server_error(errormsg=SQL)
+
+        try:
+            if SQL and SQL.strip('\n') and SQL.strip(' '):
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                return make_json_response(
+                    success=1,
+                    info="Foreign Table updated",
+                    data={
+                        'id': foid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': foid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, foid=None):
+        """
+        Returns the SQL for the Foreign Table object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+        data = self.get_properties(gid, sid, did, scid, foid, inherits=True)
+
+        col_data = []
+        for c in data['columns']:
+            if (not 'inheritedfrom' in c) or (c['inheritedfrom'] is None):
+                col_data.append(c)
+
+        data['columns'] = col_data
+
+        SQL = render_template("/".join([self.template_path,
+                                        'create.sql']), data=data)
+
+        return ajax_response(response=SQL)
+
+    @check_precondition
+    @validate_request
+    def msql(self, gid, sid, did, scid, foid=None):
+        """
+        Returns the modified SQL.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+            name: Foreign Table Name
+            ftsrvname: Foreign Server Name
+
+        Returns:
+            SQL statements to create/update the Foreign Table.
+        """
+        status, SQL = self.get_sql(gid, sid, did, scid, self.request, foid)
+        if status:
+            return make_json_response(
+                data=SQL,
+                status=200
+            )
+        else:
+            return SQL
+
+    def get_sql(self, gid, sid, did, scid, data, foid=None):
+        """
+        Genrates the SQL statements to create/update the Foreign Table.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+        try:
+            if foid is not None:
+                old_data = self.get_properties(gid, sid, did, scid, foid,
+                                               inherits=True)
+
+                # Prepare dict of columns with key = column's attnum
+                # Will use this in the update template when any column is
+                # changed, to identify the columns.
+                col_data = {}
+                for c in old_data['columns']:
+                    col_data[c['attnum']] = c
+
+                old_data['columns'] = col_data
+
+                SQL = render_template(
+                    "/".join([self.template_path, 'update.sql']),
+                    data=data, o_data=old_data
+                )
+            else:
+                SQL = render_template("/".join([self.template_path,
+                                                'create.sql']), data=data)
+            return True, SQL
+
+        except Exception as e:
+            return False, e
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, foid):
+        """
+        This function get the dependents and return ajax response
+        for the Foreign Table object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+        dependents_result = self.get_dependents(self.conn, foid)
+        return ajax_response(
+            response=dependents_result,
+            status=200
+        )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, foid):
+        """
+        This function get the dependencies and return ajax response
+        for the  Foreign Table object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+        """
+        dependencies_result = self.get_dependencies(self.conn, foid)
+        return ajax_response(
+            response=dependencies_result,
+            status=200
+        )
+
+    def get_properties(self, gid, sid, did, scid, foid, inherits=False):
+        """
+        Returns the Foreign Table properties which will be used in
+        properties, sql and get_sql functions.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            foid: Foreign Table Id
+            inherits: If True then inherited table will be fetched from
+                      database
+
+        Returns:
+
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              scid=scid, foid=foid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+
+        SQL = render_template("/".join([self.template_path,
+                                        'get_constraints.sql']), foid=foid)
+        status, cons = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=cons)
+
+        if cons and 'rows' in cons:
+            data['constraints'] = cons['rows']
+
+        SQL = render_template("/".join([self.template_path,
+                                        'get_columns.sql']), foid=foid)
+        status, cols = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=cols)
+
+        # The Length and the precision of the Datatype should be separated.
+        # The Format we getting from database is: numeric(1,1)
+        # So, we need to separate it as Length: 1, Precision: 1
+        for c in cols['rows']:
+            if c['fulltype'] != '' and c['fulltype'].find("(") > 0:
+                substr = c['fulltype'][c['fulltype'].find("(") + 1:len(
+                    c['fulltype']) - 1]
+                typlen = substr.split(",")
+                if len(typlen) > 1:
+                    c['typlen'] = typlen[0]
+                    c['precision'] = typlen[1]
+                else:
+                    c['typlen'] = typlen
+                    c['precision'] = ''
+
+        if cols and 'rows' in cols:
+            data['columns'] = cols['rows']
+
+        # Get Inherited table names from their OID
+        if inherits:
+            if 'inherits' in data and data['inherits']:
+                inherits = tuple([int(x) for x in data['inherits']])
+                if len(inherits) == 1:
+                    inherits = "(" + str(inherits[0]) + ")"
+
+                SQL = render_template("/".join([self.template_path,
+                                                'get_tables.sql']),
+                                      attrelid=inherits)
+                status, res = self.conn.execute_dict(SQL)
+
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                if 'inherits' in res['rows'][0]:
+                    data['inherits'] = res['rows'][0]['inherits']
+
+        return data
+
+ForeignTableView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/img/coll-foreign-table.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/img/coll-foreign-table.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc1f742bbca05980f3c4cbf3c16af0e0cf9afa41
GIT binary patch
literal 492
zcmV<I0Tcd-P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp)
z=>Px#x=>71MPs@4A(iKf&i<6o`K{IR%G~PS<LBn*=jP_-#n|R{x#%#Q=!nh!Qn2!~
z)$QHg-Gj^iddB;2!1-3P^GU1lJ*Dk2p6e%>>E7PngUkI&tMEOf?I@Y)-{0Tj;^O1u
z<K^Y$hrs9H;NYjHr>v~3?Ck9B?(XU7>EYqwrlzK-sHm;2t*@`I>gwu{%Jb*v=j-e1
z=;-LFsj2Vp@A2{R?d|QVs;aQCu(GnU<mBY=@bG)a`)|PbD4FPqpT#2p0004WQchC<
zK<3zH00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA00(qQO+^RW0~{47
zCVc;%cK`qYo=HSOR2b7;&%F-9Fbqb~d^Ca9F5y=Q3JhUtmQ1dM!u!7$P76ZR<IR!f
z82rYJgGFWCa#hz2@f6zizd<`Q@H2zW$2=nuHC%$4?T(m2Al7g$=E5Y|iAXfK7sEj;
zPLi`2Jc=Qy04vsM=+&^dH+YdDJCl+cEQbyUeIH!8q}L#F1ok2?4QkNnPT(qT`XOTQ
i<DH48klNUNZ+`=M(hUmmK;RYt0000<MNUMnLSTX=Tlvud

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/img/foreign-table.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/img/foreign-table.png
new file mode 100644
index 0000000000000000000000000000000000000000..53c133885a0c0c7050e4c8580adfd75f8ceaa852
GIT binary patch
literal 563
zcmV-30?hr1P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp)
z=>Px#)lf`SMdak<A(iKi(EpOp`=ZnJy4dZ}-|64u=jG<-=H}+w;pME(=VP(xE}Q6!
z&;NzY{(i{(b;J5>zW8Cd_Exg<ORVrfr|vhP>@A$?CYb4k%>8S=_(7)bHlge;oa(gJ
z?%mzp-rnBd-{0Wi;Njun;^N}t<KyM!<%hrL=;-L_>FMX^=c%cwtgNi;?CkCB?eFjJ
z>gwvJr>Cf>sI9H7udlD`>+A6F@bdEV?(XiZtE;fEu(7eRva+)A@$vKX^Y!)hwY9bO
z_V)Pr__nsTxVX6Y_xDt#KRo~d00DGTPE!Ct=GbNc0004EOGiWihy@);00009a7bBm
z000XU000XU0RWnu7ytkO2XskIMF-mh92F=elJWmr0002WNkl<Z7}HhH-EM+F5C!0!
z!a`SOE!5&qON&%(w?ITOaT9H_G4uZK<Sexp&fR=DnamLa?nVd!4?!rS_))1*g4)CQ
zDb1cIFZpyfUkr)yYnr_=6z?B@P%f9%=W4zA`c`Iu+kWX0jT}HVKkWuJCI&TMg{nWa
ztu|yMBI+4@9u0*sBWsCaRo8o@03dXwbPVhEq?2+1l3Zsa2G!IMcR5VT$aNl?orRdF
z0LaLt4E6b!;~_UINb+ZJ+;+W@L)Soi@Pqk(|0_yz6w|&QQ{?~v002ovPDHLkV1nFh
BCnEp=

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/js/foreign_tables.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/js/foreign_tables.js
new file mode 100644
index 0000000..8258544
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/js/foreign_tables.js
@@ -0,0 +1,493 @@
+/* Create and Register Foreign Table Collection and Node. */
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+  if (!pgBrowser.Nodes['coll-foreign-table']) {
+    var foreigntable = pgAdmin.Browser.Nodes['coll-foreign-table'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'foreign-table',
+        label: '{{ _('Foreign Tables') }}',
+        type: 'coll-foreign-table',
+        columns: ['name', 'owner', 'description']
+      });
+  };
+
+  // Security Model
+  var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+    defaults: {
+      provider: null,
+      label: null
+    },
+    schema: [{
+      id: 'provider', label: '{{ _('Provider') }}',
+      type: 'text'
+    },{
+      id: 'label', label: '{{ _('Security Label') }}',
+      type: 'text'
+    }],
+    validate: function() {
+      var err = {},
+          errmsg = null,
+          data = this.toJSON();
+
+      if (_.isUndefined(data.label) ||
+        _.isNull(data.label) ||
+        String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+        return _("Please specify the value for all the security providers.");
+      }
+      return null;
+    }
+  });
+
+
+  // Columns Model
+  var ColumnsModel = pgAdmin.Browser.Node.Model.extend({
+    idAttribute: 'attname',
+    defaults: {
+      attname: undefined,
+      datatype: undefined,
+      typlen: undefined,
+      precision: undefined,
+      typdefault: undefined,
+      attnotnull: undefined,
+      collname: undefined,
+      attnum: undefined,
+      inheritedfrom: undefined,
+      inheritedid: undefined
+    },
+    schema: [
+        {id: 'attname', label:'{{ _('Name') }}', cell: 'string', type: 'text' },
+        {id: 'datatype', label:'{{ _('Data Type') }}', cell: 'string', control: 'node-ajax-options', type: 'text', url: 'get_types'},
+        {id: 'typlen', label:'{{ _('Length') }}', cell: 'string',type: 'text'},
+        {id: 'precision', label:'{{ _('Precision') }}', cell: 'string',type: 'text'},
+        {id: 'typdefault', label:'{{ _('Default') }}', type: 'text', cell: 'string', min_version: 90300},
+        {id: 'attnotnull', label:'{{ _('Not Null') }}', cell: 'boolean',type: 'switch'},
+        {id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', type: 'text', url: 'get_collations', min_version: 90300},
+        {id: 'attnum', cell: 'string',type: 'text', visible: false},
+        {id: 'inheritedfrom', label:'{{ _('Inherited From') }}', cell: 'string',type: 'text', visible: false, mode: ['properties']}
+    ],
+    validate: function() {
+      // TODO: Add validation here
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+  var formatNode = function(opt) {
+    if (!opt.id) {
+      return opt.text;
+    }
+
+    var optimage = $(opt.element).data('image');
+
+    if(!optimage){
+      return opt.text;
+    } else {
+      return $(
+          '<span><span class="wcTabIcon ' + optimage + '"/>' + opt.text + '</span>'
+          );
+    }
+  };
+
+
+  /* NodeAjaxOptionsMultipleControl is for multiple selection of Combobox.
+  *  This control is used to select Multiple Parent Tables to be inherited.
+  *  It also populates/vacates Columns on selection/deselection of the option (i.e. table name).
+  *  To populates the column, it calls the server and fetch the columns data
+  *  for the selected table.
+  */
+
+  var NodeAjaxOptionsMultipleControl = Backform.NodeAjaxOptionsMultipleControl = Backform.NodeAjaxOptionsControl.extend({
+    template: _.template([
+      '<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+      '<div class="<%=Backform.controlsClassName%> <%=extraClasses.join(\' \')%>">',
+      '  <select class="pgadmin-node-select form-control" name="<%=name%>" style="width:100%;" value=<%-value%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> >',
+      '  </select>',
+      '</div>'].join("\n")),
+    defaults: _.extend(
+      {}, Backform.NodeAjaxOptionsControl.prototype.defaults,
+      {
+        select2: {
+          allowClear: true,
+          placeholder: 'Select from the list',
+          width: 'style',
+          templateResult: formatNode,
+          templateSelection: formatNode
+        }
+      }),
+    render: function() {
+      var field = _.defaults(this.field.toJSON(), this.defaults),
+        attributes = this.model.toJSON(),
+        attrArr = field.name.split('.'),
+        name = attrArr.shift(),
+        path = attrArr.join('.'),
+        rawValue = this.keyPathAccessor(attributes[name], path),
+        data = _.extend(field, {
+          rawValue: rawValue,
+          value: this.formatter.fromRaw(rawValue, this.model),
+          attributes: attributes,
+          formatter: this.formatter
+        }),
+        evalF = function(f, d, m) {
+          return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
+        };
+
+      // Evaluate the disabled, visible, and required option
+      _.extend(data, {
+        disabled: evalF(data.disabled, data, this.model),
+        visible:  evalF(data.visible, data, this.model),
+        required: evalF(data.required, data, this.model)
+      });
+
+      // Evaluation the options
+      if (_.isFunction(data.options)) {
+        try {
+          data.options = data.options.apply(this)
+        } catch(e) {
+          // Do nothing
+          data.options = []
+          this.model.trigger('pgadmin-view:transform:error', m, self.field, e);
+        }
+      }
+
+      // Clean up first
+      this.$el.removeClass(Backform.hiddenClassname);
+      this.$el.html(this.template(data)).addClass(field.name);
+
+      if (!data.visible) {
+        this.$el.addClass(Backform.hiddenClassname);
+      } else {
+        var opts = _.extend(
+          {}, this.defaults.select2, data.select2,
+          {
+            'data': data.options
+          });
+        this.$el.find("select").select2(opts).val(data.rawValue).trigger("change");
+        this.updateInvalid();
+      }
+
+      return this;
+    },
+    onChange: function(e) {
+      var model = this.model,
+          $el = $(e.target),
+          attrArr = this.field.get("name").split('.'),
+          name = attrArr.shift(),
+          path = attrArr.join('.'),
+          value = this.getValueFromDOM(),
+          changes = {},
+          col_changes = {},
+          col_model = _.clone(model.get('columns'));
+          col_changes['columns'] = col_model.toJSON();
+
+      if (this.model.errorModel instanceof Backbone.Model) {
+        if (_.isEmpty(path)) {
+          this.model.errorModel.unset(name);
+        } else {
+          var nestedError = this.model.errorModel.get(name);
+          if (nestedError) {
+            this.keyPathSetter(nestedError, path, null);
+            this.model.errorModel.set(name, nestedError);
+          }
+        }
+      }
+
+      var m = model.get(name),
+          self = this;
+
+      if (typeof(m)  == "string"){ m = JSON.parse(m); }
+
+      // Remove Columns if option is deselected from the combobox
+      if(_.size(JSON.parse(value)) < _.size(m)) {
+        var dif = _.difference(m, JSON.parse(value));
+        cols = _.filter(col_model.toJSON(), function(o) {
+                 return o["inheritedid"] != dif[0]
+               });
+        col_changes['columns'] = cols;
+      }
+      // Populate the Columns on selection of the combobox
+      else {
+         _.each(JSON.parse(value), function(i) {
+           if (! _.contains(m, parseInt(i))){
+             // Fetch Columns from server
+             columns = self.fetchColumns(i);
+             col_changes['columns'] = _.union(col_changes['columns'],columns);
+           }
+         });
+      }
+
+      // Set model for this control and also for columns
+      col_model.set(col_changes['columns']);
+      changes[name] = _.isEmpty(path) ? value : _.clone(model.get(name)) || {};
+
+      if (!_.isEmpty(path)) this.keyPathSetter(changes[name], path, value);
+
+      this.stopListening(this.model, "change:" + name, this.render);
+      model.set(changes);
+
+      this.listenTo(this.model, "change:" + name, this.render);
+    },
+    fetchColumns: function(table_id){
+      var self = this,
+          url = 'get_columns',
+          m = self.model.top || self.model;
+
+      if (url) {
+        var node = this.field.get('schema_node'),
+            node_info = this.field.get('node_info'),
+            full_url = node.generate_url.apply(
+              node, [
+                null, url, this.field.get('node_data'),
+                this.field.get('url_with_id') || false, node_info
+              ]),
+            cache_level = this.field.get('cache_level') || node.type,
+            cache_node = this.field.get('cache_node');
+
+        cache_node = (cache_node && pgAdmin.Browser.Nodes['cache_node']) || node;
+
+        m.trigger('pgadmin:view:fetching', m, self.field);
+        data = {attrelid: table_id}
+
+        // Fetching Columns data for the selected table.
+        $.ajax({
+          async: false,
+          url: full_url,
+          data: data,
+          success: function(res) {
+            /*
+             * We will cache this data for short period of time for avoiding
+             * same calls.
+             */
+            data = cache_node.cache(url, node_info, cache_level, res.data);
+          },
+          error: function() {
+            m.trigger('pgadmin:view:fetch:error', m, self.field);
+          }
+        });
+        m.trigger('pgadmin:view:fetched', m, self.field);
+
+        // To fetch only options from cache, we do not need time from 'at'
+        // attribute but only options.
+        //
+        // It is feasible that the data may not have been fetched.
+        data = (data && data.data) || [];
+        return data;
+      }
+    },
+  });
+
+
+  // Constraints Model
+  var ConstraintModel = pgAdmin.Browser.Node.Model.extend({
+    idAttribute: 'conname',
+    defaults: {
+      conname: undefined,
+      consrc: undefined,
+      connoinherit: undefined
+    },
+    schema: [
+        {id: 'conname', label:'{{ _('Name') }}', type: 'text', cell: 'string', group: 'Definition'},
+        {id: 'consrc', label:'{{ _('Check') }}', type: 'multiline', cell: 'string', group: 'Definition'},
+        {id: 'connoinherit', label:'{{ _('No Inherit') }}', type: 'switch', cell: 'boolean', group: 'Definition'}
+
+    ],
+    validate: function() {
+      // TODO: Add validation here
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+
+  // Options Model
+  var OptionsModel = pgAdmin.Browser.Node.Model.extend({
+    defaults: {
+      option: undefined,
+      value: undefined
+    },
+    schema: [
+        {id: 'option', label:'{{ _('Option') }}', cell: 'string', type: 'text'},
+        {id: 'value', label:'{{ _('Value') }}', cell: 'string',type: 'text'}
+    ],
+    validate: function() {
+      // TODO: Add validation here
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+
+  if (!pgBrowser.Nodes['foreign-table']) {
+    pgAdmin.Browser.Nodes['foreign-table'] = pgBrowser.Node.extend({
+      type: 'foreign-table',
+      label: '{{ _('Foreign Table') }}',
+      collection_type: 'coll-foreign-table',
+      hasSQL: true,
+      hasDepends: true,
+      parent_type: ['schema'],
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_foreign-table_on_coll', node: 'coll-foreign-table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Foreign Table...') }}',
+          icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_foreign-table', node: 'foreign-table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Foreign Table...') }}',
+          icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_foreign-table', node: 'schema', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Foreign Table...') }}',
+          icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: false},
+          enable: 'canCreate'
+        }
+        ]);
+
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          owner: undefined,
+          basensp: undefined,
+          description: undefined,
+          ftsrvname: undefined,
+          inherits: [],
+          columns: [],
+          constraints: [],
+          ftoptions: [],
+          seclabels: []
+        },
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', cell: 'string',
+          type: 'text', mode: ['properties', 'create', 'edit']
+        },{
+          id: 'oid', label:'{{ _('Oid') }}', cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl,
+          node: 'role',  type: 'text', mode: ['edit', 'properties']
+        },{
+          id: 'basensp', label:'{{ _('Schema') }}', cell: 'string', control: 'node-ajax-options',
+          type: 'text', url: 'get_schemas', mode: ['edit', 'create', 'properties'],
+        },{
+          id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline'
+        },{
+          id: 'ftsrvname', label:'{{ _('Foreign Server') }}', cell: 'string', control: 'node-ajax-options',
+          type: 'text', group: 'Definition', url: 'get_foreign_servers', disabled: function(m) { return !m.isNew(); }
+        },{
+          id: 'inherits', label:'{{ _('Inherits') }}', cell: 'string', group: 'Definition',
+          type: 'list', min_version: 90500, control: 'node-ajax-options-multiple',
+          url: 'get_tables', select2: {multiple: true},
+          'cache_level': 'database'
+        },{
+          id: 'columns', label:'{{ _('Columns') }}', cell: 'string',
+          type: 'collection', group: 'Columns', visible: false, mode: ['edit', 'create', 'properties'],
+          model: ColumnsModel, canAdd: true, canDelete: true, canEdit: true,
+          columns: ['attname', 'datatype', 'typlen', 'precision', 'typdefault', 'attnotnull', 'collname', 'inheritedfrom']
+        },
+        {
+          id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string',
+          type: 'collection', group: 'Constraints', visible: false, mode: ['edit', 'create', 'properties'],
+          model: ConstraintModel, canAdd: true, canDelete: true, canEdit: function(o) {
+            if (o instanceof Backbone.Model) {
+              if (o instanceof ConstraintModel) {
+                return o.isNew();
+              }
+            }
+            return true;
+          }, min_version: 90500
+        },{
+          id: 'ftoptions', label:'{{ _('Options') }}', cell: 'string',
+          type: 'collection', group: 'Options', visible: false, mode: ['edit', 'create', 'properties'],
+          model: OptionsModel, canAdd: true, canDelete: true, canEdit: true
+        },{
+          id: 'seclabels', label: '{{ _('Security Labels') }}',
+          model: SecurityModel, type: 'collection',
+          group: '{{ _('Security') }}', mode: ['edit', 'create'],
+          min_version: 90100, canAdd: true,
+          canEdit: true, canDelete: true
+        }
+        ],
+        validate: function()
+        {
+          var err = {},
+              errmsg,
+              seclabels = this.get('seclabels');
+
+          if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            err['name'] = '{{ _('Name can not be empty!') }}';
+            errmsg = errmsg || err['name'];
+          }
+
+          if (_.isUndefined(this.get('ftsrvname')) || String(this.get('ftsrvname')).replace(/^\s+|\s+$/g, '') == '') {
+            err['ftsrvname'] = '{{ _('Foreign Server can not be empty!') }}';
+            errmsg = errmsg || err['ftsrvname'];
+          }
+
+          if (seclabels) {
+            var secLabelsErr;
+            for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) {
+              secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]);
+              if (secLabelsErr) {
+                err['seclabels'] = secLabelsErr;
+                errmsg = errmsg || secLabelsErr;
+              }
+            }
+          }
+
+          this.errorModel.clear().set(err);
+
+          if (_.size(err)) {
+            this.trigger('on-status', {msg: errmsg});
+            return errmsg;
+          }
+
+          return null;
+        }
+      }),
+      canCreate: function(itemData, item, data) {
+        //If check is false then , we will allow create menu
+        if (data && data.check == false)
+          return true;
+
+        var t = pgBrowser.tree, i = item, d = itemData;
+        // To iterate over tree to check parent node
+        while (i) {
+          // If it is schema then allow user to create foreign table
+          if (_.indexOf(['schema'], d._type) > -1)
+            return true;
+
+          if ('coll-foreign-table' == d._type) {
+            //Check if we are not child of catalog
+            prev_i = t.hasParent(i) ? t.parent(i) : null;
+            prev_d = prev_i ? t.itemData(prev_i) : null;
+            if( prev_d._type == 'catalog') {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          i = t.hasParent(i) ? t.parent(i) : null;
+          d = i ? t.itemData(i) : null;
+        }
+        // by default we do not want to allow create menu
+        return true;
+      }
+  });
+
+  }
+
+  return pgBrowser.Nodes['foreign-table'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..00df296
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/create.sql
@@ -0,0 +1,37 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% if data %}
+CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+   ({% if data.columns %}
+{% for c in data.columns %}
+{% if not loop.first%}    {% endif %}{{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
+{% if not loop.last %},
+{% endif %}
+{% endfor %}
+{% endif %}
+)
+    SERVER {{ conn|qtIdent(data.ftsrvname) }};
+
+{% if data.ftoptions %}
+{% for o in data.ftoptions %}
+{% if o.option and o.value %}
+{% if loop.first %}OPTIONS ( {% endif %}
+{% if not loop.first %}, {% endif %}
+{{o.option}} {{o.value|qtLiteral}}  {% if loop.last %}){% endif %}
+{% endif %}
+{% endfor %}
+{% endif -%}
+
+{% if data.description %}
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+  IS '{{ data.description }}';
+{% endif -%}
+
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.label and r.provider %}
+{{ SECLABLE.APPLY(conn, 'FOREIGN TABLE', data.name, r.provider, r.label) }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..b1bc53d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/delete.sql
@@ -0,0 +1,15 @@
+{% if scid and foid %}
+SELECT
+    c.relname AS name, nspname as basensp
+FROM
+    pg_class c
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+    AND c.oid = {{foid}}::oid;
+{% endif %}
+
+{% if name %}
+DROP FOREIGN TABLE {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..e59c17d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,10 @@
+SELECT --nspname, collname,
+    CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+    concat(nspname, '."', collname,'"')
+    ELSE '' END AS copy_collation
+FROM
+    pg_collation c, pg_namespace n
+WHERE
+    c.collnamespace=n.oid
+ORDER BY
+    nspname, collname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_columns.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_columns.sql
new file mode 100644
index 0000000..0733a3c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_columns.sql
@@ -0,0 +1,16 @@
+SELECT
+    attname, attndims, atttypmod, format_type(t.oid,NULL) AS datatype,
+    format_type(t.oid, att.atttypmod) AS fulltype, attnotnull, attnum,
+    (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup
+FROM
+    pg_attribute att
+JOIN
+    pg_type t ON t.oid=atttypid
+JOIN
+    pg_namespace nsp ON t.typnamespace=nsp.oid
+LEFT OUTER JOIN
+    pg_type b ON t.typelem=b.oid
+WHERE
+    att.attrelid={{foid}}::oid
+    AND attnum>0
+ORDER by attnum;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_constraints.sql
new file mode 100644
index 0000000..fc93549
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_constraints.sql
@@ -0,0 +1,7 @@
+SELECT
+    conname, contype, consrc
+FROM
+    pg_constraint
+WHERE
+    conrelid={{foid}}::oid
+ORDER by conname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_foreign_servers.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_foreign_servers.sql
new file mode 100644
index 0000000..1980f02
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_foreign_servers.sql
@@ -0,0 +1,5 @@
+SELECT
+    srvname
+FROM
+    pg_foreign_server
+ORDER BY srvname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..561434b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,9 @@
+SELECT
+    c.oid, c.relname as name
+FROM
+    pg_class c
+JOIN
+    pg_namespace bn ON bn.oid=c.relnamespace
+WHERE
+    bn.nspname = {{ basensp|qtLiteral }}
+    AND c.relname={{ name|qtLiteral }}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_schemas.sql
new file mode 100644
index 0000000..bf87a37
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_schemas.sql
@@ -0,0 +1,23 @@
+SELECT
+    nsp.nspname
+FROM
+    pg_namespace nsp
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
+LEFT OUTER JOIN
+    pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid)
+WHERE
+    NOT ((nspname = 'pg_catalog' AND EXISTS
+        (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR
+    (nspname = 'pgagent' AND EXISTS
+        (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR
+    (nspname = 'information_schema' AND EXISTS
+        (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR
+    (nspname LIKE '_%' AND EXISTS
+      (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1))
+    ) AND
+    nspname NOT LIKE E'pg\\temp\\%' AND
+    nspname NOT LIKE E'pg\\toast_temp\\%' AND
+    -- ADDED: Because We need to omit system schema except the one on which we are trying to  create collation
+    ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' )
+ORDER BY nspname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_types.sql
new file mode 100644
index 0000000..72cfa26
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_types.sql
@@ -0,0 +1,17 @@
+SELECT
+    *
+FROM
+    (SELECT format_type(t.oid,NULL) AS typname, CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname,
+    (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+    FROM
+        pg_type t
+    JOIN
+        pg_namespace nsp ON typnamespace=nsp.oid
+    WHERE
+        (NOT (typname = 'unknown' AND nspname = 'pg_catalog'))
+        AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')
+        AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c')
+        AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c'))
+    ) AS dummy
+ORDER BY
+    nspname <> 'pg_catalog', nspname <> 'public', nspname, 1;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/node.sql
new file mode 100644
index 0000000..2d3a38b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/node.sql
@@ -0,0 +1,12 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, nspname as basensp
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..3561380
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/properties.sql
@@ -0,0 +1,21 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, srvname AS ftsrvname, description, nspname as basensp,
+    (SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid) AS labels,
+    (SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=c.oid) AS providers
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_foreign_server fs ON ft.ftserver=fs.oid
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=c.oid AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+    {% if foid %}
+    AND c.oid = {{foid}}::oid
+    {% endif %}
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..cf9056b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/update.sql
@@ -0,0 +1,103 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}
+{% if data.name != o_data.name %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, o_data.name) }}
+RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}
+{% endif -%}
+
+{% if data.owner %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OWNER TO {{ data.owner }};
+{% endif -%}
+
+{% if data.columns %}
+{% for c in data.columns.deleted %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP COLUMN {{conn|qtIdent(c.attname)}};
+{% endfor -%}
+
+{% for c in data.columns.added %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL {% else %} NULL {% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault|qtLiteral}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endfor -%}
+
+{% for c in data.columns.changed %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['attname'])}};
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL {% else %} NULL {% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault|qtLiteral}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endfor %}
+{% endif -%}
+
+
+{% if data.ftoptions %}
+{% for o in data.ftoptions.deleted %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS ( DROP {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+
+{% for o in data.ftoptions.added %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS (ADD {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+
+{% for o in data.ftoptions.changed %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS (SET {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+{% endif -%}
+
+
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'FOREIGN TABLE', conn|qtIdent(o_data.basensp, name), r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'FOREIGN TABLE', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'FOREIGN TABLE', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }}
+{% endfor %}
+{% endif -%}
+
+{% if data.description %}
+
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    IS {{ data.description|qtLiteral }};
+{% endif -%}
+
+{% if data.basensp %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+SET SCHEMA {{ conn|qtIdent(data.basensp) }};
+{% endif %}
+
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/create.sql
new file mode 100644
index 0000000..67c44c1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/create.sql
@@ -0,0 +1,38 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% if data %}
+CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+   ({% if data.columns %}
+{% for c in data.columns %}
+{% if not loop.first%}    {% endif %}{{conn|qtIdent(c.attname)}} {{
+conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.attnotnull %}
+NOT NULL {% else %} NULL{% endif %}{% if c.typdefault %}
+DEFAULT {{c.typdefault|qtLiteral}}{% endif %}{% if c.collname %}
+COLLATE{{c.collname}}{% endif %}
+{% if not loop.last %},
+{% endif %}
+{% endfor -%}{% endif -%})
+    SERVER {{ conn|qtIdent(data.ftsrvname) }};
+
+{% if data.ftoptions %}
+{% for o in data.ftoptions %}
+{% if o.option and o.value %}
+{% if loop.first %}OPTIONS ( {% endif %}
+{% if not loop.first %}, {% endif %}
+{{o.option}} {{o.value|qtLiteral}} {% if loop.last %}){% endif %}
+{% endif %}
+{% endfor %}
+{% endif -%}
+
+{% if data.description %}
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+    IS '{{ data.description }}';
+{% endif -%}
+
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.label and r.provider %}
+{{ SECLABLE.APPLY(conn, 'FOREIGN TABLE', data.name, r.provider, r.label) }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/delete.sql
new file mode 100644
index 0000000..5bb0969
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/delete.sql
@@ -0,0 +1,17 @@
+{% if scid and foid %}
+SELECT
+    c.relname AS name, nspname as basensp
+FROM
+    pg_class c
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+AND
+    c.oid = {{foid}}::oid;
+{% endif %}
+
+
+{% if name %}
+DROP FOREIGN TABLE {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_collations.sql
new file mode 100644
index 0000000..14c3b2e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_collations.sql
@@ -0,0 +1,9 @@
+SELECT --nspname, collname,
+  CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+  concat(nspname, '."', collname,'"')
+  ELSE '' END AS copy_collation
+FROM
+    pg_collation c, pg_namespace n
+WHERE
+    c.collnamespace=n.oid
+ORDER BY nspname, collname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_columns.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_columns.sql
new file mode 100644
index 0000000..c6cfa97
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_columns.sql
@@ -0,0 +1,22 @@
+SELECT
+    attname, attndims, atttypmod, format_type(t.oid,NULL) AS datatype,
+    attnotnull, attnum, format_type(t.oid, att.atttypmod) AS fulltype,
+    CASE WHEN length(cn.nspname) > 0 AND length(cl.collname) > 0 THEN
+    concat(cn.nspname, '."', cl.collname,'"') ELSE '' END AS collname,
+    (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup
+FROM
+    pg_attribute att
+JOIN
+    pg_type t ON t.oid=atttypid
+JOIN
+    pg_namespace nsp ON t.typnamespace=nsp.oid
+LEFT OUTER JOIN
+    pg_type b ON t.typelem=b.oid
+LEFT OUTER JOIN
+    pg_collation cl ON t.typcollation=cl.oid
+LEFT OUTER JOIN
+    pg_namespace cn ON cl.collnamespace=cn.oid
+WHERE
+    att.attrelid={{foid}}::oid
+    AND attnum>0
+ORDER by attnum;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_constraints.sql
new file mode 100644
index 0000000..fc93549
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_constraints.sql
@@ -0,0 +1,7 @@
+SELECT
+    conname, contype, consrc
+FROM
+    pg_constraint
+WHERE
+    conrelid={{foid}}::oid
+ORDER by conname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_foreign_servers.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_foreign_servers.sql
new file mode 100644
index 0000000..1c95ce5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_foreign_servers.sql
@@ -0,0 +1,6 @@
+SELECT
+    srvname
+FROM
+    pg_foreign_server
+ORDER BY
+    srvname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_oid.sql
new file mode 100644
index 0000000..561434b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_oid.sql
@@ -0,0 +1,9 @@
+SELECT
+    c.oid, c.relname as name
+FROM
+    pg_class c
+JOIN
+    pg_namespace bn ON bn.oid=c.relnamespace
+WHERE
+    bn.nspname = {{ basensp|qtLiteral }}
+    AND c.relname={{ name|qtLiteral }}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_schemas.sql
new file mode 100644
index 0000000..6b92d37
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_schemas.sql
@@ -0,0 +1,23 @@
+SELECT
+    nsp.nspname
+FROM
+    pg_namespace nsp
+ LEFT OUTER JOIN
+    pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
+ LEFT OUTER JOIN
+    pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid)
+WHERE
+    NOT ((nspname = 'pg_catalog' AND EXISTS
+        (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR
+    (nspname = 'pgagent' AND EXISTS
+        (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR
+    (nspname = 'information_schema' AND EXISTS
+        (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR
+    (nspname LIKE '_%' AND EXISTS
+        (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1))
+  )AND
+  nspname NOT LIKE E'pg\\temp\\%' AND
+  nspname NOT LIKE E'pg\\toast_temp\\%' AND
+  -- ADDED: Because We need to omit system schema except the one on which we are trying to  create collation
+  ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' )
+ORDER BY nspname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_types.sql
new file mode 100644
index 0000000..1f57a85
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_types.sql
@@ -0,0 +1,15 @@
+SELECT
+    *
+FROM
+    (
+     SELECT format_type(t.oid,NULL) AS typname, CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname,
+     (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+    FROM
+        pg_type t
+    JOIN
+        pg_namespace nsp ON typnamespace=nsp.oid
+    WHERE
+        (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c'))
+    ) AS dummy
+ORDER BY
+    nspname <> 'pg_catalog', nspname <> 'public', nspname, 1;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/node.sql
new file mode 100644
index 0000000..2d3a38b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/node.sql
@@ -0,0 +1,12 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, nspname as basensp
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/properties.sql
new file mode 100644
index 0000000..8f6cedc
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/properties.sql
@@ -0,0 +1,23 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, srvname AS ftsrvname, description, nspname as basensp, consrc,
+    (SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid) AS labels,
+    (SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=c.oid) AS providers
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_foreign_server fs ON ft.ftserver=fs.oid
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=c.oid AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+LEFT OUTER JOIN
+    pg_constraint cn ON (cn.conrelid=c.oid)
+WHERE
+    c.relnamespace = {{scid}}::oid
+    {% if foid %}
+    AND c.oid = {{foid}}::oid
+    {% endif %}
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/update.sql
new file mode 100644
index 0000000..385c4fa
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/update.sql
@@ -0,0 +1,116 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}
+{% if data.name != o_data.name %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, o_data.name) }}
+RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}
+{% endif -%}
+
+{% if data.owner %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OWNER TO {{ data.owner }};
+{% endif -%}
+
+{% if data.columns %}
+{% for c in data.columns.deleted %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP COLUMN {{conn|qtIdent(c.attname)}};
+{% endfor -%}
+
+{% for c in data.columns.added %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL {% else %} NULL {% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault|qtLiteral}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endfor -%}
+
+{% for c in data.columns.changed %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['attname'])}};
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL {% else %} NULL {% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault|qtLiteral}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endfor -%}
+
+{% endif -%}
+
+{% if data.constraints %}
+{% for c in data.constraints.deleted %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP CONSTRAINT {{conn|qtIdent(c.conname)}};
+{% endfor %}
+
+{% for c in data.constraints.added %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD CONSTRAINT {{conn|qtIdent(c.conname)}} {% if c.consrc %} CHECK ({{c.consrc}}){% endif %} {% if c.connoinherit %} NO INHERIT{% endif %};
+{% endfor %}
+{% endif -%}
+
+{% if data.ftoptions %}
+{% for o in data.ftoptions.deleted %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS ( DROP {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+
+{% for o in data.ftoptions.added %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS (ADD {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+
+{% for o in data.ftoptions.changed %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS (SET {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+{% endif -%}
+
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'FOREIGN TABLE', conn|qtIdent(o_data.basensp, name), r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'FOREIGN TABLE', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'FOREIGN TABLE', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }}
+{% endfor %}
+{% endif -%}
+
+{% if data.description %}
+
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    IS {{ data.description|qtLiteral }};
+{% endif -%}
+
+{% if data.basensp %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+SET SCHEMA {{ conn|qtIdent(data.basensp) }};
+{% endif %}
+
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/create.sql
new file mode 100644
index 0000000..612df40
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/create.sql
@@ -0,0 +1,57 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% set is_columns = [] %}
+{% if data %}
+CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+   ({% if data.columns %}
+{% for c in data.columns %}
+{% if (c.inheritedfrom =='' or  c.inheritedfrom == None or  c.inheritedfrom == 'None' ) %}
+{% if is_columns.append('1') %}{% endif %}
+{% if not loop.first%}    {% endif %}{{conn|qtIdent(c.attname)}} {{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
+{% if c.typdefault %} DEFAULT{{c.typdefault|qtLiteral}}{% endif %}
+{% if c.collname %} COLLATE{{c.collname}}{% endif %}
+{% if not loop.last %},
+{% endif %}
+{% endif %}
+{% endfor -%}
+
+{% if data.constraints %}
+{% for c in data.constraints %}
+{% if is_columns|length > 0 and loop.first %},
+{% endif %}
+    CONSTRAINT {{conn|qtIdent(c.conname)}}{% if c.consrc %} CHECK ({{c.consrc}}){% endif %}{% if c.connoinherit %} NO INHERIT{% endif %}
+{% if not loop.last %},
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif -%}
+)
+
+{% if data.inherits %}
+    INHERITS ({% for i in data.inherits %}{% if i %}{{i}}{% if not loop.last %}, {% endif %}{% endif %}{% endfor %})
+{% endif %}
+    SERVER {{ conn|qtIdent(data.ftsrvname) }};
+
+{% if data.ftoptions %}
+{% for o in data.ftoptions %}
+{% if o.option and o.value %}
+{% if loop.first %}OPTIONS ( {% endif %}
+{% if not loop.first %}, {% endif %}
+{{o.option}} {{o.value|qtLiteral}} {% if loop.last %}){% endif %}
+{% endif %}
+{% endfor %}
+{% endif -%}
+
+{% if data.description %}
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+    IS '{{ data.description }}';
+{% endif -%}
+
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.label and r.provider %}
+{{ SECLABLE.APPLY(conn, 'FOREIGN TABLE', data.name, r.provider, r.label) }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/delete.sql
new file mode 100644
index 0000000..5bb0969
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/delete.sql
@@ -0,0 +1,17 @@
+{% if scid and foid %}
+SELECT
+    c.relname AS name, nspname as basensp
+FROM
+    pg_class c
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+AND
+    c.oid = {{foid}}::oid;
+{% endif %}
+
+
+{% if name %}
+DROP FOREIGN TABLE {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_collations.sql
new file mode 100644
index 0000000..e8d274b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_collations.sql
@@ -0,0 +1,9 @@
+SELECT --nspname, collname,
+    CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+    concat(nspname, '."', collname,'"')
+    ELSE '' END AS copy_collation
+FROM
+    pg_collation c, pg_namespace n
+WHERE
+    c.collnamespace=n.oid
+ORDER BY nspname, collname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_columns.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_columns.sql
new file mode 100644
index 0000000..dda5a2b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_columns.sql
@@ -0,0 +1,34 @@
+SELECT
+    att.attname, att.attndims, att.atttypmod, format_type(t.oid,NULL) AS datatype,
+    att.attnotnull, att.attnum, format_type(t.oid, att.atttypmod) AS fulltype,
+    CASE WHEN length(cn.nspname) > 0 AND length(cl.collname) > 0 THEN
+    concat(cn.nspname, '."', cl.collname,'"')
+    ELSE '' END AS collname,
+    (SELECT concat(nmsp_parent.nspname, '.',parent.relname ) as inheritedfrom
+FROM
+    pg_attribute at
+JOIN
+    pg_inherits ph ON ph.inhparent = at.attrelid AND ph.inhrelid =  {{foid}}::oid
+JOIN
+    pg_class parent ON ph.inhparent  = parent.oid
+JOIN
+    pg_namespace nmsp_parent ON nmsp_parent.oid  = parent.relnamespace
+    WHERE at.attname = att.attname
+    ),
+    (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup
+FROM
+    pg_attribute att
+JOIN
+    pg_type t ON t.oid=atttypid
+JOIN
+    pg_namespace nsp ON t.typnamespace=nsp.oid
+LEFT OUTER JOIN
+    pg_type b ON t.typelem=b.oid
+LEFT OUTER JOIN
+    pg_collation cl ON t.typcollation=cl.oid
+LEFT OUTER JOIN
+    pg_namespace cn ON cl.collnamespace=cn.oid
+WHERE
+    att.attrelid={{foid}}::oid
+    AND att.attnum>0
+ORDER by att.attnum;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_constraints.sql
new file mode 100644
index 0000000..fc93549
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_constraints.sql
@@ -0,0 +1,7 @@
+SELECT
+    conname, contype, consrc
+FROM
+    pg_constraint
+WHERE
+    conrelid={{foid}}::oid
+ORDER by conname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_foreign_servers.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_foreign_servers.sql
new file mode 100644
index 0000000..9ec3c2f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_foreign_servers.sql
@@ -0,0 +1,6 @@
+SELECT
+  srvname
+FROM
+  pg_foreign_server
+ORDER
+  BY srvname
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_oid.sql
new file mode 100644
index 0000000..e5be5fe
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_oid.sql
@@ -0,0 +1,21 @@
+{% if basensp %}
+SELECT
+    c.oid, bn.oid as scid
+FROM
+    pg_class c
+JOIN
+    pg_namespace bn ON bn.oid=c.relnamespace
+WHERE
+    bn.nspname = {{ basensp|qtLiteral }}
+    AND c.relname={{ name|qtLiteral }}
+{% else %}
+SELECT
+    c.oid, bn.oid as scid
+FROM
+    pg_class c
+JOIN
+    pg_namespace bn ON bn.oid=c.relnamespace
+WHERE
+    c.relname={{ name|qtLiteral }}
+    AND bn.oid = {{scid}}::oid
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_schemas.sql
new file mode 100644
index 0000000..ba3793c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_schemas.sql
@@ -0,0 +1,26 @@
+SELECT
+    nsp.nspname
+FROM
+    pg_namespace nsp
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
+LEFT OUTER JOIN
+    pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid)
+WHERE
+    NOT (
+    (nspname = 'pg_catalog' AND EXISTS
+    (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR
+    (nspname = 'pgagent' AND EXISTS
+    (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR
+    (nspname = 'information_schema' AND EXISTS
+    (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR
+    (nspname LIKE '_%' AND EXISTS
+      (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1))
+    )
+    AND
+    nspname NOT LIKE E'pg\\temp\\%' AND
+    nspname NOT LIKE E'pg\\toast_temp\\%' AND
+    -- ADDED: Because We need to omit system schema except the one on which we are trying to  create collation
+    ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' )
+ORDER BY
+    nspname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_table_columns.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_table_columns.sql
new file mode 100644
index 0000000..e1882db
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_table_columns.sql
@@ -0,0 +1,14 @@
+{% if attrelid  %}
+SELECT
+    a.attname, format_type(a.atttypid, NULL) AS datatype,
+    quote_ident(n.nspname)||'.'||quote_ident(c.relname) as inheritedfrom,
+    c.oid as inheritedid
+FROM
+    pg_class c
+JOIN
+    pg_namespace n ON c.relnamespace=n.oid
+JOIN
+    pg_attribute a ON a.attrelid = c.oid AND NOT a.attisdropped AND a.attnum>0
+WHERE
+    c.oid = {{attrelid}}::OID
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_tables.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_tables.sql
new file mode 100644
index 0000000..3085a26
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_tables.sql
@@ -0,0 +1,19 @@
+{% if attrelid  %}
+SELECT
+    array_agg(quote_ident(n.nspname) || '.' || quote_ident(c.relname)) as inherits
+FROM
+    pg_class c, pg_namespace n
+WHERE
+    c.relnamespace=n.oid AND c.relkind IN ('r', 'f')
+    AND c.oid in {{attrelid}};
+
+{% else %}
+SELECT
+    c.oid AS id, quote_ident(n.nspname) || '.' || quote_ident(c.relname) as text
+FROM
+    pg_class c, pg_namespace n
+WHERE
+    c.relnamespace=n.oid AND c.relkind IN ('r', 'f')
+ORDER BY
+    n.nspname, c.relname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_types.sql
new file mode 100644
index 0000000..5f6c4fc
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_types.sql
@@ -0,0 +1,16 @@
+SELECT
+    *
+FROM
+    (
+        SELECT format_type(t.oid,NULL) AS typname, CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname,
+        (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+         FROM
+            pg_type t
+         JOIN
+                pg_namespace nsp ON typnamespace=nsp.oid
+         WHERE
+         (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND
+            typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c'))
+     ) AS dummy
+ORDER BY
+    nspname <> 'pg_catalog', nspname <> 'public', nspname, 1;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/node.sql
new file mode 100644
index 0000000..2d3a38b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/node.sql
@@ -0,0 +1,12 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, nspname as basensp
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/properties.sql
new file mode 100644
index 0000000..22a69d6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/properties.sql
@@ -0,0 +1,24 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, srvname AS ftsrvname, description, nspname as basensp,
+    (SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid) AS labels,
+    (SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=c.oid) AS providers {% if foid %},
+    (SELECT array_agg(i.inhparent) FROM pg_inherits i
+WHERE
+    i.inhrelid = {{foid}}::oid GROUP BY i.inhrelid) as inherits {% endif %}
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_foreign_server fs ON ft.ftserver=fs.oid
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=c.oid AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+    {% if foid %}
+    AND c.oid = {{foid}}::oid
+    {% endif %}
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/update.sql
new file mode 100644
index 0000000..51453b0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/update.sql
@@ -0,0 +1,136 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}
+{% if data.name != o_data.name %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, o_data.name) }}
+RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}
+{% endif -%}
+
+{% if data.owner %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OWNER TO {{ data.owner }};
+{% endif -%}
+
+{% if data.columns %}
+{% for c in data.columns.deleted %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP COLUMN {{conn|qtIdent(c.attname)}};
+{% endfor -%}
+
+{% for c in data.columns.added %}
+{% if (c.inheritedfrom =='' or  c.inheritedfrom == None or  c.inheritedfrom == 'None' ) %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL {% else %} NULL {% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault|qtLiteral}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endif %}
+{% endfor -%}
+
+{% for c in data.columns.changed %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['attname'])}};
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL {% else %} NULL {% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault|qtLiteral}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endfor %}
+
+{% endif -%}
+
+{% if data.inherits and data.inherits|length > 0%}
+{% for i in data.inherits %}{% if i not in o_data.inherits %}{% if i %}
+
+ALTER FOREIGN TABLE{{ conn|qtIdent(o_data.basensp, name) }} INHERIT {{i}};{% endif %}{% endif %}{% endfor %}
+{% endif -%}
+
+{% if o_data.inherits and 'inherits' in data %}
+{% if data.inherits == None or data.inherits == 'None' %}
+{% set inherits = '' %}
+{% else %}
+{% set inherits = data.inherits %}
+{% endif %}
+{% for i in o_data.inherits %}{% if i not in inherits %}{% if i %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }} NO INHERIT {{i}};{% endif %}{% endif %}{% endfor %}
+{% endif -%}
+
+{% if data.constraints %}
+{% for c in data.constraints.deleted %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP CONSTRAINT {{conn|qtIdent(c.conname)}};
+{% endfor -%}
+
+{% for c in data.constraints.added %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD CONSTRAINT {{conn|qtIdent(c.conname)}} {% if c.consrc %} CHECK ({{c.consrc}}){% endif %} {% if c.connoinherit %} NO INHERIT{% endif %};
+{% endfor %}
+
+{% endif -%}
+
+
+{% if data.ftoptions %}
+{% for o in data.ftoptions.deleted %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS ( DROP {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor -%}
+
+{% for o in data.ftoptions.added %}
+{% if o.option and o.value %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS (ADD {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+
+{% for o in data.ftoptions.changed %}
+{% if o.option and o.value %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS (SET {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+
+{% endif -%}
+
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'FOREIGN TABLE', conn|qtIdent(o_data.basensp, name), r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'FOREIGN TABLE', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'FOREIGN TABLE', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }}
+{% endfor %}
+{% endif -%}
+
+{% if data.description %}
+
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+IS {{ data.description|qtLiteral }};
+{% endif -%}
+
+{% if data.basensp %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+SET SCHEMA {{ conn|qtIdent(data.basensp) }};
+{% endif %}
+
+{% endif %}

-- 
Sent via pgadmin-hackers mailing list ([email-masked])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers

---

From: Khushboo Vashi <[email-masked]>
Date: Wed, 24 Feb 2016 14:57:46 +0530

Hi,

I have updated the Foreign Table module as below:

- Used 'NodeByListControl' to get schemas, in the foreign_table.js file as
suggested by Ashesh to avoid code redundancy.

- Applied *'Security Label Macro'*  Patch (Implemented by Harshal).
  To test the Foreign Table patch, 'Security Label Macro' patch must be
applied first as that is not committed yet.

Please find attached Foreign Table Patch.

Thanks,
Khushboo

index 0000000..be9e671
@@ -0,0 +1,972 @@
+                                  name=self.request['name'])
+                                      foid=foid)
+                scid = res['rows'][0]['scid']

literal 0
HcmV?d00001

literal 0
HcmV?d00001

index 0000000..f5bc4e8
@@ -0,0 +1,500 @@
+          id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name',
+           control: 'node-list-by-name', cache_level: 'database', type: 'text',
+           node: 'schema'
+          if (_.isUndefined(this.get('basensp')) || String(this.get('basensp'))
+          .replace(/^\s+|\s+$/g, '') == '') {
+            err['basensp'] = '{{ _('Schema can not be empty!') }}';
+            errmsg = errmsg || err['basensp'];
index 0000000..a92648e
@@ -0,0 +1,39 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', data.name, r.provider, r.label, data.basensp) }}
index 0000000..9a5f5d1
+    AND c.relname={{ name|qtLiteral }};
+{% elif foid %}
+    c.relnamespace as scid
index 0000000..ffb31c1
@@ -0,0 +1,105 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{{ SECLABLE.UNSET(conn, 'FOREIGN TABLE', name, r.provider, o_data.basensp) }}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+    SET SCHEMA {{ conn|qtIdent(data.basensp) }};
index 0000000..b639fb2
@@ -0,0 +1,40 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', data.name, r.provider, r.label, data.basensp) }}
index 0000000..9a5f5d1
+    AND c.relname={{ name|qtLiteral }};
+{% elif foid %}
+    c.relnamespace as scid
index 0000000..557c495
@@ -0,0 +1,119 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{{ SECLABLE.UNSET(conn, 'FOREIGN TABLE', name, r.provider, o_data.basensp) }}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+    SET SCHEMA {{ conn|qtIdent(data.basensp) }};
index 0000000..d50b392
@@ -0,0 +1,59 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', data.name, r.provider, r.label, data.basensp) }}
index 0000000..9a5f5d1
+    AND c.relname={{ name|qtLiteral }};
+{% elif foid %}
+    c.relnamespace as scid
index 0000000..c513519
@@ -0,0 +1,138 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{{ SECLABLE.UNSET(conn, 'FOREIGN TABLE', name, r.provider, o_data.basensp) }}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+    SET SCHEMA {{ conn|qtIdent(data.basensp) }};
+{% endif %}
+{% endif %}

---

From: Khushboo Vashi <[email-masked]>
Date: Tue, 5 Apr 2016 14:27:05 +0530

Hi,

Please find updated patch for the Foreign Table Module.

This patch is dependent on
1. Backgrid Depscell Patch, (submitted by me)
2. NodeAjaxOptionsCell Transform change patch, on which Ashesh and Murtuza
are working

Thanks,
Khushboo

On Wed, Feb 24, 2016 at 2:57 PM, Khushboo Vashi <
[email-masked]> wrote:

>> The patch will be modified after Types module implementation as we need
>> to populate Base Type  and some Type related validations from the Types
>> module.
>>
index 0000000..04d7592
@@ -0,0 +1,1034 @@
+"""Implements the Foreign Table Module."""
+from pgadmin.browser.server_groups.servers.databases.schemas.utils import \
+    SchemaChildModule, DataTypeReader
+from pgadmin.browser.server_groups.servers.databases.utils import \
+    parse_sec_labels_from_db
+class ForeignTableModule(SchemaChildModule):
+        Make the node as leaf node.
+class ForeignTableView(PGChildNodeView, DataTypeReader):
+        'get_types': [{'get': 'types'}, {'get': 'types'}],
+                req = json.loads(request.data.decode())
+        data = self._fetch_properties(gid, sid, did, scid, foid)
+    def types(self, gid, sid, did, scid, foid=None):
+        Returns the Data Types.
+        condition = """typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')
+AND NOT EXISTS (SELECT 1 FROM pg_class WHERE relnamespace=typnamespace
+AND relname = typname AND relkind != 'c') AND
+(typname NOT LIKE '_%' OR NOT EXISTS (SELECT 1 FROM pg_class WHERE
+relnamespace=typnamespace AND relname = substring(typname FROM 2)::name
+AND relkind != 'c'))"""
+        if self.blueprint.show_system_objects:
+            condition += " AND nsp.nspname != 'information_schema'"
+        # Get Types
+        status, types = self.get_types(self.conn, condition)
+            return internal_server_error(errormsg=types)
+            data=types,
+        data = self._fetch_properties(gid, sid, did, scid, foid, inherits=True)
+        sql_header = """-- {0}: {1}
+-- DROP {0} {1};
+""".format('FOREIGN TABLE', data['basensp'] + "." + data['name'])
+        SQL = sql_header + SQL
+        return ajax_response(response=SQL.strip('\n'))
+                data=SQL.strip('\n'),
+                old_data = self._fetch_properties(gid, sid, did, scid, foid,
+    def _fetch_properties(self, gid, sid, did, scid, foid, inherits=False):
+        # Get formatted Security Labels
+        if 'seclabels' in data:
+            data.update(parse_sec_labels_from_db(data['seclabels']))
+        # Get formatted Variables
+        if 'ftoptions' in data:
+            data.update({'strftoptions': data['ftoptions']})
+            data.update(self._parse_variables_from_db(data['ftoptions']))
+                    c['typlen'] = int(typlen[0])
+                    c['precision'] = int(typlen[1])
+                    c['typlen'] = int(typlen)
+        data['strcolumn'] = ''
+        str_cols = []
+        if 'columns' in data:
+                str_cols.append(c['strcolumn'])
+            data['strcolumn'] = ', '.join(str_cols)
+    def _parse_variables_from_db(self, db_variables):
+        Function to format the output for variables.
+            db_variables: Variable object
+                Expected Object Format:
+                    ['option1=value1', ..]
+                where:
+                    user_name and database are optional
+            Variable Object in below format:
+                'variables': [
+                    {'name': 'var_name', 'value': 'var_value',
+                    'user_name': 'user_name', 'database': 'database_name'},
+                    ...]
+                where:
+                    user_name and database are optional
+        variables_lst = []
+        if db_variables is not None:
+            for row in db_variables:
+                var_name, var_value = row.split("=")
+                # Because we save as boolean string in db so it needs
+                # conversion
+                if var_value == 'false' or var_value == 'off':
+                    var_value = False
+                var_dict = {'option': var_name, 'value': var_value}
+                variables_lst.append(var_dict)
+        return {"ftoptions": variables_lst}

literal 0
HcmV?d00001

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/js/foreign_tables.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/js/foreign_tables.js
new file mode 100644
index 0000000..8865dc9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/js/foreign_tables.js
@@ -0,0 +1,642 @@
+/* Create and Register Foreign Table Collection and Node. */
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+  if (!pgBrowser.Nodes['coll-foreign-table']) {
+    var foreigntable = pgAdmin.Browser.Nodes['coll-foreign-table'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'foreign-table',
+        label: '{{ _('Foreign Tables') }}',
+        type: 'coll-foreign-table',
+        columns: ['name', 'owner', 'description']
+      });
+  };
+
+  // Security Model
+  var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+    defaults: {
+      provider: null,
+      label: null
+    },
+    schema: [{
+      id: 'provider', label: '{{ _('Provider') }}',
+      type: 'text', editable: true, cellHeaderClasses:'width_percent_50'
+    },{
+      id: 'security_label', label: '{{ _('Security Label') }}',
+      type: 'text', editable: true, cellHeaderClasses:'width_percent_50'
+    }],
+    validate: function() {
+      var err = {},
+          errmsg = null,
+          data = this.toJSON();
+
+      if (_.isUndefined(data.label) ||
+        _.isNull(data.label) ||
+        String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+        return _("Please specify the value for all the security providers.");
+      }
+      return null;
+    }
+  });
+
+  // Integer Cell for Columns Length and Precision
+  var IntegerDepCell = Backgrid.IntegerCell.extend({
+      initialize: function() {
+        Backgrid.NumberCell.prototype.initialize.apply(this, arguments);
+        Backgrid.Extension.DependentCell.prototype.initialize.apply(this, arguments);
+      },
+      updateUIDeps: function () {
+        this.$el.empty();
+        var model = this.model;
+        var column = this.column;
+        editable = this.column.get("editable");
+
+        is_editable = _.isFunction(editable) ? !!editable.apply(column, [model]) : !!editable;
+        if (is_editable){ this.$el.addClass("editable"); }
+        else { this.$el.removeClass("editable"); }
+
+        this.delegateEvents();
+        return this;
+      },
+      remove: Backgrid.Extension.DependentCell.prototype.remove
+    });
+
+
+  // Columns Model
+  var ColumnsModel = pgAdmin.Browser.Node.Model.extend({
+    idAttribute: 'attnum',
+    defaults: {
+      attname: undefined,
+      datatype: undefined,
+      typlen: undefined,
+      precision: undefined,
+      typdefault: undefined,
+      attnotnull: undefined,
+      collname: undefined,
+      attnum: undefined,
+      inheritedfrom: undefined,
+      inheritedid: undefined,
+    },
+    type_options: undefined,
+    schema: [{
+        id: 'attname', label:'{{ _('Name') }}', cell: 'string', type: 'text',
+        editable: true, cellHeaderClasses: 'width_percent_20'
+      },{
+        id: 'datatype', label:'{{ _('Data Type') }}', cell: 'node-ajax-options',
+        control: 'node-ajax-options', type: 'text', url: 'get_types',
+        editable: true, cellHeaderClasses: 'width_percent_20',
+        transform: function(d, self){
+            self.model.type_options = d;
+            return d;
+          }
+      },{
+        id: 'typlen', label:'{{ _('Length') }}',
+        cell: IntegerDepCell,
+        type: 'text', deps: ['datatype'],
+        editable: function(m) {
+        // We will store type from selected from combobox
+        //_.isUndefined(m.isNew) ? false : m.isNew();
+        var of_type = m.get('datatype');
+        if(m.type_options) {
+          m.set('is_tlength', false, {silent: true});
+
+          // iterating over all the types
+          _.each(m.type_options, function(o) {
+            // if type from selected from combobox matches in options
+            if ( of_type == o.value ) {
+                // if length is allowed for selected type
+                if(o.length)
+                {
+                  // set the values in model
+                  m.set('is_tlength', true, {silent: true});
+                  m.set('min_val', o.min_val, {silent: true});
+                  m.set('max_val', o.max_val, {silent: true});
+                }
+            }
+          });
+          return m.get('is_tlength');
+        }
+        return true;
+        },
+        cellHeaderClasses: 'width_percent_10'
+      },{
+        id: 'precision', label:'{{ _('Precision') }}',
+        type: 'text', deps: ['datatype'],
+        cell: IntegerDepCell,
+        editable: function(m) {
+          var of_type = m.get('datatype');
+          if(m.type_options) {
+             m.set('is_precision', false, {silent: true});
+            // iterating over all the types
+            _.each(m.type_options, function(o) {
+              // if type from selected from combobox matches in options
+              if ( of_type == o.value ) {
+                // if precession is allowed for selected type
+                if(o.precision)
+                {
+                  // set the values in model
+                  m.set('is_precision', true, {silent: true});
+                  m.set('min_val', o.min_val, {silent: true});
+                  m.set('max_val', o.max_val, {silent: true});
+                }
+            }
+          });
+          return m.get('is_precision');
+        }
+        return true;
+        }, cellHeaderClasses: 'width_percent_10'
+      },{
+        id: 'typdefault', label:'{{ _('Default') }}', type: 'text',
+        cell: 'string', min_version: 90300,
+        placeholder: "Enter an expression or a value.",
+        cellHeaderClasses: 'width_percent_10',
+        editable: function(m) {
+          if (this.get('node_info').server.version < 90300){
+            return false;
+          }
+          return true;
+        }
+      },{
+        id: 'attnotnull', label:'{{ _('Not Null') }}',
+        cell: 'boolean',type: 'switch', editable: true,
+        cellHeaderClasses: 'width_percent_10'
+      },{
+        id: 'collname', label:'{{ _('Collation') }}', cell: 'node-ajax-options',
+        control: 'node-ajax-options', type: 'text', url: 'get_collations',
+        min_version: 90300, editable: true,
+        cellHeaderClasses: 'width_percent_20'
+      },{
+        id: 'attnum', cell: 'string',type: 'text', visible: false
+      },{
+        id: 'inheritedfrom', label:'{{ _('Inherited From') }}', cell: 'string',
+        type: 'text', visible: false, mode: ['properties'],
+        cellHeaderClasses: 'width_percent_10'
+    }],
+    validate: function() {
+      // TODO: Add validation here
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+  var formatNode = function(opt) {
+    if (!opt.id) {
+      return opt.text;
+    }
+
+    var optimage = $(opt.element).data('image');
+
+    if(!optimage){
+      return opt.text;
+    } else {
+      return $(
+          '<span><span class="wcTabIcon ' + optimage + '"/>' + opt.text + '</span>'
+          );
+    }
+  };
+
+
+  /* NodeAjaxOptionsMultipleControl is for multiple selection of Combobox.
+  *  This control is used to select Multiple Parent Tables to be inherited.
+  *  It also populates/vacates Columns on selection/deselection of the option (i.e. table name).
+  *  To populates the column, it calls the server and fetch the columns data
+  *  for the selected table.
+  */
+
+  var NodeAjaxOptionsMultipleControl = Backform.NodeAjaxOptionsMultipleControl = Backform.NodeAjaxOptionsControl.extend({
+    template: _.template([
+      '<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+      '<div class="<%=Backform.controlsClassName%> <%=extraClasses.join(\' \')%>">',
+      '  <select class="pgadmin-node-select form-control" name="<%=name%>" style="width:100%;" value=<%-value%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> >',
+      '  </select>',
+      '</div>'].join("\n")),
+    defaults: _.extend(
+      {}, Backform.NodeAjaxOptionsControl.prototype.defaults,
+      {
+        select2: {
+          allowClear: true,
+          placeholder: 'Select from the list',
+          width: 'style',
+          templateResult: formatNode,
+          templateSelection: formatNode
+        }
+      }),
+    render: function() {
+      var field = _.defaults(this.field.toJSON(), this.defaults),
+        attributes = this.model.toJSON(),
+        attrArr = field.name.split('.'),
+        name = attrArr.shift(),
+        path = attrArr.join('.'),
+        rawValue = this.keyPathAccessor(attributes[name], path),
+        data = _.extend(field, {
+          rawValue: rawValue,
+          value: this.formatter.fromRaw(rawValue, this.model),
+          attributes: attributes,
+          formatter: this.formatter
+        }),
+        evalF = function(f, d, m) {
+          return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
+        };
+
+      // Evaluate the disabled, visible, and required option
+      _.extend(data, {
+        disabled: evalF(data.disabled, data, this.model),
+        visible:  evalF(data.visible, data, this.model),
+        required: evalF(data.required, data, this.model)
+      });
+
+      if (field.node_info.server.version < field.min_version) {
+        field.version_compatible = false
+        return this;
+      }
+      else {
+        // Evaluation the options
+        if (_.isFunction(data.options)) {
+        try {
+          data.options = data.options.apply(this)
+        } catch(e) {
+          // Do nothing
+          data.options = []
+          this.model.trigger('pgadmin-view:transform:error', m, self.field, e);
+        }
+        }
+
+        // Clean up first
+        this.$el.removeClass(Backform.hiddenClassname);
+        this.$el.html(this.template(data)).addClass(field.name);
+
+        if (!data.visible) {
+        this.$el.addClass(Backform.hiddenClassname);
+        } else {
+        var opts = _.extend(
+          {}, this.defaults.select2, data.select2,
+          {
+            'data': data.options
+          });
+        this.$el.find("select").select2(opts).val(data.rawValue).trigger("change");
+        this.updateInvalid();
+        }
+      }
+      return this;
+    },
+    onChange: function(e) {
+      var model = this.model,
+          $el = $(e.target),
+          attrArr = this.field.get("name").split('.'),
+          name = attrArr.shift(),
+          path = attrArr.join('.'),
+          value = this.getValueFromDOM(),
+          changes = {},
+          columns = model.get('columns'),
+          inherits = model.get(name);
+
+      if (this.model.errorModel instanceof Backbone.Model) {
+        if (_.isEmpty(path)) {
+          this.model.errorModel.unset(name);
+        } else {
+          var nestedError = this.model.errorModel.get(name);
+          if (nestedError) {
+            this.keyPathSetter(nestedError, path, null);
+            this.model.errorModel.set(name, nestedError);
+          }
+        }
+      }
+
+      var self = this;
+
+      if (typeof(inherits)  == "string"){ inherits = JSON.parse(inherits); }
+
+      // Remove Columns if inherit option is deselected from the combobox
+      if(_.size(JSON.parse(value)) < _.size(inherits)) {
+        var dif =  _.difference(inherits, JSON.parse(value));
+        var rmv_columns = columns.where({inheritedid: parseInt(dif[0])});
+        columns.remove(rmv_columns);
+      }
+      else
+      {
+        _.each(JSON.parse(value), function(i) {
+          // Fetch Columns from server
+          inhted_columns = self.fetchColumns(i);
+          columns.add(inhted_columns);
+        });
+      }
+
+      changes[name] = _.isEmpty(path) ? value : _.clone(model.get(name)) || {};
+      this.stopListening(this.model, "change:" + name, this.render);
+      model.set(changes);
+      this.listenTo(this.model, "change:" + name, this.render);
+    },
+    fetchColumns: function(table_id){
+      var self = this,
+          url = 'get_columns',
+          m = self.model.top || self.model;
+
+      if (url) {
+        var node = this.field.get('schema_node'),
+            node_info = this.field.get('node_info'),
+            full_url = node.generate_url.apply(
+              node, [
+                null, url, this.field.get('node_data'),
+                this.field.get('url_with_id') || false, node_info
+              ]),
+            cache_level = this.field.get('cache_level') || node.type,
+            cache_node = this.field.get('cache_node');
+
+        cache_node = (cache_node && pgAdmin.Browser.Nodes['cache_node']) || node;
+
+        m.trigger('pgadmin:view:fetching', m, self.field);
+        data = {attrelid: table_id}
+
+        // Fetching Columns data for the selected table.
+        $.ajax({
+          async: false,
+          url: full_url,
+          data: data,
+          success: function(res) {
+            /*
+             * We will cache this data for short period of time for avoiding
+             * same calls.
+             */
+            data = cache_node.cache(url, node_info, cache_level, res.data);
+          },
+          error: function() {
+            m.trigger('pgadmin:view:fetch:error', m, self.field);
+          }
+        });
+        m.trigger('pgadmin:view:fetched', m, self.field);
+
+        // To fetch only options from cache, we do not need time from 'at'
+        // attribute but only options.
+        //
+        // It is feasible that the data may not have been fetched.
+        data = (data && data.data) || [];
+        return data;
+      }
+    },
+  });
+
+
+  // Constraints Model
+  var ConstraintModel = pgAdmin.Browser.Node.Model.extend({
+    idAttribute: 'conoid',
+    initialize: function(attrs, args) {
+      var isNew = (_.size(attrs) === 0);
+      if (!isNew) {
+        this.convalidated_default = this.get('convalidated')
+      }
+      pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+    },
+    defaults: {
+      conoid: undefined,
+      conname: undefined,
+      consrc: undefined,
+      connoinherit: undefined,
+      convalidated: true
+    },
+    convalidated_default: true,
+    schema: [{
+      id: 'conoid', type: 'text', cell: 'string', visible: false
+    },{
+      id: 'conname', label:'{{ _('Name') }}', type: 'text', cell: 'string',
+      editable: 'is_editable', cellHeaderClasses: 'width_percent_30'
+    },{
+      id: 'consrc', label:'{{ _('Check') }}', type: 'multiline',
+      editable: 'is_editable', cell: Backgrid.Extension.TextareaCell,
+      cellHeaderClasses: 'width_percent_30'
+    },{
+      id: 'connoinherit', label:'{{ _('No Inherit') }}', type: 'switch',
+      cell: 'boolean', editable: 'is_editable',
+      cellHeaderClasses: 'width_percent_20'
+    },{
+      id: 'convalidated', label:'{{ _('Validate?') }}', type: 'switch',
+      cell: 'boolean', cellHeaderClasses: 'width_percent_20',
+      editable: function(m) {
+        var server = this.get('node_info').server;
+        if (_.isUndefined(m.isNew)) { return true; }
+        if (!m.isNew()) {
+          if(m.get('convalidated') && m.convalidated_default) {
+            return false;
+          }
+          return true;
+        }
+        return true;
+      }
+     }
+    ],
+    validate: function() {
+      // TODO: Add validation here
+    },
+    is_editable: function(m) {
+        return _.isUndefined(m.isNew) ? true : m.isNew();
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+
+  // Options Model
+  var OptionsModel = pgAdmin.Browser.Node.Model.extend({
+    defaults: {
+      option: undefined,
+      value: undefined
+    },
+    schema: [{
+      id: 'option', label:'{{ _('Option') }}', cell: 'string', type: 'text',
+      editable: true, cellHeaderClasses:'width_percent_50'
+    },{
+      id: 'value', label:'{{ _('Value') }}', cell: 'string',type: 'text',
+      editable: true, cellHeaderClasses:'width_percent_50'
+    }
+    ],
+    validate: function() {
+      // TODO: Add validation here
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+
+  if (!pgBrowser.Nodes['foreign-table']) {
+    pgAdmin.Browser.Nodes['foreign-table'] = pgBrowser.Node.extend({
+      type: 'foreign-table',
+      label: '{{ _('Foreign Table') }}',
+      collection_type: 'coll-foreign-table',
+      hasSQL: true,
+      hasDepends: true,
+      parent_type: ['schema'],
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_foreign-table_on_coll', node: 'coll-foreign-table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Foreign Table...') }}',
+          icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_foreign-table', node: 'foreign-table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Foreign Table...') }}',
+          icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_foreign-table', node: 'schema', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Foreign Table...') }}',
+          icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: false},
+          enable: 'canCreate'
+        }
+        ]);
+
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      model: pgAdmin.Browser.Node.Model.extend({
+        initialize: function(attrs, args) {
+          var isNew = (_.size(attrs) === 0);
+          if (isNew) {
+            // Set Selected Schema
+            schema = args.node_info.schema.label
+            this.set({'basensp': schema}, {silent: true});
+          }
+          pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+        },
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          owner: undefined,
+          basensp: undefined,
+          description: undefined,
+          ftsrvname: undefined,
+          strcolumn: undefined,
+          strftoptions: undefined,
+          inherits: [],
+          columns: [],
+          constraints: [],
+          ftoptions: [],
+          seclabels: []
+        },
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', cell: 'string',
+          type: 'text', mode: ['properties', 'create', 'edit']
+        },{
+          id: 'oid', label:'{{ _('Oid') }}', cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl,
+          node: 'role',  type: 'text', mode: ['edit', 'properties']
+        },{
+          id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name',
+           control: 'node-list-by-name', cache_level: 'database', type: 'text',
+           node: 'schema', mode:['create', 'edit']
+        },{
+          id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline'
+        },{
+          id: 'ftsrvname', label:'{{ _('Foreign Server') }}', cell: 'string', control: 'node-ajax-options',
+          type: 'text', group: 'Definition', url: 'get_foreign_servers', disabled: function(m) { return !m.isNew(); }
+        },{
+          id: 'inherits', label:'{{ _('Inherits') }}', cell: 'string', group: 'Definition',
+          type: 'list', min_version: 90500, control: 'node-ajax-options-multiple',
+          url: 'get_tables', select2: {multiple: true},
+          'cache_level': 'database'
+        },{
+          id: 'strcolumn', label:'{{ _('Columns') }}', cell: 'string', group: 'Definition',
+          type: 'text', min_version: 90500, mode: ['properties']
+        },{
+          id: 'columns', label:'{{ _('Columns') }}', cell: 'string',
+          type: 'collection', group: 'Columns', visible: false, mode: ['edit', 'create'],
+          model: ColumnsModel, canAdd: true, canDelete: true, canEdit: false,
+          columns: ['attname', 'datatype', 'typlen', 'precision', 'typdefault', 'attnotnull', 'collname', 'inheritedfrom']
+        },
+        {
+          id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string',
+          type: 'collection', group: 'Constraints', visible: false, mode: ['edit', 'create'],
+          model: ConstraintModel, canAdd: true, canDelete: true, columns: ['conname','consrc', 'connoinherit', 'convalidated'],
+          canEdit: function(o) {
+            if (o instanceof Backbone.Model) {
+              if (o instanceof ConstraintModel) {
+                return o.isNew();
+              }
+            }
+            return true;
+          }, min_version: 90500
+        },{
+          id: 'strftoptions', label:'{{ _('Options') }}', cell: 'string',
+          type: 'text', group: 'Definition', mode: ['properties']
+        },{
+          id: 'ftoptions', label:'{{ _('Options') }}', cell: 'string',
+          type: 'collection', group: 'Options', visible: false, mode: ['edit', 'create'],
+          model: OptionsModel, canAdd: true, canDelete: true, canEdit: false
+        },{
+          id: 'seclabels', label: '{{ _('Security Labels') }}',
+          model: SecurityModel, type: 'collection',
+          group: '{{ _('Security') }}', mode: ['edit', 'create'],
+          min_version: 90100, canAdd: true,
+          canEdit: false, canDelete: true,
+          control: 'unique-col-collection', uniqueCol : ['provider']
+        }
+        ],
+        validate: function()
+        {
+          var err = {},
+              errmsg,
+              seclabels = this.get('seclabels');
+
+          if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            err['name'] = '{{ _('Name can not be empty!') }}';
+            errmsg = errmsg || err['name'];
+          }
+
+          if (_.isUndefined(this.get('basensp')) || String(this.get('basensp'))
+          .replace(/^\s+|\s+$/g, '') == '') {
+            err['basensp'] = '{{ _('Schema can not be empty!') }}';
+            errmsg = errmsg || err['basensp'];
+          }
+
+          if (_.isUndefined(this.get('ftsrvname')) || String(this.get('ftsrvname')).replace(/^\s+|\s+$/g, '') == '') {
+            err['ftsrvname'] = '{{ _('Foreign Server can not be empty!') }}';
+            errmsg = errmsg || err['ftsrvname'];
+          }
+
+          this.errorModel.clear().set(err);
+
+          return null;
+        }
+      }),
+      canCreate: function(itemData, item, data) {
+        //If check is false then , we will allow create menu
+        if (data && data.check == false)
+          return true;
+
+        var t = pgBrowser.tree, i = item, d = itemData;
+        // To iterate over tree to check parent node
+        while (i) {
+          // If it is schema then allow user to create foreign table
+          if (_.indexOf(['schema'], d._type) > -1)
+            return true;
+
+          if ('coll-foreign-table' == d._type) {
+            //Check if we are not child of catalog
+            prev_i = t.hasParent(i) ? t.parent(i) : null;
+            prev_d = prev_i ? t.itemData(prev_i) : null;
+            if( prev_d._type == 'catalog') {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          i = t.hasParent(i) ? t.parent(i) : null;
+          d = i ? t.itemData(i) : null;
+        }
+        // by default we do not want to allow create menu
+        return true;
+      }
+  });
+
+  }
+
+  return pgBrowser.Nodes['foreign-table'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..8c46eca
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/create.sql
@@ -0,0 +1,28 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% if data %}
+CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }} ({% if data.columns %}
+{% for c in data.columns %}
+{% if not loop.first%}    {% endif %}{{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.attnotnull %}
+ NOT NULL{% else %} NULL{% endif %}
+{% if not loop.last %},
+{% endif %}{% endfor -%}{% endif %}
+)
+    SERVER {{ conn|qtIdent(data.ftsrvname) }}{% if data.ftoptions %}
+{% for o in data.ftoptions %}
+{% if o.option and o.value %}
+{% if loop.first %}    OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral}}{% if loop.last %}){% endif %}{% endif %}
+{% endfor %}{% endif -%};
+{% if data.description %}
+
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+    IS '{{ data.description }}';
+{% endif -%}
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.label and r.provider %}
+
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', data.name, r.provider, r.label, data.basensp) }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..6243e05
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/delete.sql
@@ -0,0 +1,15 @@
+{% if scid and foid %}
+SELECT
+    c.relname AS name, nspname as basensp
+FROM
+    pg_class c
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+    AND c.oid = {{foid}}::oid;
+{% endif %}
+
+{% if name %}
+DROP FOREIGN TABLE {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..e59c17d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,10 @@
+SELECT --nspname, collname,
+    CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+    concat(nspname, '."', collname,'"')
+    ELSE '' END AS copy_collation
+FROM
+    pg_collation c, pg_namespace n
+WHERE
+    c.collnamespace=n.oid
+ORDER BY
+    nspname, collname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_columns.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_columns.sql
new file mode 100644
index 0000000..4bf5e17
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_columns.sql
@@ -0,0 +1,22 @@
+SELECT
+    attname, attndims, atttypmod, format_type(t.oid,NULL) AS datatype,
+    format_type(t.oid, att.atttypmod) AS fulltype, attnotnull, attnum,
+    (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup,
+    (
+        attname || ' ' || format_type(t.oid, att.atttypmod) || ' ' ||
+        (CASE WHEN attnotnull='true'
+        THEN 'NOT NULL' ELSE 'NULL'
+        END)
+    ) as strcolumn
+FROM
+    pg_attribute att
+JOIN
+    pg_type t ON t.oid=atttypid
+JOIN
+    pg_namespace nsp ON t.typnamespace=nsp.oid
+LEFT OUTER JOIN
+    pg_type b ON t.typelem=b.oid
+WHERE
+    att.attrelid={{foid}}::oid
+    AND attnum>0
+ORDER by attnum;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_constraints.sql
new file mode 100644
index 0000000..fc93549
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_constraints.sql
@@ -0,0 +1,7 @@
+SELECT
+    conname, contype, consrc
+FROM
+    pg_constraint
+WHERE
+    conrelid={{foid}}::oid
+ORDER by conname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_foreign_servers.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_foreign_servers.sql
new file mode 100644
index 0000000..1980f02
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_foreign_servers.sql
@@ -0,0 +1,5 @@
+SELECT
+    srvname
+FROM
+    pg_foreign_server
+ORDER BY srvname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..9a5f5d1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,19 @@
+{% if basensp %}
+SELECT
+    c.oid, bn.oid as scid
+FROM
+    pg_class c
+JOIN
+    pg_namespace bn ON bn.oid=c.relnamespace
+WHERE
+    bn.nspname = {{ basensp|qtLiteral }}
+    AND c.relname={{ name|qtLiteral }};
+
+{% elif foid %}
+SELECT
+    c.relnamespace as scid
+FROM
+    pg_class c
+WHERE
+    c.oid = {{foid}}::oid;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/node.sql
new file mode 100644
index 0000000..2d3a38b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/node.sql
@@ -0,0 +1,12 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, nspname as basensp
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..25abc1b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/properties.sql
@@ -0,0 +1,25 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, srvname AS ftsrvname, description, nspname as basensp,
+    (SELECT
+        array_agg(provider || '=' || label)
+    FROM
+        pg_seclabel sl1
+    WHERE
+        sl1.objoid=c.oid) AS seclabels
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_foreign_server fs ON ft.ftserver=fs.oid
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=c.oid AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+    {% if foid %}
+    AND c.oid = {{foid}}::oid
+    {% endif %}
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..25fbcef
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.1_plus/update.sql
@@ -0,0 +1,105 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}
+{% if data.name != o_data.name %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, o_data.name) }}
+RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}
+{% endif -%}
+
+{% if data.owner %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OWNER TO {{ data.owner }};
+{% endif -%}
+
+{% if data.columns %}
+{% for c in data.columns.deleted %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP COLUMN {{conn|qtIdent(c.attname)}};
+{% endfor -%}
+
+{% for c in data.columns.added %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endfor -%}
+
+{% for c in data.columns.changed %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['attname'])}};
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endfor %}
+{% endif -%}
+
+
+{% if data.ftoptions %}
+{% for o in data.ftoptions.deleted %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS ( DROP {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+
+{% for o in data.ftoptions.added %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS (ADD {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+
+{% for o in data.ftoptions.changed %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS (SET {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+{% endif -%}
+
+
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.UNSET(conn, 'FOREIGN TABLE', name, r.provider, o_data.basensp) }}
+{% endfor %}
+{% endif -%}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+{% endfor %}
+{% endif -%}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+{% endfor %}
+{% endif -%}
+
+{% if data.description %}
+
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    IS {{ data.description|qtLiteral }};
+{% endif -%}
+
+{% if data.basensp %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    SET SCHEMA {{ conn|qtIdent(data.basensp) }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/create.sql
new file mode 100644
index 0000000..4f970df
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/create.sql
@@ -0,0 +1,30 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% if data %}
+CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }} ({% if data.columns %}
+{% for c in data.columns %}
+{% if not loop.first%}    {% endif %}{{conn|qtIdent(c.attname)}} {{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.attnotnull %}
+ NOT NULL{% else %} NULL{% endif %}{% if c.typdefault %}
+ DEFAULT {{c.typdefault}}{% endif %}{% if c.collname %}
+ COLLATE {{c.collname}}{% endif %}
+{% if not loop.last %},
+{% endif %}{% endfor -%}{% endif %}
+)
+    SERVER {{ conn|qtIdent(data.ftsrvname) }}{% if data.ftoptions %}
+{% for o in data.ftoptions %}
+{% if o.option and o.value %}
+{% if loop.first %}    OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral}}{% if loop.last %}){% endif %}{% endif %}
+{% endfor %}{% endif %};
+{% if data.description %}
+
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+    IS '{{ data.description }}';
+{% endif -%}
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.security_label and r.provider %}
+
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', data.name, r.provider, r.security_label, data.basensp) }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/delete.sql
new file mode 100644
index 0000000..5bb0969
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/delete.sql
@@ -0,0 +1,17 @@
+{% if scid and foid %}
+SELECT
+    c.relname AS name, nspname as basensp
+FROM
+    pg_class c
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+AND
+    c.oid = {{foid}}::oid;
+{% endif %}
+
+
+{% if name %}
+DROP FOREIGN TABLE {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_collations.sql
new file mode 100644
index 0000000..e8d274b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_collations.sql
@@ -0,0 +1,9 @@
+SELECT --nspname, collname,
+    CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+    concat(nspname, '."', collname,'"')
+    ELSE '' END AS copy_collation
+FROM
+    pg_collation c, pg_namespace n
+WHERE
+    c.collnamespace=n.oid
+ORDER BY nspname, collname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_columns.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_columns.sql
new file mode 100644
index 0000000..0824d3e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_columns.sql
@@ -0,0 +1,34 @@
+SELECT
+    attname, attndims, atttypmod, format_type(t.oid,NULL) AS datatype,
+    attnotnull, attnum, format_type(t.oid, att.atttypmod) AS fulltype,
+    CASE WHEN length(cn.nspname) > 0 AND length(cl.collname) > 0 THEN
+    concat(cn.nspname, '."', cl.collname,'"') ELSE '' END AS collname,
+    (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup,
+    pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS typdefault,
+    (
+        attname || ' ' || format_type(t.oid, att.atttypmod) || ' ' ||
+        (CASE WHEN attnotnull='true'
+        THEN 'NOT NULL' ELSE 'NULL'
+        END) || ' ' ||
+        (CASE WHEN pg_catalog.pg_get_expr(def.adbin, def.adrelid)<>''
+        THEN 'DEFAULT ' || pg_catalog.pg_get_expr(def.adbin, def.adrelid)
+        ELSE '' END)
+    ) as strcolumn
+FROM
+    pg_attribute att
+JOIN
+    pg_type t ON t.oid=atttypid
+JOIN
+    pg_namespace nsp ON t.typnamespace=nsp.oid
+LEFT OUTER JOIN
+    pg_attrdef def ON adrelid=att.attrelid AND adnum=att.attnum
+LEFT OUTER JOIN
+    pg_type b ON t.typelem=b.oid
+LEFT OUTER JOIN
+    pg_collation cl ON t.typcollation=cl.oid
+LEFT OUTER JOIN
+    pg_namespace cn ON cl.collnamespace=cn.oid
+WHERE
+    att.attrelid={{foid}}::oid
+    AND attnum>0
+ORDER by attnum;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_constraints.sql
new file mode 100644
index 0000000..fc93549
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_constraints.sql
@@ -0,0 +1,7 @@
+SELECT
+    conname, contype, consrc
+FROM
+    pg_constraint
+WHERE
+    conrelid={{foid}}::oid
+ORDER by conname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_foreign_servers.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_foreign_servers.sql
new file mode 100644
index 0000000..1c95ce5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_foreign_servers.sql
@@ -0,0 +1,6 @@
+SELECT
+    srvname
+FROM
+    pg_foreign_server
+ORDER BY
+    srvname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_oid.sql
new file mode 100644
index 0000000..9a5f5d1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/get_oid.sql
@@ -0,0 +1,19 @@
+{% if basensp %}
+SELECT
+    c.oid, bn.oid as scid
+FROM
+    pg_class c
+JOIN
+    pg_namespace bn ON bn.oid=c.relnamespace
+WHERE
+    bn.nspname = {{ basensp|qtLiteral }}
+    AND c.relname={{ name|qtLiteral }};
+
+{% elif foid %}
+SELECT
+    c.relnamespace as scid
+FROM
+    pg_class c
+WHERE
+    c.oid = {{foid}}::oid;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/node.sql
new file mode 100644
index 0000000..2d3a38b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/node.sql
@@ -0,0 +1,12 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, nspname as basensp
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/properties.sql
new file mode 100644
index 0000000..3ff088f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/properties.sql
@@ -0,0 +1,27 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, srvname AS ftsrvname, description, nspname as basensp, consrc,
+    (SELECT
+        array_agg(provider || '=' || label)
+    FROM
+        pg_shseclabel sl1
+    WHERE
+        sl1.objoid=c.oid) AS seclabels
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_foreign_server fs ON ft.ftserver=fs.oid
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=c.oid AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+LEFT OUTER JOIN
+    pg_constraint cn ON (cn.conrelid=c.oid)
+WHERE
+    c.relnamespace = {{scid}}::oid
+    {% if foid %}
+    AND c.oid = {{foid}}::oid
+    {% endif %}
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/update.sql
new file mode 100644
index 0000000..73b0508
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.3_plus/update.sql
@@ -0,0 +1,113 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}
+{% if data.name != o_data.name %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, o_data.name) }}
+RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}
+{% endif -%}
+{% if data.owner %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OWNER TO {{ data.owner }};
+{% endif -%}
+{% if data.columns %}
+{% for c in data.columns.deleted %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP COLUMN {{conn|qtIdent(c.attname)}};
+{% endfor -%}
+{% for c in data.columns.added %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endfor -%}
+{% for c in data.columns.changed %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['attname'])}};
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endfor -%}
+{% endif -%}
+{% if data.constraints %}
+{% for c in data.constraints.deleted %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+DROP CONSTRAINT {{conn|qtIdent(c.conname)}};
+{% endfor %}
+
+{% for c in data.constraints.added %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+ADD CONSTRAINT {{conn|qtIdent(c.conname)}} {% if c.consrc %} CHECK ({{c.consrc}}){% endif %} {% if c.connoinherit %} NO INHERIT{% endif %};
+{% endfor %}
+{% endif -%}
+
+{% if data.ftoptions %}
+{% for o in data.ftoptions.deleted %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS ( DROP {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+
+{% for o in data.ftoptions.added %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS (ADD {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+
+{% for o in data.ftoptions.changed %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+OPTIONS (SET {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+{% endif -%}
+
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+
+{{ SECLABLE.UNSET(conn, 'FOREIGN TABLE', name, r.provider, o_data.basensp) }}
+{% endfor %}
+{% endif -%}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+{% endfor %}
+{% endif -%}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+{% endfor %}
+{% endif -%}
+
+{% if data.description %}
+
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    IS {{ data.description|qtLiteral }};
+{% endif -%}
+
+{% if data.basensp %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    SET SCHEMA {{ conn|qtIdent(data.basensp) }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/create.sql
new file mode 100644
index 0000000..409b294
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/create.sql
@@ -0,0 +1,46 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% set is_columns = [] %}
+{% if data %}
+CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+({% if data.columns %}
+{% for c in data.columns %}
+{% if (not c.inheritedfrom or c.inheritedfrom =='' or  c.inheritedfrom == None or  c.inheritedfrom == 'None' ) %}
+{% if is_columns.append('1') %}{% endif %}
+{{conn|qtIdent(c.attname)}} {{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
+{% if c.typdefault %} DEFAULT{{c.typdefault}}{% endif %}
+{% if c.collname %} COLLATE{{c.collname}}{% endif %}
+{% if not loop.last %},
+{% endif %}{% endif %}{% endfor -%}
+{% endif -%}
+)
+{% if data.inherits %}
+    INHERITS ({% for i in data.inherits %}{% if i %}{{i}}{% if not loop.last %}, {% endif %}{% endif %}{% endfor %})
+{% endif %}
+    SERVER {{ conn|qtIdent(data.ftsrvname) }}{% if data.ftoptions %}
+{% for o in data.ftoptions %}
+
+{% if o.option and o.value %}{% if loop.first %}    OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral}}{% if loop.last %}){% endif %}{% endif %}
+{% endfor %}{% endif %};
+{% if data.constraints %}
+{% for c in data.constraints %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+    ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif %};
+{% endfor %}
+{% endif %}
+{% if data.description %}
+
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
+    IS '{{ data.description }}';
+{% endif -%}
+
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.label and r.provider %}
+
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', data.name, r.provider, r.label, data.basensp) }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/delete.sql
new file mode 100644
index 0000000..5bb0969
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/delete.sql
@@ -0,0 +1,17 @@
+{% if scid and foid %}
+SELECT
+    c.relname AS name, nspname as basensp
+FROM
+    pg_class c
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+AND
+    c.oid = {{foid}}::oid;
+{% endif %}
+
+
+{% if name %}
+DROP FOREIGN TABLE {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_collations.sql
new file mode 100644
index 0000000..e8d274b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_collations.sql
@@ -0,0 +1,9 @@
+SELECT --nspname, collname,
+    CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+    concat(nspname, '."', collname,'"')
+    ELSE '' END AS copy_collation
+FROM
+    pg_collation c, pg_namespace n
+WHERE
+    c.collnamespace=n.oid
+ORDER BY nspname, collname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_columns.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_columns.sql
new file mode 100644
index 0000000..b0602ed
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_columns.sql
@@ -0,0 +1,46 @@
+SELECT
+    att.attname, att.attndims, att.atttypmod, format_type(t.oid,NULL) AS datatype,
+    att.attnotnull, att.attnum, format_type(t.oid, att.atttypmod) AS fulltype,
+    CASE WHEN length(cn.nspname) > 0 AND length(cl.collname) > 0 THEN
+    concat(cn.nspname, '."', cl.collname,'"')
+    ELSE '' END AS collname,
+    pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS typdefault,
+    (
+        attname || ' ' || format_type(t.oid, att.atttypmod) || ' ' ||
+        (CASE WHEN attnotnull='true'
+        THEN 'NOT NULL' ELSE 'NULL'
+        END) || ' ' ||
+        (CASE WHEN pg_catalog.pg_get_expr(def.adbin, def.adrelid)<>''
+        THEN 'DEFAULT ' || pg_catalog.pg_get_expr(def.adbin, def.adrelid)
+        ELSE '' END)
+    ) as strcolumn,
+    (SELECT concat(nmsp_parent.nspname, '.',parent.relname ) as inheritedfrom
+    FROM
+        pg_attribute at
+    JOIN
+        pg_inherits ph ON ph.inhparent = at.attrelid AND ph.inhrelid =  {{foid}}::oid
+    JOIN
+        pg_class parent ON ph.inhparent  = parent.oid
+    JOIN
+        pg_namespace nmsp_parent ON nmsp_parent.oid  = parent.relnamespace
+        WHERE at.attname = att.attname
+    ),
+    (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup
+FROM
+    pg_attribute att
+JOIN
+    pg_type t ON t.oid=atttypid
+JOIN
+    pg_namespace nsp ON t.typnamespace=nsp.oid
+LEFT OUTER JOIN
+    pg_attrdef def ON adrelid=att.attrelid AND adnum=att.attnum
+LEFT OUTER JOIN
+    pg_type b ON t.typelem=b.oid
+LEFT OUTER JOIN
+    pg_collation cl ON t.typcollation=cl.oid
+LEFT OUTER JOIN
+    pg_namespace cn ON cl.collnamespace=cn.oid
+WHERE
+    att.attrelid={{foid}}::oid
+    AND att.attnum>0
+ORDER by att.attnum;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_constraints.sql
new file mode 100644
index 0000000..7560c31
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_constraints.sql
@@ -0,0 +1,7 @@
+SELECT
+    oid as conoid, conname, contype, consrc, connoinherit, convalidated
+FROM
+    pg_constraint
+WHERE
+    conrelid={{foid}}::oid
+ORDER by conname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_foreign_servers.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_foreign_servers.sql
new file mode 100644
index 0000000..717bdd3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_foreign_servers.sql
@@ -0,0 +1,6 @@
+SELECT
+    srvname
+FROM
+    pg_foreign_server
+ORDER
+    BY srvname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_oid.sql
new file mode 100644
index 0000000..9a5f5d1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_oid.sql
@@ -0,0 +1,19 @@
+{% if basensp %}
+SELECT
+    c.oid, bn.oid as scid
+FROM
+    pg_class c
+JOIN
+    pg_namespace bn ON bn.oid=c.relnamespace
+WHERE
+    bn.nspname = {{ basensp|qtLiteral }}
+    AND c.relname={{ name|qtLiteral }};
+
+{% elif foid %}
+SELECT
+    c.relnamespace as scid
+FROM
+    pg_class c
+WHERE
+    c.oid = {{foid}}::oid;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_table_columns.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_table_columns.sql
new file mode 100644
index 0000000..e1882db
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_table_columns.sql
@@ -0,0 +1,14 @@
+{% if attrelid  %}
+SELECT
+    a.attname, format_type(a.atttypid, NULL) AS datatype,
+    quote_ident(n.nspname)||'.'||quote_ident(c.relname) as inheritedfrom,
+    c.oid as inheritedid
+FROM
+    pg_class c
+JOIN
+    pg_namespace n ON c.relnamespace=n.oid
+JOIN
+    pg_attribute a ON a.attrelid = c.oid AND NOT a.attisdropped AND a.attnum>0
+WHERE
+    c.oid = {{attrelid}}::OID
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_tables.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_tables.sql
new file mode 100644
index 0000000..3085a26
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/get_tables.sql
@@ -0,0 +1,19 @@
+{% if attrelid  %}
+SELECT
+    array_agg(quote_ident(n.nspname) || '.' || quote_ident(c.relname)) as inherits
+FROM
+    pg_class c, pg_namespace n
+WHERE
+    c.relnamespace=n.oid AND c.relkind IN ('r', 'f')
+    AND c.oid in {{attrelid}};
+
+{% else %}
+SELECT
+    c.oid AS id, quote_ident(n.nspname) || '.' || quote_ident(c.relname) as text
+FROM
+    pg_class c, pg_namespace n
+WHERE
+    c.relnamespace=n.oid AND c.relkind IN ('r', 'f')
+ORDER BY
+    n.nspname, c.relname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/node.sql
new file mode 100644
index 0000000..2d3a38b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/node.sql
@@ -0,0 +1,12 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, nspname as basensp
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/properties.sql
new file mode 100644
index 0000000..c20b824
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/properties.sql
@@ -0,0 +1,31 @@
+SELECT
+    c.oid, c.relname AS name, pg_get_userbyid(relowner) AS owner,
+    ftoptions, srvname AS ftsrvname, description, nspname AS basensp,
+    (SELECT
+        array_agg(provider || '=' || label)
+    FROM
+        pg_shseclabel sl1
+    WHERE
+        sl1.objoid=c.oid) AS seclabels
+    {% if foid %},
+    (SELECT
+        array_agg(i.inhparent) FROM pg_inherits i
+    WHERE
+        i.inhrelid = {{foid}}::oid GROUP BY i.inhrelid) AS inherits
+    {% endif %}
+FROM
+    pg_class c
+JOIN
+    pg_foreign_table ft ON c.oid=ft.ftrelid
+LEFT OUTER JOIN
+    pg_foreign_server fs ON ft.ftserver=fs.oid
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=c.oid AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN
+    pg_namespace nsp ON (nsp.oid=c.relnamespace)
+WHERE
+    c.relnamespace = {{scid}}::oid
+    {% if foid %}
+    AND c.oid = {{foid}}::oid
+    {% endif %}
+ORDER BY c.relname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/update.sql
new file mode 100644
index 0000000..e7cc1a5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/update.sql
@@ -0,0 +1,139 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}{% if data.name != o_data.name %}
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, o_data.name) }}
+    RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}{% endif %}
+{% if data.owner %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    OWNER TO {{ data.owner }};
+{% endif %}
+{% if data.columns %}
+{% for c in data.columns.deleted %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    DROP COLUMN {{conn|qtIdent(c.attname)}};
+{% endfor -%}
+{% for c in data.columns.added %}
+{% if (not c.inheritedfrom or c.inheritedfrom =='' or  c.inheritedfrom == None or  c.inheritedfrom == 'None' ) %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endif %}
+{% endfor -%}
+{% for c in data.columns.changed %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    DROP COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['attname'])}};
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}
+{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
+{% if c.typdefault %} DEFAULT {{c.typdefault}}{% endif %}
+{% if c.collname %} COLLATE {{c.collname}}{% endif %};
+{% endfor %}
+{% endif %}
+{% if data.inherits and data.inherits|length > 0%}
+{% if o_data.inherits == None or o_data.inherits == 'None' %}
+{% set inherits = '' %}
+{% else %}
+{% set inherits = o_data.inherits %}
+{% endif %}
+{% for i in data.inherits %}
+{% if i not in inherits %}{% if i %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }} INHERIT {{i}};
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if o_data.inherits and 'inherits' in data %}
+{% if data.inherits == None or data.inherits == 'None' %}
+{% set inherits = '' %}
+{% else %}
+{% set inherits = data.inherits %}
+{% endif %}
+{% for i in o_data.inherits %}{% if i not in inherits %}{% if i %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }} NO INHERIT {{i}};{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if data.constraints %}
+{% for c in data.constraints.deleted %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    DROP CONSTRAINT {{conn|qtIdent(c.conname)}};
+{% endfor -%}
+{% for c in data.constraints.added %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif %};
+{% endfor %}
+{% for c in data.constraints.changed %}
+{% if c.convalidated %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    VALIDATE CONSTRAINT {{ conn|qtIdent(c.conname) }};
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if data.ftoptions %}
+{% for o in data.ftoptions.deleted %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    OPTIONS ( DROP {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+{% for o in data.ftoptions.added %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    OPTIONS (ADD {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+{% for o in data.ftoptions.changed %}
+{% if o.option and o.value %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    OPTIONS (SET {{o.option}} {{o.value|qtLiteral}});
+{% endif %}
+{% endfor %}
+{% endif -%}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+
+{{ SECLABLE.UNSET(conn, 'FOREIGN TABLE', name, r.provider, o_data.basensp) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.label, o_data.basensp) }}
+{% endfor %}
+{% endif -%}
+{% if data.description %}
+
+COMMENT ON FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+IS {{ data.description|qtLiteral }};
+{% endif -%}
+{% if data.basensp %}
+
+ALTER FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, name) }}
+    SET SCHEMA {{ conn|qtIdent(data.basensp) }};
+{% endif %}
+{% endif %}

---

From: Neel Patel <[email-masked]>
Date: Fri, 29 Apr 2016 12:20:38 +0530

Hi Khushboo,

Below are the observations.

   - When we create the new Foreign Table with column name and types then
   it shows NULL along with column name and type in properties tab.

             e.g. column_1 xml[] NULL

   - Once we inherits the table from another table then column and another
   parameters of inherited table should not allowed to change.
   - When we create any foreign table then same foreign table is also
   listed under "Tables" node.
   - SQL is not generated properly. Please find below SQL which gives error
   during execution.

           CREATE FOREIGN TABLE public.test_2
           (id1 integer NOT NULL DEFAULT12 COLLATEpg_catalog."POSIX")
               SERVER fsrv;

   - When we create the new foreign table with security label then no SQL
   is generated for security label.
   - In Edit mode, when we provide security label with both value
   "provider" and "security label" then security label is displayed NULL

            e.g.  SECURITY LABEL FOR frgn_table ON FOREIGN TABLE
public.fsrv_table IS NULL;

   - During creation of the column, when we remove the collation then it
   gives below error.

               TypeError: item is undefined

   - Delete/Drop cascade functionality is not working, we are getting below
   error.

               TypeError: self.canDrop.apply is not a function

   - When we edit the foreign table and try to remove the existing "Data
   Type" of column then it gives below error.

              TypeError: this.dataAdapter is null

   - Create the new foreign table and click on ADD button in Column tab and
   do not provide any column name and data type. Need to do proper validation
   in Column tab for all parameters. Currently if user do not provide any
   value then wrong SQL is getting generated.

           CREATE FOREIGN TABLE public.test_4
           (None None NULL)
              SERVER test_fsrv;

   - When we do not provide the Check parameters in constraint then it
   gives SQL syntax error.

           CREATE FOREIGN TABLE public.test_5
           ()
               SERVER test_fsrv;

          ALTER FOREIGN TABLE public.test_5
              ADD CONSTRAINT test CHECK () NOT VALID;

   - If we edit foreign table and change the schema then it gives below
   error.

              IndexError: list index out of range

   - We should have proper indentation in the SQL tab once we give the
   parameters. Currently it looks like below for "Options" value.

            CREATE FOREIGN TABLE "1_test"."5_test"
            ()
                SERVER asas
                OPTIONS (test1 'test2'
            , test3 'test4');

   - If user provide foreign table name and do not provide foreign server
   and click on SQL tab then we are getting error on browser side as below. We
   should have proper error handling in this case.

             Couldn't find the required parameter (ftsrvname).

   - Create the foreign table, add the constraint and do not provide any
   constraint information then SQL generated is wrong.

           CREATE FOREIGN TABLE "1_test"."9_test"
           ()
               SERVER test_fsrv;

          ALTER FOREIGN TABLE "1_test"."9_test"
              ADD CONSTRAINT None CHECK ();

   - When we click on the foreign table collection node then "Comment"
   column is blank even though we have comment in the foreign table.
   - Create the foreign table on PG 9.1 and after pressing Save button we
   are getting below error.

                "the JSON object must be str, not 'list'"

   - When we delete the options parameters then it gives SQL error because
   DROP statement does not include the value.

              ALTER FOREIGN TABLE public.test_12
                  OPTIONS ( DROP ser2 'val2');

   - There are some new functionality added in PG 9.5. Do we really need to
   implement ? Need to discuss with Dave/Ashesh. Below are the new
   functionality.

               - In create foreign table,we have added column constraint
but "table constraint" is added from 9.5.Do we really require to add table
constraint ?
               - In alter foreign table, below are the new functionality
added.
                     1.  ALTER [ COLUMN ] column_name SET STORAGE { PLAIN |
EXTERNAL | EXTENDED | MAIN }
                     2.  DISABLE TRIGGER [ trigger_name | ALL | USER ]
                     3.  ENABLE TRIGGER [ trigger_name | ALL | USER ]
                     4.  ENABLE REPLICA TRIGGER trigger_name
                     5.  ENABLE ALWAYS TRIGGER trigger_name
                     6.  SET WITH OIDS
                     7.  SET WITHOUT OIDS

Do let us know in case of any queries.

Thanks,
Neel Patel

On Tue, Apr 5, 2016 at 2:27 PM, Khushboo Vashi <
[email-masked]> wrote:

>> - Used 'NodeByListControl' to get schemas, in the foreign_table.js file
>> as suggested by Ashesh to avoid code redundancy.
>>

---

From: Khushboo Vashi <[email-masked]>
Date: Wed, 4 May 2016 12:21:47 +0530

Hi Dave,

In PG 9.5, for Foreign Table Editing, below new functionalities are added:

   - ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED
   | MAIN }
   - DISABLE TRIGGER [ trigger_name | ALL | USER ]
   - ENABLE TRIGGER [ trigger_name | ALL | USER ]
   - ENABLE REPLICA TRIGGER trigger_name
   - ENABLE ALWAYS TRIGGER trigger_name
   - SET WITH OIDS
   - SET WITHOUT OIDS

As per our conversation yesterday, we need to incorporate these
functionalities into pgAdmin4. Here I have some queries regarding the
implementation. Under the Foreign Table Node, we don't have separate nodes
for columns, triggers and constraints as table. So in order to add above
Trigger related functionalities, we will have to add Trigger Node under the
Foreign Table. And If we add Triggers, then Do we need to add Columns and
Constraints as separate nodes under the Foreign Table?

Thanks,

Khushboo

On Fri, Apr 29, 2016 at 12:20 PM, Neel Patel <[email-masked]>
wrote:

>    - Once we inherits the table from another table then column and
>    another parameters of inherited table should not allowed to change.
>    - SQL is not generated properly. Please find below SQL which gives
>    error during execution.
>
>    - Delete/Drop cascade functionality is not working, we are getting
>    below error.
>
>    - Create the new foreign table and click on ADD button in Column tab
>    and do not provide any column name and data type. Need to do proper
>    validation in Column tab for all parameters. Currently if user do not
>    provide any value then wrong SQL is getting generated.
>
>    - When we delete the options parameters then it gives SQL error
>    because DROP statement does not include the value.
>
>    - There are some new functionality added in PG 9.5. Do we really need
>    to implement ? Need to discuss with Dave/Ashesh. Below are the new
>    functionality.
>
> but "table constraint" is added from 9.5.Do we really require to add
> table constraint ?
>                      1.  ALTER [ COLUMN ] column_name SET STORAGE { PLAIN
> | EXTERNAL | EXTENDED | MAIN }
>> 2. NodeAjaxOptionsCell Transform change patch, on which Ashesh and
>> Murtuza are working
>>
>> Thanks,
>> Khushboo
>>
>>
>>
>>

---

From: Dave Page <[email-masked]>
Date: Wed, 4 May 2016 12:07:37 +0100

Hi

On Wed, May 4, 2016 at 7:51 AM, Khushboo Vashi
<[email-masked]> wrote:
> ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED |
> MAIN }
> DISABLE TRIGGER [ trigger_name | ALL | USER ]
> ENABLE TRIGGER [ trigger_name | ALL | USER ]
> ENABLE REPLICA TRIGGER trigger_name
> ENABLE ALWAYS TRIGGER trigger_name
> SET WITH OIDS
> SET WITHOUT OIDS
>

Following discussion with Ashesh and some further thought, please put
these enhancements on hold. We'll add them in a future release, as
it's just too much work to do right now.

Please add a TODO file to the root directory of pgAdmin 4, and add
these as items for later completion.

Thanks.

-- 
Dave Page
VP, Chief Architect, Tools & Installers
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

---

From: Khushboo Vashi <[email-masked]>
Date: Wed, 4 May 2016 16:49:38 +0530

Hi,

On Wed, May 4, 2016 at 4:37 PM, Dave Page <[email-masked]>
wrote:

> Okay.

> Sure. I will do that.

---

From: Khushboo Vashi <[email-masked]>
Date: Fri, 6 May 2016 15:55:01 +0530

Hi,

Please find the attached patch for the Foreign Table Node with fixed issues.

Thanks,
Khushboo

>    it shows NULL along with column name and type in properties tab.
I think if its NOT NULL, then it should be NULL and it is default, so this
should be okay.

> Done

> This bug is related to Table node and Harshal is working on this issue.

>            (id1 integer NOT NULL DEFAULT12 COLLATEpg_catalog."POSIX")
Done

> Done

> public.fsrv_table IS NULL;
Done

>                TypeError: item is undefined
Done

>                TypeError: self.canDrop.apply is not a function
Done

>               TypeError: this.dataAdapter is null
Done

>            CREATE FOREIGN TABLE public.test_4
Done

>               ADD CONSTRAINT test CHECK () NOT VALID;
Done

>               IndexError: list index out of range
Done

>             CREATE FOREIGN TABLE "1_test"."5_test"
Done

>              Couldn't find the required parameter (ftsrvname).
Done

>               ADD CONSTRAINT None CHECK ();
Done

> Done

>                 "the JSON object must be str, not 'list'"
Done

>                   OPTIONS ( DROP ser2 'val2');
Done

> As per the discussion, we will add these functionality into the next
phase.

index 0000000..3248a93
@@ -0,0 +1,1053 @@
+    make_response as ajax_response, internal_server_error, gone
+                        data[key] = [] if\
+                            type(req[key]) == list and len(req[key]) == 0 else \
+                            json.loads(req[key])
+        if not data:
+            return gone(gettext("""
+Could not find the foreign table in the database.
+It may have been removed by another user or
+shifted to the another schema.
+"""))
+                if not old_data:
+                    return gone(gettext("""
+        Could not find the foreign table in the database.
+        It may have been removed by another user or
+        shifted to the another schema.
+        """))
+        if len(res['rows']) == 0:

literal 0
HcmV?d00001

literal 0
HcmV?d00001

index 0000000..ab151f1
@@ -0,0 +1,694 @@
+      if (_.isUndefined(data.security_label) ||
+        _.isNull(data.security_label) ||
+        String(data.security_label).replace(/^\s+|\s+$/g, '') == '') {
+        editable: 'is_editable_column', cellHeaderClasses: 'width_percent_20'
+        editable: 'is_editable_column', cellHeaderClasses: 'width_percent_20',
+          if(!(_.isUndefined(m.get('inheritedid'))
+            || _.isNull(m.get('inheritedid'))
+            || _.isUndefined(m.get('inheritedfrom'))
+            || _.isNull(m.get('inheritedfrom')))) { return false; }
+          if(!(_.isUndefined(m.get('inheritedid'))
+            || _.isNull(m.get('inheritedid'))
+            || _.isUndefined(m.get('inheritedfrom'))
+            || _.isNull(m.get('inheritedfrom')))) { return false; }
+          if(!(_.isUndefined(m.get('inheritedid'))
+            || _.isNull(m.get('inheritedid'))
+            || _.isUndefined(m.get('inheritedfrom'))
+            || _.isNull(m.get('inheritedfrom')))) { return false; }
+        cell: 'boolean',type: 'switch', editable: 'is_editable_column',
+        min_version: 90300, editable: 'is_editable_column',
+        type: 'text', visible: false, mode: ['properties', 'edit'],
+      errmsg;
+      if (_.isUndefined(this.get('attname')) || String(this.get('attname')).replace(/^\s+|\s+$/g, '') == '') {
+        err['name'] = '{{ _('Column Name can not be empty!') }}';
+        errmsg = errmsg || err['attname'];
+      if (_.isUndefined(this.get('datatype')) || String(this.get('datatype'))
+        err['basensp'] = '{{ _('Column Datatype can not be empty!') }}';
+        errmsg = errmsg || err['datatype'];
+    is_editable_column: function(m) {
+      return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
+       || _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
+          var fnd_columns = columns.where({inheritedid: parseInt(i)});
+          if (fnd_columns && fnd_columns.length <= 0) {
+      errmsg;
+      if (_.isUndefined(this.get('conname')) || String(this.get('conname')).replace(/^\s+|\s+$/g, '') == '') {
+        err['conname'] = '{{ _('Constraint Name can not be empty!') }}';
+        errmsg = errmsg || err['conname'];
+      if (_.isUndefined(this.get('consrc')) || String(this.get('consrc'))
+        err['consrc'] = '{{ _('Constraint Check can not be empty!') }}';
+        errmsg = errmsg || err['consrc'];
index 0000000..5e061e9
@@ -0,0 +1,29 @@
index 0000000..bc731c5
+    ftoptions, nspname as basensp, description
index 0000000..3e5b4fc
+OPTIONS ( DROP {{o.option}});
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.security_label, o_data.basensp) }}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.security_label, o_data.basensp) }}
index 0000000..52f5660
index 0000000..bc731c5
+    ftoptions, nspname as basensp, description
index 0000000..c285aa6
+OPTIONS ( DROP {{o.option}});
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.security_label, o_data.basensp) }}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.security_label, o_data.basensp) }}
index 0000000..99bf93e
@@ -0,0 +1,47 @@
+{% if c.collname %} COLLATE {{c.collname}}{% endif %}
index 0000000..d314564
@@ -0,0 +1,53 @@
+WITH INH_TABLES AS
+     distinct on (at.attname) attname, ph.inhparent AS inheritedid, ph.inhseqno,
+     concat(nmsp_parent.nspname, '.',parent.relname ) AS inheritedfrom
+    GROUP BY at.attname, ph.inhparent, ph.inhseqno, inheritedfrom
+    ORDER BY at.attname, ph.inhparent, ph.inhseqno, inheritedfrom
+SELECT INH.inheritedfrom, INH.inheritedid,
+        att.attname || ' ' || format_type(t.oid, att.atttypmod) || ' ' ||
+LEFT JOIN
+    INH_TABLES as INH ON att.attname = INH.attname
+    ORDER BY att.attname;
index 0000000..bc731c5
+    ftoptions, nspname as basensp, description
index 0000000..76a2625
+    OPTIONS ( DROP {{o.option}});
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.security_label, o_data.basensp) }}
+{{ SECLABLE.SET(conn, 'FOREIGN TABLE', name, r.provider, r.security_label, o_data.basensp) }}

---

From: Murtuza Zabuawala <[email-masked]>
Date: Fri, 6 May 2016 20:38:51 +0530

Hi Khushboo,

Please find comments as below,

I pulled latest version of code and then I applied v2 patch.

1) Once applying patch, When I re-started pgAdmin4 server again, I got below error (Screenshot is also attached),
   ( FYI, I was not able to re-produce it again second time. )

  File "/Users/murtuza/Desktop/pgadmin4/web/pgadmin/utils/__init__.py", line 37, in create_module_preference
    self.preference = Preferences(self.name, None)
  File "/Users/murtuza/Desktop/pgadmin4/web/pgadmin/utils/preferences.py", line 261, in __init__
    db.session.commit()
…..
…..
    cursor.execute(statement, parameters)
sqlalchemy.exc.OperationalError: (OperationalError) database is locked 'INSERT INTO module_preference (name) VALUES (?)' ('NODE-foreign-table’,)

2) I am not able to create/open Foreign table node as I am getting errors from JS side  (when I expand the schema node & do not get Create context menu as well)
    Please find screenshots for both scenario.

> On 06-May-2016, at 3:55 pm, Khushboo Vashi <[email-masked]> wrote:
> 
> Hi,
> 
> On Fri, Apr 29, 2016 at 12:20 PM, Neel Patel <[email-masked] <mailto:[email-masked]>> wrote:
> Hi Khushboo,
> 
> When we create the new Foreign Table with column name and types then it shows NULL along with column name and type in properties tab.
>              e.g. column_1 xml[] NULL 
> I think if its NOT NULL, then it should be NULL and it is default, so this should be okay. 
> Once we inherits the table from another table then column and another parameters of inherited table should not allowed to change.
> Done 
> When we create any foreign table then same foreign table is also listed under "Tables" node.
> SQL is not generated properly. Please find below SQL which gives error during execution.
> When we create the new foreign table with security label then no SQL is generated for security label.
> Done 
> In Edit mode, when we provide security label with both value "provider" and "security label" then security label is displayed NULL
>             e.g.  SECURITY LABEL FOR frgn_table ON FOREIGN TABLE public.fsrv_table IS NULL;
> Done 
> During creation of the column, when we remove the collation then it gives below error.
> Delete/Drop cascade functionality is not working, we are getting below error.
> When we edit the foreign table and try to remove the existing "Data Type" of column then it gives below error.
> Create the new foreign table and click on ADD button in Column tab and do not provide any column name and data type. Need to do proper validation in Column tab for all parameters. Currently if user do not provide any value then wrong SQL is getting generated.
> When we do not provide the Check parameters in constraint then it gives SQL syntax error.
> If we edit foreign table and change the schema then it gives below error.
> We should have proper indentation in the SQL tab once we give the parameters. Currently it looks like below for "Options" value.
> If user provide foreign table name and do not provide foreign server and click on SQL tab then we are getting error on browser side as below. We should have proper error handling in this case.
> Create the foreign table, add the constraint and do not provide any constraint information then SQL generated is wrong.
> When we click on the foreign table collection node then "Comment" column is blank even though we have comment in the foreign table.
> Done 
> Create the foreign table on PG 9.1 and after pressing Save button we are getting below error.
> When we delete the options parameters then it gives SQL error because DROP statement does not include the value.
> There are some new functionality added in PG 9.5. Do we really need to implement ? Need to discuss with Dave/Ashesh. Below are the new functionality.
>                - In create foreign table,we have added column constraint but "table constraint" is added from 9.5.Do <http://9.5.do/> we really require to add table constraint ?
>                - In alter foreign table, below are the new functionality added.
>                      1.  ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
> As per the discussion, we will add these functionality into the next phase. 
> On Tue, Apr 5, 2016 at 2:27 PM, Khushboo Vashi <[email-masked] <mailto:[email-masked]>> wrote:
> Hi,
> 
> 2. NodeAjaxOptionsCell Transform change patch, on which Ashesh and Murtuza are working
> 
> Thanks,
> Khushboo
> 
> 
> 
> 
> On Wed, Feb 24, 2016 at 2:57 PM, Khushboo Vashi <[email-masked] <mailto:[email-masked]>> wrote:
> Hi,
> 
> - Used 'NodeByListControl' to get schemas, in the foreign_table.js file as suggested by Ashesh to avoid code redundancy.
> 
> - Applied 'Security Label Macro'  Patch (Implemented by Harshal). 
>   To test the Foreign Table patch, 'Security Label Macro' patch must be applied first as that is not committed yet.
> 
> On Tue, Feb 23, 2016 at 6:53 PM, Khushboo Vashi <[email-masked] <mailto:[email-masked]>> wrote:
> Hi,
> 
> The patch will be modified after Types module implementation as we need to populate Base Type  and some Type related validations from the Types module.
> 
> Sent via pgadmin-hackers mailing list ([email-masked] <mailto:[email-masked]>)
> http://www.postgresql.org/mailpref/pgadmin-hackers <http://www.postgresql.org/mailpref/pgadmin-hackers>
> 
> 
> 
> <pgAdmin4_Foreign_tables_ver2.patch>
> --

---

From: Khushboo Vashi <[email-masked]>
Date: Fri, 6 May 2016 22:15:54 +0530

Hi Murtuza,

Have you applied Dependent Cell patch on this? As the Foreign table is
dependent on that.

Thanks,
Khushboo
On 6 May 2016 20:39, "Murtuza Zabuawala" <[email-masked]>
wrote:

> 1) Once applying patch, When I re-started pgAdmin4 server again, I got
> below error (Screenshot is also attached),
> *   ( FYI, I was not able to re-produce it again second time. )*
>
>
>
>
>
> *  File "/Users/murtuza/Desktop/pgadmin4/web/pgadmin/utils/__init__.py",
> line 37, in create_module_preference    self.preference =
> Preferences(self.name <http://self.name>, None)  File
> "/Users/murtuza/Desktop/pgadmin4/web/pgadmin/utils/preferences.py", line
> 261, in __init__    db.session.commit()…..*
>
> *…..*
>
> *    cursor.execute(statement, parameters)sqlalchemy.exc.OperationalError:
> (OperationalError) database is locked 'INSERT INTO module_preference (name)
> VALUES (?)' ('NODE-foreign-table’,)*
>
>
> 2) I am not able to create/open Foreign table node as I am getting errors
> from JS side  (when I expand the schema node & do not get Create context
> menu as well)
>>    - When we create the new Foreign Table with column name and types
>>    then it shows NULL along with column name and type in properties tab.
>>
>>              e.g. column_1 xml[] NULL
>>
>>    - When we create the new foreign table with security label then no
>>    SQL is generated for security label.
>>
>> Done
>
>>
>>    - If user provide foreign table name and do not provide foreign
>>    server and click on SQL tab then we are getting error on browser side as
>>    below. We should have proper error handling in this case.
>>
>>    - Create the foreign table on PG 9.1 and after pressing Save button
>>    we are getting below error.
>>
>> but "table constraint" is added from 9.5.Do <http://9.5.do/> we really
>> require to add table constraint ?
>>>>> The patch will be modified after Types module implementation as we
>>>>> need to populate Base Type  and some Type related validations from the
>>>>> Types module.
>>>>>

---

From: Murtuza Zabuawala <[email-masked]>
Date: Mon, 9 May 2016 10:43:10 +0530

Hi Khushboo,

No I did not. let me apply it and try again.

Thanks,
Murtuza

> On 06-May-2016, at 10:15 pm, Khushboo Vashi <[email-masked]> wrote:
> 
> Hi Murtuza,
> 
> Have you applied Dependent Cell patch on this? As the Foreign table is dependent on that.
> 
> Thanks,
> Khushboo
> 
> On 6 May 2016 20:39, "Murtuza Zabuawala" <[email-masked] <mailto:[email-masked]>> wrote:
> Hi Khushboo,
> 
>     self.preference = Preferences(self.name <http://self.name/>, None)
> <Screenshot from 2016-05-06 08-00-19.png><Screenshot from 2016-05-06 07-59-45.png><Screen Shot 2016-05-06 at 8.18.12 pm.png>
> 
> 
>> On 06-May-2016, at 3:55 pm, Khushboo Vashi <[email-masked] <mailto:[email-masked]>> wrote:
>> 
>> Hi,
>>

---

From: Murtuza Zabuawala <[email-masked]>
Date: Mon, 9 May 2016 13:46:30 +0530

Hi Khushboo,

(Tested with Python3)

1) Default string value is not quoted properly which causing SQL to break, here default string should be 'my test string' for myCol2.

CREATE FOREIGN TABLE "Test123"."test1_Server"
("myCol" bigint NOT NULL DEFAULT 213 COLLATE pg_catalog."C",
"myCol2" character varying(50) NOT NULL DEFAULT my test string COLLATE pg_catalog."C.UTF-8")
    INHERITS ("Test123".abc, pem.chart)
    SERVER test_fw_server;

2) When Length & Precision, does not clear when user selects another data type
- First select Numeric provide Length & Precision and then choose abstime 
  data type which have neither Length & Precision 
- It will not clear/ not allow user to clear old values & generates wrong SQL

ALTER FOREIGN TABLE "Test123"."test1_TT1"
    ADD COLUMN col1 abstime(50 , 2) NOT NULL COLLATE pg_catalog."C";

3) I am allowed to select self table as inherited table

ALTER FOREIGN TABLE pem."test1_TT1" INHERIT pem."test1_TT1";

4) Wrong SQL generated for array like data types

ALTER FOREIGN TABLE pem."test1_TT1"
    ADD COLUMN name character[](50 ) NOT NULL COLLATE pg_catalog."POSIX";

Correct SQL:
   
ALTER FOREIGN TABLE pem."test1_TT1"
       ADD COLUMN name character(50)[] NOT NULL COLLATE pg_catalog."POSIX";

5) I am allowed to enter duplicate options but as per postgres documentation 
   we should not allow duplicate options 

ALTER FOREIGN TABLE pem."test1_TT1"
    OPTIONS (ADD op1 'val1');

ALTER FOREIGN TABLE pem."test1_TT1"
    OPTIONS (ADD op1 'val1');

ALTER FOREIGN TABLE pem."test1_TT1"
    OPTIONS (ADD op1 'val2');

6) Created table with Special name (eg: table name => "@Test#" ) & it breaks when we clicks on it.

  File "/home/murtuza/projects/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py", line 414, in properties
    data = self._fetch_properties(gid, sid, did, scid, foid)
  File "/home/murtuza/projects/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py", line 982, in _fetch_properties
    c['typlen'] = int(typlen)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'

7) While dropping any foreign table gives this error but table gets deleted from browser tree.

s/databases/schemas/foreign_tables/__init__.py", line 414, in properties
    data = self._fetch_properties(gid, sid, did, scid, foid)
  File "/home/murtuza/projects/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py", line 982, in _fetch_properties
    c['typlen'] = int(typlen)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'

Can be added into TODO:
=====================

1) We should not allowed user to Inherits from catalog tables like pg_catalog.pg_type;

2) Minor SQL alignment when only server & table name was given 

CREATE FOREIGN TABLE "Test123"."test1_Server"
()
    SERVER test_fw_server;

Expected:
---------
CREATE FOREIGN TABLE "Test123"."test1_Server" ()
    SERVER test_fw_server;

3) Collation client side validation missing for columns, not every data type support Collations,
   For other data types it should be disable just like Length & Precision.

Regards,
Murtuza

> On 09-May-2016, at 10:43 am, Murtuza Zabuawala <[email-masked]> wrote:
> 
> Hi Khushboo,
> 
>> On 06-May-2016, at 10:15 pm, Khushboo Vashi <[email-masked] <mailto:[email-masked]>> wrote:
>> 
>> Hi Murtuza,
>>

---

From: Khushboo Vashi <[email-masked]>
Date: Mon, 9 May 2016 17:50:59 +0530

Hi,

Please find the attached updated patch for the Foreign Tables module.

Thanks,
Khushboo

On Mon, May 9, 2016 at 1:46 PM, Murtuza Zabuawala <
[email-masked]> wrote:

> *1)* Default string value is not quoted properly which causing SQL to
> break, here default string should be 'my test string' for myCol2.
>
> I think this behavior is perfect as we can't add single quotes for all the
data-types. User should have to add himself if required.

> *2)* When Length & Precision, does not clear when user selects another
> data type
Done

> *3)* I am allowed to select self table as inherited table
>

> *4)* Wrong SQL generated for array like data types
>

> *5)* I am allowed to enter duplicate options but as per postgres
> documentation

> *6)* Created table with Special name (eg: table name => "@Test#" ) & it
> breaks when we clicks on it.
>
>   File
> "/home/murtuza/projects/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py",
> line 414, in properties
>   File
> "/home/murtuza/projects/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py",
> line 982, in _fetch_properties

Couldn't reproduce but I think while fixing above issues it is resolved.

> *7) *While dropping any foreign table gives this error but table gets
> deleted from browser tree.
>
>   File
> "/home/murtuza/projects/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py",
> line 982, in _fetch_properties
 Couldn't reproduce but I think while fixing above issues it is resolved.

>
> *Can be added into TODO:*
> *=====================*
>
> *1)* We should not allowed user to Inherits from catalog tables like
> pg_catalog.pg_type;
>
> *2)* Minor SQL alignment when only server & table name was given
>
> *3)* Collation client side validation missing for columns, not every data
> type support Collations,
> On 09-May-2016, at 10:43 am, Murtuza Zabuawala <
> [email-masked]> wrote:
>
> Hi Khushboo,
>
> On 6 May 2016 20:39, "Murtuza Zabuawala" <
> [email-masked]> wrote:
>
>> Hi Khushboo,
>>
>> Preferences(self.name <http://self.name/>, None)  File
>> *    cursor.execute(statement,
>> parameters)sqlalchemy.exc.OperationalError: (OperationalError) database is
>> locked 'INSERT INTO module_preference (name) VALUES (?)'
>> ('NODE-foreign-table’,)*
>>
>>
>> <Screenshot from 2016-05-06 08-00-19.png><Screenshot from 2016-05-06
>> 07-59-45.png><Screen Shot 2016-05-06 at 8.18.12 pm.png>
>>
>>
>> On Fri, Apr 29, 2016 at 12:20 PM, Neel Patel <[email-masked]
>> > wrote:
>>
>>> Hi Khushboo,
>>>
>>>    - During creation of the column, when we remove the collation then
>>>    it gives below error.
>>>
>>>    - When we edit the foreign table and try to remove the existing
>>>    "Data Type" of column then it gives below error.
>>>
>>>    - Create the foreign table, add the constraint and do not provide
>>>    any constraint information then SQL generated is wrong.
>>>
>>>    - There are some new functionality added in PG 9.5. Do we really
>>>    need to implement ? Need to discuss with Dave/Ashesh. Below are the new
>>>    functionality.
>>>
>>>>> - Used 'NodeByListControl' to get schemas, in the foreign_table.js
>>>>> file as suggested by Ashesh to avoid code redundancy.
>>>>>
>>>>>   To test the Foreign Table patch, 'Security Label Macro' patch must
>>>>> be applied first as that is not committed yet.
>>>>>
index 0000000..f4c2b3a
@@ -0,0 +1,1075 @@
+                                            'get_tables.sql']), foid=foid)
+                if 'columns' in data and 'added' in data['columns']:
+                    data['columns']['added'] = self._format_columns(
+                        data['columns']['added'])
+                if 'columns' in data and 'changed' in data['columns']:
+                    data['columns']['changed'] = self._format_columns(
+                        data['columns']['changed'])
+                data['columns'] = self._format_columns(data['columns'])
+    def _format_columns(self, columns):
+        Format Table Columns.
+        for c in columns:
+            if '[]' in c['datatype']:
+                c['datatype'] = c['datatype'].replace('[]', '')
+                c['isArrayType'] = True
+                c['isArrayType'] = False
+        return columns
+                substr = c['fulltype'][c['fulltype'].find("(") + 1:c['fulltype'].find(")")]
+                    c['precision'] = None

literal 0
HcmV?d00001

literal 0
HcmV?d00001

index 0000000..b13e85c
@@ -0,0 +1,706 @@
+      dependentChanged: function () {
+          this.model.trigger('pgadmin-view:transform:error', self.model, self.field, e);
+          'cache_level': 'database',
+            if (this.field.get('mode') == 'edit') {
+              oid = this.model.get('oid');
+              s = _.findWhere(d, {'id': oid});
+              if (s) {
+                d = _.reject(d, s);
+          type: 'collection', group: 'Options', mode: ['edit', 'create'],
+          model: OptionsModel, canAdd: true, canDelete: true, canEdit: false,
+          control: 'unique-col-collection', uniqueCol : ['option']
index 0000000..47b8d33
+{% if not loop.first%}    {% endif %}{{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% if c.attnotnull %}
index 0000000..0780766
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}
index 0000000..09d7f25
+{% if not loop.first%}    {% endif %}{{conn|qtIdent(c.attname)}} {{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% if c.attnotnull %}
index 0000000..de98b53
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}
+ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}
index 0000000..ecef1d8
+{{conn|qtIdent(c.attname)}} {{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}
index 0000000..83445a2
+{% if foid %}
+    AND c.oid <> {{foid}}::oid
index 0000000..cd91a5e
+    ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}
+    ADD COLUMN {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}

---

From: Ashesh Vashi <[email-masked]>
Date: Mon, 9 May 2016 17:53:35 +0530

On Mon, May 9, 2016 at 5:50 PM, Khushboo Vashi <
[email-masked]> wrote:

>> I think this behavior is perfect as we can't add single quotes for all
> the data-types. User should have to add himself if required.
>
This is correct behaviour as of now.

Default value can be an expression too.
Hence - we need to rely on the user to provide correct value here.

We may improve this in the future.

--

Thanks & Regards,

Ashesh Vashi
EnterpriseDB INDIA: Enterprise PostgreSQL Company
<http://www.enterprisedb.com/>

*http://www.linkedin.com/in/asheshvashi*
<http://www.linkedin.com/in/asheshvashi>

>> *3)* Collation client side validation missing for columns, not every
>> data type support Collations,
>>> 2) I am not able to create/open Foreign table node as I am getting
>>> errors from JS side  (when I expand the schema node & do not get Create
>>> context menu as well)
>>> On Fri, Apr 29, 2016 at 12:20 PM, Neel Patel <
>>> [email-masked]> wrote:
>>>
>>>> Hi Khushboo,
>>>>
>>>>    - Create the new foreign table and click on ADD button in Column
>>>>    tab and do not provide any column name and data type. Need to do proper
>>>>    - If we edit foreign table and change the schema then it gives
>>>>    below error.
>>>>
>>>>                - In create foreign table,we have added column
>>>> constraint but "table constraint" is added from 9.5.Do <http://9.5.do/>
>>>> we really require to add table constraint ?

---

From: Dave Page <[email-masked]>
Date: Tue, 10 May 2016 12:03:58 +0100

>> Please find the attached updated patch for the Foreign Tables module.
Feedback from my initial testing:

- Owner is missing from the New dialogue.

- The Properties list shows "Select from the list" in a combo box for Owner
and
  Inherits.

- s/Oid/OID

- s/Foreign Server/Foreign server

- SQL Help filenames are not defined.

- SQL formatting is weird, e.g.

CREATE FOREIGN TABLE public.redis_table
(id text NOT NULL COLLATE pg_catalog."default",
value text NULL COLLATE pg_catalog."default")
    SERVER redis_server1
    OPTIONS (database '0');

instead of:

CREATE FOREIGN TABLE public.redis_table (
    id text NOT NULL COLLATE pg_catalog."default",
    value text NULL COLLATE pg_catalog."default"
)
    SERVER redis_server1
    OPTIONS (database '0');

- In the validation error messages, please replace ! with . and "can not"
with
  "cannot" for consistency with recently reviewed strings.

- s/mulitple/multiple (in the js).

- How do I manage the ACL?

Thanks.

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

---

From: Khushboo Vashi <[email-masked]>
Date: Wed, 11 May 2016 15:17:09 +0530

Hi,

Please find the attached updated patch for the Foreign Table module.

Thanks,
Khushboo

On Tue, May 10, 2016 at 4:33 PM, Dave Page <[email-masked]> wrote:

>
>
> On Mon, May 9, 2016 at 1:23 PM, Ashesh Vashi <
> [email-masked]> wrote:
>

> - The Properties list shows "Select from the list" in a combo box for
> Owner and
>   Inherits.
>

This is a generalize issue, I will create a new Kanban card for the same.

Done

Done

> Done. I missed this as I was following pgAdmin-3 as usual.

index 0000000..cc04047
@@ -0,0 +1,1118 @@
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+    parse_priv_to_db
+                                   'seclabels', 'inherits', 'acl']
+            elif ver >= 90200:
+                self.template_path = 'foreign_tables/sql/9.2_plus'
+                # Parse Privileges
+                if 'acl' in data and 'added' in data['acl']:
+                    data['acl']['added'] = parse_priv_to_db(data['acl']['added'],
+                                                   ["a", "r", "w", "x"])
+                if 'acl' in data and 'changed' in data['acl']:
+                    data['acl']['changed'] = parse_priv_to_db(
+                        data['acl']['changed'],["a", "r", "w", "x"])
+                if 'acl' in data and 'deleted' in data['acl']:
+                    data['acl']['deleted'] = parse_priv_to_db(
+                        data['acl']['deleted'],["a", "r", "w", "x"])
+                # Parse Privileges
+                if 'acl' in data:
+                    data['acl'] = parse_priv_to_db(data['acl'],
+                                                   ["a", "r", "w", "x"])
+        if self.manager.version >= 90200:
+            # Fetch privileges
+            SQL = render_template("/".join([self.template_path, 'acl.sql']),
+            status, aclres = self.conn.execute_dict(SQL)
+                return internal_server_error(errormsg=aclres)
+            # Get Formatted Privileges
+            data.update(self._format_proacl_from_db(aclres['rows']))
+    def _format_proacl_from_db(self, proacl):
+        Returns privileges.
+            proacl: Privileges Dict
+        privileges = []
+        for row in proacl:
+            priv = parse_priv_from_db(row)
+            privileges.append(priv)
+        return {"acl": privileges}

literal 0
HcmV?d00001

literal 0
HcmV?d00001

index 0000000..cbdfcd9
@@ -0,0 +1,726 @@
+      sqlAlterHelp: 'sql-alterforeigntable.html',
+      sqlCreateHelp: 'sql-createforeigntable.html',
+        /* Avoid multiple registration of menus */
+            // Set Current User
+            var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+            this.set({'owner': userInfo.name}, {silent: true});
+          relacl: [],
+          stracl: [],
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          node: 'role',  type: 'text'
+          id: 'ftsrvname', label:'{{ _('Foreign server') }}', cell: 'string', control: 'node-ajax-options',
+          id: 'relacl', label: '{{ _('Privileges') }}', cell: 'string',
+          type: 'text', group: '{{ _('Security') }}',
+          mode: ['properties'], min_version: 90200
+          id: 'acl', label: '{{ _('Privileges') }}', model: pgAdmin
+          .Browser.Node.PrivilegeRoleModel.extend(
+          {privileges: ['a','r','w','x']}), uniqueCol : ['grantee', 'grantor'],
+          editable: false, type: 'collection', group: '{{ _('Security') }}',
+          mode: ['edit', 'create'],
+          canAdd: true, canDelete: true, control: 'unique-col-collection',
+          min_version: 90200
+            err['name'] = '{{ _('Name cannot be empty.') }}';
+            err['basensp'] = '{{ _('Schema cannot be empty.') }}';
+            err['ftsrvname'] = '{{ _('Foreign server cannot be empty.') }}';
index 0000000..dcd6027
@@ -0,0 +1,36 @@
+CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}(
+    {{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% if c.attnotnull %}
index 0000000..5bb552d
@@ -0,0 +1,97 @@
+    OPTIONS (DROP {{o.option}});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/acl.sql
index 0000000..121a035
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/acl.sql
@@ -0,0 +1,35 @@
+    COALESCE(gt.rolname, 'public') AS grantee,
+    g.rolname AS grantor, array_agg(privilege_type) AS privileges,
+    array_agg(is_grantable) AS grantable
+        d.grantee, d.grantor, d.is_grantable,
+        CASE d.privilege_type
+        WHEN 'CONNECT' THEN 'c'
+        WHEN 'CREATE' THEN 'C'
+        WHEN 'DELETE' THEN 'd'
+        WHEN 'EXECUTE' THEN 'X'
+        WHEN 'INSERT' THEN 'a'
+        WHEN 'REFERENCES' THEN 'x'
+        WHEN 'SELECT' THEN 'r'
+        WHEN 'TEMPORARY' THEN 'T'
+        WHEN 'TRIGGER' THEN 't'
+        WHEN 'TRUNCATE' THEN 'D'
+        WHEN 'UPDATE' THEN 'w'
+        WHEN 'USAGE' THEN 'U'
+        ELSE 'UNKNOWN'
+        END AS privilege_type
+            (d).grantee AS grantee, (d).grantor AS grantor,
+            (d).is_grantable AS is_grantable,
+            (d).privilege_type AS privilege_type
+            (SELECT aclexplode(db.relacl) AS d FROM pg_class db
+            WHERE db.oid = {{foid}}::OID) a
+        ) d
+    ) d
+    LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+    LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/create.sql
index 0000000..497b124
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/create.sql
@@ -0,0 +1,44 @@
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}(
+    {{conn|qtIdent(c.attname)}} {{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% if c.attnotnull %}
+{% if data.acl %}
+{% for priv in data.acl %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.basensp) }}
+{% for r in data.seclabels %}{% if r.security_label and r.provider %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/delete.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/delete.sql
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_collations.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_collations.sql
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_columns.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_columns.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_columns.sql
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_constraints.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_constraints.sql
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_foreign_servers.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_foreign_servers.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_foreign_servers.sql
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_oid.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/get_oid.sql
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/node.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/node.sql
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/properties.sql
index 0000000..c557c87
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/properties.sql
+    c.oid, c.relname AS name, c.relacl, pg_get_userbyid(relowner) AS owner,
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/update.sql
index 0000000..7915eda
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.2_plus/update.sql
@@ -0,0 +1,129 @@
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+    ADD CONSTRAINT {{conn|qtIdent(c.conname)}} {% if c.consrc %} CHECK ({{c.consrc}}){% endif %} {% if c.connoinherit %} NO INHERIT{% endif %};
+{% if data.acl %}
+{% if 'deleted' in data.acl %}
+{% for priv in data.acl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, name, o_data.basensp) }}
+{% if 'changed' in data.acl %}
+{% for priv in data.acl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, name, o_data.basensp) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.basensp) }}
+{% if 'added' in data.acl %}
+{% for priv in data.acl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.basensp) }}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/acl.sql
index 0000000..121a035
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/templates/foreign_tables/sql/9.5_plus/acl.sql
@@ -0,0 +1,35 @@
+    COALESCE(gt.rolname, 'public') AS grantee,
+    g.rolname AS grantor, array_agg(privilege_type) AS privileges,
+    array_agg(is_grantable) AS grantable
+        d.grantee, d.grantor, d.is_grantable,
+        CASE d.privilege_type
+        WHEN 'CONNECT' THEN 'c'
+        WHEN 'CREATE' THEN 'C'
+        WHEN 'DELETE' THEN 'd'
+        WHEN 'EXECUTE' THEN 'X'
+        WHEN 'INSERT' THEN 'a'
+        WHEN 'REFERENCES' THEN 'x'
+        WHEN 'SELECT' THEN 'r'
+        WHEN 'TEMPORARY' THEN 'T'
+        WHEN 'TRIGGER' THEN 't'
+        WHEN 'TRUNCATE' THEN 'D'
+        WHEN 'UPDATE' THEN 'w'
+        WHEN 'USAGE' THEN 'U'
+        ELSE 'UNKNOWN'
+        END AS privilege_type
+            (d).grantee AS grantee, (d).grantor AS grantor,
+            (d).is_grantable AS is_grantable,
+            (d).privilege_type AS privilege_type
+            (SELECT aclexplode(db.relacl) AS d FROM pg_class db
+            WHERE db.oid = {{foid}}::OID) a
+        ) d
+    ) d
+    LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+    LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname;
index 0000000..ef57008
@@ -0,0 +1,61 @@
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}(
+    {{conn|qtIdent(c.attname)}} {{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}
+{% if data.acl %}
+{% for priv in data.acl %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.basensp) }}
index 0000000..3976a07
+    c.oid, c.relname AS name, c.relacl, pg_get_userbyid(relowner) AS owner,
index 0000000..52b54b3
@@ -0,0 +1,162 @@
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data.acl %}
+{% if 'deleted' in data.acl %}
+{% for priv in data.acl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, name, o_data.basensp) }}
+{% if 'changed' in data.acl %}
+{% for priv in data.acl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, name, o_data.basensp) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.basensp) }}
+{% if 'added' in data.acl %}
+{% for priv in data.acl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.basensp) }}

---

From: Murtuza Zabuawala <[email-masked]>
Date: Fri, 13 May 2016 10:50:17 +0530

*1)* If we delete one inherited table and add another one
It generated drop columns sql for that inherited table which throws error,
for example "Test123"."TEST" has col1, col2 in it, once we remove table
see the wrong drop column sql.

SQL:
----
ALTER FOREIGN TABLE pem.asdas
    DROP COLUMN col1;

ALTER FOREIGN TABLE pem.asdas
    DROP COLUMN col2;

ALTER FOREIGN TABLE pem.asdas INHERIT pemdata.number_of_wal_files;

ALTER FOREIGN TABLE pem.asdas NO INHERIT "Test123"."TEST";

Expected:
---------
ALTER FOREIGN TABLE pem.asdas INHERIT pemdata.number_of_wal_files;

ALTER FOREIGN TABLE pem.asdas NO INHERIT "Test123"."TEST";

*2)* In EDIT mode, Changing just column name of existing column first drops
the entire column
and adds it again, instead of just renaming column name

(*** Consider a case when user have data in the table & just want to rename
column col1 to col2
or any other attribute like default value/not null etc, why do we need to
drop entire column in that case? ***)

SQL:
----
ALTER FOREIGN TABLE test.test_ft
    DROP COLUMN c1;

ALTER FOREIGN TABLE test.test_ft
    ADD COLUMN c12 bigint NOT NULL;

Expected:
---------
ALTER FOREIGN TABLE test.test_ft
RENAME c1 TO c12;

*3)* We should not allow user to delete columns inherited from
other table in edit mode from UI

*4)* We should not allow user to delete constraint inherited from
other table in edit mode from UI

*5)* Issue from my last email still exists, changing type of column
creates wrong sql if it has Length/Precision.

eg: col12 was numeric(12) which I changed to aclitem
See the wrong sql

ALTER FOREIGN TABLE pem."test-2"
    DROP COLUMN col12;

ALTER FOREIGN TABLE pem."test-2"
    ADD COLUMN col12 aclitem(12) NOT NULL COLLATE pg_catalog."default";

Expected:
---------
ALTER FOREIGN TABLE pem."test-2"
    ADD COLUMN col12 aclitem;

*6)* EDIT mode, clearing Data Type from column throws Error on console

*7)* EDIT mode, If i have blank column row and clicks on SQL tab throws
error

    app_iter = app(environ, start_response)
TypeError: 'KeyError' object is not callable

*8)* User can clear Owner which we should not allow to clear OR we have to
generate SQL for that opration

*9)* I do not have option to provide Statistics attribute for column

--
Regards,
Murtuza Zabuawala

---

From: Khushboo Vashi <[email-masked]>
Date: Fri, 13 May 2016 15:25:20 +0530

Hi,

Please find the attached updated patch for the foreign table module.

Thanks,
Khushboo

>
> *2)* In EDIT mode, Changing just column name of existing column first
> drops the entire column
> (*** Consider a case when user have data in the table & just want to
> rename column col1 to col2

> Couldn't reproduce.

>
> *8)* User can clear Owner which we should not allow to clear OR we have
> to generate SQL for that opration
>
>
> Done

>>> - In the validation error messages, please replace ! with . and "can
>>> not" with
index 0000000..1fb6d3c
@@ -0,0 +1,1121 @@
+        cols = []
+            if len(c) > 0:
+                cols.append(c)
+        return cols

literal 0
HcmV?d00001

literal 0
HcmV?d00001

index 0000000..eded3d9
@@ -0,0 +1,751 @@
+      attstattarget: undefined
+                 m.set('typlen', undefined);
+                m.set('precision', undefined);
+        id: 'attstattarget', label:'{{ _('Statistics') }}', min_version: 90200,
+        cell: 'integer', type: 'int', editable: function(m) {
+        if (_.isUndefined(m.isNew) || m.isNew()) { return false; }
+        if (this.get('node_info').server.version < 90200){
+        min_version: 90300, editable: function(m) {
+          if (!(_.isUndefined(m.isNew)) && !m.isNew()) { return false; }
+      convalidated: true,
+      conislocal: undefined
+          id: 'owner', label:'{{ _('Owner') }}', cell: 'string',
+          control: Backform.NodeListByNameControl,
+          node: 'role',  type: 'text', select2: { allowClear: false }
+          columns: ['attname', 'datatype', 'typlen', 'precision', 'typdefault', 'attnotnull', 'attstattarget', 'collname', 'inheritedfrom'],
+          canDeleteRow: function(m) {
+          }, min_version: 90500, canDeleteRow: function(m) {
+            return (m.get('conislocal') == true) ? true : false
index 0000000..d34b2d0
@@ -0,0 +1,109 @@
+{% set col_name = o_data['columns'][c.attnum]['attname'] %}
+{% if c.attname != o_data['columns'][c.attnum]['attname'] %}
+{% set col_name = c.attname %}
+    RENAME COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['attname'])}} TO {{conn|qtIdent(c.attname)}};
+{% if c.attnotnull != o_data['columns'][c.attnum]['attnotnull'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}}{% if c.attnotnull %} SET{% else %} DROP{% endif %} NOT NULL;
+{% if c.datatype != o_data['columns'][c.attnum]['datatype'] or c.typlen != o_data['columns'][c.attnum]['typlen'] or
+c.precision != o_data['columns'][c.attnum]['precision'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}} TYPE {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %};
+{% if c.typdefault != o_data['columns'][c.attnum]['typdefault'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}}{% if c.typdefault %} SET DEFAULT {{c.typdefault}}{% else %} DROP DEFAULT{% endif %};
index 0000000..4c03ad7
+    attnotnull, attstattarget, attnum, format_type(t.oid, att.atttypmod) AS fulltype,
index 0000000..43a8298
+    conname, contype, consrc, conislocal
index 0000000..44fb433
@@ -0,0 +1,148 @@
+{% set col_name = o_data['columns'][c.attnum]['attname'] %}
+{% if c.attname != o_data['columns'][c.attnum]['attname'] %}
+{% set col_name = c.attname %}
+    RENAME COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['attname'])}} TO {{conn|qtIdent(c.attname)}};
+{% if c.attnotnull != o_data['columns'][c.attnum]['attnotnull'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}}{% if c.attnotnull %} SET{% else %} DROP{% endif %} NOT NULL;
+{% if c.datatype != o_data['columns'][c.attnum]['datatype'] or c.typlen != o_data['columns'][c.attnum]['typlen'] or
+c.precision != o_data['columns'][c.attnum]['precision'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}} TYPE {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %};
+{% if c.typdefault != o_data['columns'][c.attnum]['typdefault'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}}{% if c.typdefault %} SET DEFAULT {{c.typdefault}}{% else %} DROP DEFAULT{% endif %};
+{% if c.attstattarget != o_data['columns'][c.attnum]['attstattarget'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}} SET STATISTICS {% if c.attstattarget %}{{c.attstattarget}}{% else %}-1{% endif %};
index 0000000..18f70e8
+    att.attnotnull, att.attstattarget, att.attnum, format_type(t.oid, att.atttypmod) AS fulltype,
index 0000000..f8333dc
+    oid as conoid, conname, contype, consrc, connoinherit, convalidated, conislocal
index 0000000..1aed700
@@ -0,0 +1,182 @@
+{% set col_name = o_data['columns'][c.attnum]['attname'] %}
+{% if c.attname != o_data['columns'][c.attnum]['attname'] %}
+{% set col_name = c.attname %}
+    RENAME COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['attname'])}} TO {{conn|qtIdent(c.attname)}};
+{% if c.attnotnull != o_data['columns'][c.attnum]['attnotnull'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}}{% if c.attnotnull %} SET{% else %} DROP{% endif %} NOT NULL;
+{% if c.datatype != o_data['columns'][c.attnum]['datatype'] or c.typlen != o_data['columns'][c.attnum]['typlen'] or
+c.precision != o_data['columns'][c.attnum]['precision'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}} TYPE {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %};
+{% if c.typdefault != o_data['columns'][c.attnum]['typdefault'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}}{% if c.typdefault %} SET DEFAULT {{c.typdefault}}{% else %} DROP DEFAULT{% endif %};
+{% if c.attstattarget != o_data['columns'][c.attnum]['attstattarget'] %}
+    ALTER COLUMN {{conn|qtIdent(col_name)}} SET STATISTICS {% if c.attstattarget %}{{c.attstattarget}}{% else %}-1{% endif %};

---

From: Murtuza Zabuawala <[email-masked]>
Date: Sat, 14 May 2016 22:36:10 +0530

Hi,

One last otherwise rest of functionality looks good.

- We are not able to drop/delete any constraints, not even empty blank row
once added.

Regards,
Murtuza

---

From: Khushboo Vashi <[email-masked]>
Date: Sun, 15 May 2016 13:07:22 +0530

Hi,

Please find the updated patch for the foreign table.

Thanks,
Khushboo

On Sat, May 14, 2016 at 10:36 PM, Murtuza Zabuawala <
[email-masked]> wrote:

> Fixed

>>> or any other attribute like default value/not null etc, why do we need
>>> to drop entire column in that case? ***)
>>>
>>> SQL:
>>> ----

literal 0
HcmV?d00001

literal 0
HcmV?d00001

index 0000000..e6ed9f6
+            return (m.get('conislocal') == true || _.isUndefined(m.get('conislocal'))) ? true : false

---

From: Ashesh Vashi <[email-masked]>
Date: Mon, 16 May 2016 01:42:34 +0530

Thanks - committed!

--

Thanks & Regards,

Ashesh Vashi
EnterpriseDB INDIA: Enterprise PostgreSQL Company
<http://www.enterprisedb.com>

On Sun, May 15, 2016 at 1:07 PM, Khushboo Vashi <
[email-masked]> wrote:

>> - We are not able to drop/delete any constraints, not even empty blank
>> row once added.
>>
>> Fixed
>
>>
>> Regards,
>> Murtuza
>>
>>
>> --
>> Regards,
>> Murtuza Zabuawala
>>>> *7)* EDIT mode, If i have blank column row and clicks on SQL tab
>>>> throws error
>>>>
>>>> *8)* User can clear Owner which we should not allow to clear OR we
>>>> have to generate SQL for that opration
>>>>
>>>>
>>>> Done
>>>