Subject: [PATCH] Tables node (pgAdmin4)

From: Murtuza Zabuawala <[email-masked]>
Date: Mon, 18 Apr 2016 19:04:06 +0530

Hi,

Please find initial patch for tables node.

This patch includes below nodes,

1) Tables node
2) Columns node
3) Index node
4) Trigger node
5) Constraints node (Primary key & Unique constraints only)    *-- From:
Harshal*
6) Roles node
               *-- From: Surinder*

This patch also includes "VacuumSettings control" required by table node.

Please apply Fieldset Control UI patch sent earlier.

*Please note that constraint node is still partial, It has Primary Key &
Unique constraint working & integrated in tables node.*

1)  I have used initial patch of index constraints node from Harshal &
further extend it it to work with table node.
[ Harshal will integrate rest of constraints in tables node, he is working
on it.]

2) I have also used initial patches of rules node and VacuumSettings
control from Surinder & further extend them it to work with table node.

--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

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

---

From: Harshal Dhumal <[email-masked]>
Date: Wed, 27 Apr 2016 14:52:09 +0530

Hi,

PFA attached patches for table node and all table child nodes.

1) Table node                              *-- Initial patch by Murtuza,
constraints compatibility by Harshal. *
2) Column node                           *-- by Murtuza. *
3) Index node                              *-- by Murtuza. *
4) Trigger node                            *-- by Murtuzz. *
6) Rules node
*-- by Surinder.*
7) Constraints nodes:
      i]  Index Constraint                 *-- Initial patch by Harshal,
Integration with table node by **Murtuza.*
      ii] Foreign key                        *-- Initial patch and
Integration with table node by Harshal**.*
      iii] Check constraint                *-- Initial patch and
Integration with table node by Harshal**.*
      iv] Exclusion constraint           *-- Initial patch and Integration
with table node by Harshal**.*

Please apply patches in following order as all of them depends on each
other.

*Order:  Table Node ----> Index constraint ---> remaining patches in any
order.*

-- 
*Harshal Dhumal*
*Software Engineer *

EenterpriseDB <http://www.enterprisedb.com>

---

From: Harshal Dhumal <[email-masked]>
Date: Wed, 27 Apr 2016 15:22:10 +0530

Hi,

The exclusion constraint has minor validation issue (this won't block
testing of patch). I'm closely working with Ashesh to fix this.

-- 
*Harshal Dhumal*
*Software Engineer *

On Wed, Apr 27, 2016 at 2:52 PM, Harshal Dhumal <
[email-masked]> wrote:

>       iv] Exclusion constraint           *-- Initial patch and
>> [ Harshal will integrate rest of constraints in tables node, he is
>> working on it.]
>>

---

From: Thom Brown <[email-masked]>
Date: Wed, 27 Apr 2016 13:43:46 +0100

On 27 April 2016 at 10:22, Harshal Dhumal
<[email-masked]> wrote:
> 1) Table node                              -- Initial patch by Murtuza, constraints compatibility by Harshal.
> 2) Column node                           -- by Murtuza.
> 3) Index node                              -- by Murtuza.
> 4) Trigger node                            -- by Murtuzz.
> 6) Rules node                              -- by Surinder.
> 7) Constraints nodes:
>       i]  Index Constraint                 -- Initial patch by Harshal, Integration with table node by Murtuza.
>       ii] Foreign key                        -- Initial patch and Integration with table node by Harshal.
>       iii] Check constraint                -- Initial patch and Integration with table node by Harshal.
>       iv] Exclusion constraint           -- Initial patch and Integration with table node by Harshal.
>
> Please apply patches in following order as all of them depends on each other.
>
> Order:  Table Node ----> Index constraint ---> remaining patches in any order.
>
>

Nice work.  Here's some initial feedback from a very quick play around.

On the Create table editor, in the Advance tab (which should probably
be labelled "Advanced"), the Like section should grey out the "With *"
values if no relation is selected in the drop-down box.

The way primary keys are defined are kinda awkward.  It might be
useful to provide some kind of checkbox on the initial column list
that tells it which columns are involved in the primary key, then the
user could just select which ones they want.  If they want to refine
it, they could edit it in the Constraints > Primary Key section.

I'm getting weird spacing in the SQL output.  Here's an example:

CREATE UNLOGGED TABLE public.test
(
    id integer COLLATE pg_catalog."de_DE.utf8" NOT NULL DEFAULT -1,
    stuff text COLLATE pg_catalog."C.UTF-8" DEFAULT "hello",
    CONSTRAINT pk PRIMARY KEY (id, stuff) WITH (FILLFACTOR=33) DEFERRABLE
)
WITH (
    OIDS = TRUE,
    FILLFACTOR = 88,
    autovacuum_enabled = TRUE,
    autovacuum_analyze_scale_factor = 0.33,
    autovacuum_analyze_threshold = 30,
    autovacuum_freeze_max_age = 3333333,
    autovacuum_vacuum_cost_delay = 30,
    autovacuum_vacuum_cost_limit = 3,
    autovacuum_vacuum_scale_factor = 0.33,
    autovacuum_vacuum_threshold = 33,
    autovacuum_freeze_min_age = 3300000,
    autovacuum_freeze_table_age = 333000000
)
TABLESPACE pg_default;

ALTER TABLE public.test
    OWNER to thom;
GRANT ALL ON TABLE public.test TO thom;

COMMENT ON TABLE public.test
    IS 'This is just a test table';

COMMENT ON COLUMN public.test.id
    IS 'the main ID';

ALTER TABLE public.test
    ALTER COLUMN id
    SET (n_distinct='0.2');
COMMENT ON CONSTRAINT pk ON public.test
    IS 'primary key test'

Note there are 2 blank lines after the GRANT ALL ON TABLE line, and
none before the COMMENT ON CONSTRAINT line.

This SQL fails because collations aren't allowed on integer columns,
and the DEFAULT value for the column named stuff doesn't quote it as a
string literal, so it's looking for a column called "hello".

There's also no way to view the autovacuum options I defined other
than the SQL pane.

Thom

---

From: Thom Brown <[email-masked]>
Date: Wed, 27 Apr 2016 14:34:57 +0100

> There's also no way to view the autovacuum options I defined other

A couple more:

If I go to create a new table, give it a name, select a table to
inherit from, and then do nothing else, I get an error:

ERROR: syntax error at or near "INHERITS"
LINE 2: INHERITS (test)
^

This is because no column list was provided, and in this case, an
empty one would be needed.  So instead of:

CREATE TABLE public.test2
(
)
    INHERITS (test)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

It should be:

CREATE TABLE public.test2
    INHERITS (test)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

If I go to create another new table, give it a name, and then in the
Like section on the Advance tab, I select a table, and enable each of
the With * options, the following SQL is produced:

CREATE TABLE public.test2
    LIKE public.test
        INCLUDING DEFAULTS
        INCLUDING CONSTRAINTS
        INCLUDING INDEXES
        INCLUDING STORAGE
        INCLUDING COMMENTSWITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

The LIKE statement should appear in the column list section, so should
be surrounded in parentheses.  Also, the WITH statement here is not on
a new line.

So this should be:

CREATE TABLE public.test2
(
    LIKE public.test
        INCLUDING DEFAULTS
        INCLUDING CONSTRAINTS
        INCLUDING INDEXES
        INCLUDING STORAGE
        INCLUDING COMMENTS
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

Regards

Thom

---

From: Murtuza Zabuawala <[email-masked]>
Date: Thu, 28 Apr 2016 10:41:07 +0530

Hi Thom,

Thank you for your inputs, Will work on it & send updated patch again.

Regards,
Murtuza

> On 27-Apr-2016, at 7:04 pm, Thom Brown <[email-masked]> wrote:
>

---

From: Thom Brown <[email-masked]>
Date: Thu, 28 Apr 2016 09:44:00 +0100

> CREATE TABLE public.test2

Correction, these should be the other way around.

Thom

---

From: Dave Page <[email-masked]>
Date: Thu, 28 Apr 2016 09:52:59 +0100

> On 27 Apr 2016, at 13:43, Thom Brown <[email-masked]> wrote:
> 

If the design we did has been properly followed (I can't check right now), then that's exactly how it should be working. Harshal?

Arun Kollan and I spent a lot of time redesigning the table dialogue to make it as quick and efficient to use as possible. There will likely be minor deviations from that design to ensure consistency with the way other parts of the app have turned out, but the basic principles should be there.

---

From: Khushboo Vashi <[email-masked]>
Date: Thu, 28 Apr 2016 15:36:11 +0530

Hi,

Please find the review comments so far:

1. On the Table Collection node, The fields in the grid should be Name,
Owner and Comments. OID is not required. Please follow same for the other
Nodes like Index, Constraints etc.
2. While Updating the Table, Add any column as well as Inherits any table,
then the check the SQL tab.
ALTER TABLE SQL should be come in the new line

*Current SQL Tab:*

ALTER TABLE pem.agent_heartbeat
    INHERIT pem.alert_history;ALTER TABLE pem.agent_heartbeat
    ADD COLUMN test bigint;

3. While Creating/updating table, if the Schema is other than selected one,
then after saving the table, it is not falling under the same schema. And
also in update mode it gives an error.
4. Unlogged setting does not honor the change of value.
5. Please Check SQL tab for all the Nodes as most of them having problem of
No blank lines/More than one Blank Lines/Blank Lines at the end etc.
6. Creating Table with  auto_vacuum and updating only one field then wrong
SQL is generated.
WITH (
    OIDS = TRUE,
    FILLFACTOR = 12,
    autovacuum_enabled = TRUE,
,
    autovacuum_vacuum_cost_delay = 21
)

7. Same as toast
WITH (
    OIDS = TRUE,
    FILLFACTOR = 12,
    autovacuum_enabled = TRUE,
    toast.autovacuum_enabled = TRUE,
    autovacuum_analyze_scale_factor = 1,
    autovacuum_analyze_threshold = 2,
    autovacuum_freeze_max_age = 2,
,
    toast.autovacuum_vacuum_cost_limit = 2,
    toast.autovacuum_freeze_min_age = 4
)

8. Sometimes while creating table and checking sql table, below error is
coming

  File
"/home/khushboo/Projects/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py",
line 1060, in properties
    data = res['rows'][0]
IndexError: list index out of range

9. Please check all the Grid table columns. It should not be expanded while
editing directly into the grid. For ref: Check constraint grid

10. Constraint Nodes are not covered yet due to validation issue on which
Harshal is working.

11. While creating the table if auto-vacuume has been enabled by user, then
it should stay enabled in Edit mode also. Currently it is not.

12 .If I just enable 'custom auto activated' and don't update anything then
SQL tab is generating below SQL which is wrong.

ALTER TABLE pem.khushboo1 SET (

);

13. IF I Change privileges from pem_agent to agent1 then the SQL i like
below, which is not corrent

REVOKE ALL ON TABLE pem.khushboo1 FROM agent1;
GRANT SELECT ON TABLE pem.khushboo1 TO agent1;

14. In check constraint, change "Don't Validate" to Validated? Please refer
Domain Constraint for the same.

15. SQL for the Column is coming as below, which is not correct.

<html><head></head><body>-- Column: col3 -- ALTER TABLE pem.khushboo1 DROP
COLUMN col3; ALTER TABLE pem.khushboo1 ADD COLUMN col3 integer NOT
NULL;</body></html>

16. While updating table columns from column node. Below SQL generating an
error.

ALTER TABLE pem.khushboo1
    ALTER COLUMN col2 numeric(1, 1);

17. After deleting any column, the properties of the another column of the
same table doesn't show up. Gives below error.

TypeError: self.canDrop.apply is not a function

...lf.canDrop) ? function() { return self.canDrop.apply(self, arguments); }
: fals

18.  Table Node : Exclusion constraint : Grid validates DESC instead of
operator.

19. Please check validation of the Exclusion control, as some JS error is
coming and due to this, we can not close the dialogue.

    The select2('destroy') method was called on an element that is not
using Select2.

...this.$dropdown.on(d.join("
"),function(a){a.stopPropagation()})},a}),b.define("s...

select2....min.js (line 3)
TypeError: c is undefined

20. While updating the comments field of the Index node, it throws below
error:

TypeError: obj is null

} else if ((obj.sessChanged && obj.sessChanged()) || isNew) {
21. Job Trigger : Validation missing, so user can't get an idea what is
missing while checking the SQL tab
22. For the reverse Engineering SQL tab, the constraints should be start
with Schema.Table.Constraint. Please follow same path for all the nodes.
23. Indexes : Comments can not be updated. Please check the attached
screen-shot for reference.
24. Spelling mistake of 'Definition' in Indexes.

NOTE: I haven't check Constraints properly due to validation issue. Also I
have checked only functional flow, I will review the code today evening or
tomorrow.

Thanks,
Khushboo

---

From: Harshal Dhumal <[email-masked]>
Date: Thu, 28 Apr 2016 19:52:30 +0530

-- 
*Harshal Dhumal*
*Software Engineer *

On Thu, Apr 28, 2016 at 2:22 PM, Dave Page <[email-masked]> wrote:

> If the design we did has been properly followed (I can't check right now),
> then that's exactly how it should be working. Harshal?
>

I can't comment on entire table node ui design as I haven't worked on it
except the constraints nodes.

In constraints nodes I can see that order of some properties in tabs is not
same as that of design (this can be easily changed by reordering schema
properties of constraints node).

And also currently there is no support to show two or more controls inline
in dialog and where in design for e.g In Unique key constraint under
columns tab the "Order" and "Null's order" are shown inline (there are many
of like this). So all controls are rendered on separate row which is not as
per design.

> Arun Kollan and I spent a lot of time redesigning the table dialogue to
> make it as quick and efficient to use as possible. There will likely be
> minor deviations from that design to ensure consistency with the way other
> parts of the app have turned out, but the basic principles should be there.
>
> >

---

From: Harshal Dhumal <[email-masked]>
Date: Sat, 7 May 2016 19:33:49 +0530

Hi,

PFA updated patches for table and it's child nodes.

-- 
*Harshal Dhumal*
*Software Engineer *

On Thu, Apr 28, 2016 at 2:14 PM, Thom Brown <[email-masked]> wrote:

> >>> 1) Table node                              -- Initial patch by
> Murtuza, constraints compatibility by Harshal.
> >>>       i]  Index Constraint                 -- Initial patch by
> Harshal, Integration with table node by Murtuza.

---

From: Harshal Dhumal <[email-masked]>
Date: Sat, 7 May 2016 19:42:09 +0530

Hi,

-- 
*Harshal Dhumal*
*Software Engineer *

On Wed, Apr 27, 2016 at 6:13 PM, Thom Brown <[email-masked]> wrote:

> Fixed

> it, they could edit it in the Constraints > Primary Key section.

I have added functionality. However check box will only be enabled in
expanded mode of column (by clicking on edit button which at left side of
delete button). To enable check box in grid mode this
<http://www.postgresql.org/message-id/CAFOhELcoJTEm=V=[email-masked]>
patch needs to be committed.

> There's also no way to view the autovacuum options I defined other
Fixed the spacing.

---

From: Harshal Dhumal <[email-masked]>
Date: Sat, 7 May 2016 19:43:53 +0530

Hi,

i have fixed all of below mentioned issues.

---

From: Harshal Dhumal <[email-masked]>
Date: Sat, 7 May 2016 19:45:34 +0530

Hi,

Please find below responses.

Fixed

Fixed

> 3. While Creating/updating table, if the Schema is other than selected
> one, then after saving the table, it is not falling under the same schema.
> And also in update mode it gives an error.
>
TODO

> 4. Unlogged setting does not honor the change of value.
Not reproducible.

> 5. Please Check SQL tab for all the Nodes as most of them having problem
> of No blank lines/More than one Blank Lines/Blank Lines at the end etc.
>
Fixed

> Fixed.

Fixed

> IndexError: list index out of range
TODO. (Need exact steps to reproduce.)

>
> 9. Please check all the Grid table columns. It should not be expanded
> while editing directly into the grid. For ref: Check constraint grid
>
TODO

> 10. Constraint Nodes are not covered yet due to validation issue on which
Done.

>
> 11. While creating the table if auto-vacuume has been enabled by user,
> then it should stay enabled in Edit mode also. Currently it is not.
>
Fixed

> 12 .If I just enable 'custom auto activated' and don't update anything
> then SQL tab is generating below SQL which is wrong.
>
Fixed

> GRANT SELECT ON TABLE pem.khushboo1 TO agent1;

Not reproducible Or please provide steps to reproduce.

> 14. In check constraint, change "Don't Validate" to Validated? Please
> refer Domain Constraint for the same.
>
Fixed.

> COLUMN col3; ALTER TABLE pem.khushboo1 ADD COLUMN col3 integer NOT

Not reproducible Or please provide steps to reproduce.

Fixed

> ...lf.canDrop) ? function() { return self.canDrop.apply(self, arguments);
> } : fals
>

This issue is already raised.

> 18.  Table Node : Exclusion constraint : Grid validates DESC instead of

Not reproducible.

> TypeError: c is undefined

This is already fixed.

> } else if ((obj.sessChanged && obj.sessChanged()) || isNew) {

This is already fixed.

> missing while checking the SQL tab

Fixed.

> with Schema.Table.Constraint. Please follow same path for all the nodes.

I cross checked with pgadmin3. Reverse Engineering SQL looks good to me.
Please let me know what is missing?

Fixed

Fixed

>>> 5) Constraints node (Primary key & Unique constraints only)    *--
>>> From: Harshal*
>>> 6) Roles node
>>>                    *-- From: Surinder*
>>>

---

From: Murtuza Zabuawala <[email-masked]>
Date: Mon, 9 May 2016 17:51:48 +0530

Hi Harshal,

Please find comments as below for constraints node,

1) Not able to create Primary key due to 'Please provide primary key'
validation error
2) Primary key dialog do not close after save.
3) Error "too much recursion" when creating Forgien key from New table.
4) Error "too much recursion" when creating Check constraint from New table.
5) Remove console.log from JS (Unique constraint)
6) Unique & Exclude constraint are also not working in create mode, No SQL
is generated in create mode
7) If there are no columns on table select2 shows columns of previously
fetched objects columns.

Also attaching new updated patch, which will fixes below issues,
Fixed:
=====
1) Do not show Foreign tables under tables node
2) In trigger node changed select2 control options as per new format.
3) Removed unwanted templates from trigger node
4) clean up some unwanted code from trigger node
5) Fixed Create sql template in index node

Regards,
Murtuza

On Sat, May 7, 2016 at 7:45 PM, Harshal Dhumal <
[email-masked]> wrote:

>> 2. While Updating the Table, Add any column as well as Inherits any
>> table, then the check the SQL tab.
>> 6. Creating Table with  auto_vacuum and updating only one field then
>> wrong SQL is generated.
>> WITH (
>>     OIDS = TRUE,
>>     FILLFACTOR = 12,
>> <html><head></head><body>-- Column: col3 -- ALTER TABLE pem.khushboo1
>> DROP COLUMN col3; ALTER TABLE pem.khushboo1 ADD COLUMN col3 integer NOT
>> NULL;</body></html>
>>
>
>> 16. While updating table columns from column node. Below SQL generating
>> an error.
>>
>> 17. After deleting any column, the properties of the another column of
>> the same table doesn't show up. Gives below error.
>>
>> NOTE: I haven't check Constraints properly due to validation issue. Also
>> I have checked only functional flow, I will review the code today evening
>> or tomorrow.
>>
>>
>> Thanks,
>> Khushboo
>>
>>
>>
>>> 1) Table node                              *-- Initial patch by
>>> Murtuza, constraints compatibility by Harshal. *
>>>       i]  Index Constraint                 *-- Initial patch by
>>> Harshal, Integration with table node by **Murtuza.*
>>>> *Please note that constraint node is still partial, It has Primary Key
>>>> & Unique constraint working & integrated in tables node.*
>>>>

---

From: Murtuza Zabuawala <[email-masked]>
Date: Tue, 10 May 2016 18:37:42 +0530

Hi Harshal,

Pending issues to be fixed which I tried but not able to fix in Constraints
node,
*1)* Adding Primary key in create table mode causes "too much recursion"
error & Column collection validation error.
*2)* MultiSelect2 rendering issue causing window to hang.

PFA updated patch for table node,
- Added help file names in js.
- Added Deps for primary key cell in create table node
- Corrected validation error messages
- Formatted SQL templates properly
- Added support for View in triggers node

Regards,
Murtuza

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

>>> 10. Constraint Nodes are not covered yet due to validation issue on
>>> which Harshal is working.
>>>
>> Done.
>>
>>
>>>
>>> ...lf.canDrop) ? function() { return self.canDrop.apply(self,
>>> arguments); } : fals
>>>
>>
>>> 19. Please check validation of the Exclusion control, as some JS error
>>> is coming and due to this, we can not close the dialogue.
>>>
>>>> *Order:  Table Node ----> Index constraint ---> remaining patches in
>>>> any order.*
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> --
>>>> *Harshal Dhumal*
>>>> *Software Engineer *
>>>>
>>>>
>>>>

---

From: Harshal Dhumal <[email-masked]>
Date: Thu, 12 May 2016 18:02:35 +0530

Hi,

Apply order Table node -> Index constrain -> remaining in any order

-- 
*Harshal Dhumal*
*Software Engineer *

On Tue, May 10, 2016 at 6:37 PM, Murtuza Zabuawala <
[email-masked]> wrote:

> Hi Harshal,
>
> Pending issues to be fixed which I tried but not able to fix in
> Constraints node,
>> 6) Unique & Exclude constraint are also not working in create mode, No
>> SQL is generated in create mode
>>>> 5. Please Check SQL tab for all the Nodes as most of them having
>>>> problem of No blank lines/More than one Blank Lines/Blank Lines at the end
>>>> etc.
>>>>
>>> Fixed
>>>
>>>
>>>> 8. Sometimes while creating table and checking sql table, below error
>>>> is coming
>>>>
>>>>   File
>>>> 18.  Table Node : Exclusion constraint : Grid validates DESC instead
>>>> of  operator.
>>>>
>>>
>>> Not reproducible.
>>>
>>>
>>>>
>>>> 20. While updating the comments field of the Index node, it throws
>>>> below error:
>>>>
>>>> TypeError: obj is null
>>>>
>>>> 22. For the reverse Engineering SQL tab, the constraints should be
>>>> start with Schema.Table.Constraint. Please follow same path for all the
>>>> nodes.
>>>>
>>>
>>>> NOTE: I haven't check Constraints properly due to validation issue.
>>>> Also I have checked only functional flow, I will review the code today
>>>> evening or tomorrow.
>>>>
>>>>
>>>> Thanks,
>>>> Khushboo
>>>>
>>>>
>>>>
>>>>>> *Please note that constraint node is still partial, It has Primary
>>>>>> Key & Unique constraint working & integrated in tables node.*
>>>>>>
>>>>>> 1)  I have used initial patch of index constraints node from Harshal
>>>>>> & further extend it it to work with table node.

---

From: Harshal Dhumal <[email-masked]>
Date: Thu, 12 May 2016 18:03:11 +0530

Hi,

-- 
*Harshal Dhumal*
*Software Engineer *

Fixed.

Fixed.

---

From: Khushboo Vashi <[email-masked]>
Date: Thu, 12 May 2016 18:55:02 +0530

Hi,

I have started reviewing the patch but the basic functionalities are
breaking on python 2.7.
Please test the patch on Python 2.7, fix the issues and resend the patch.

Thanks,
Khushboo

On Thu, May 12, 2016 at 6:03 PM, Harshal Dhumal <
[email-masked]> wrote:

>>>>> 1. On the Table Collection node, The fields in the grid should be
>>>>> Name, Owner and Comments. OID is not required. Please follow same for the
>>>>> other Nodes like Index, Constraints etc.
>>>>>
>>>>
>>>> Fixed
>>>>
>>>>> 13. IF I Change privileges from pem_agent to agent1 then the SQL i
>>>>> like below, which is not corrent
>>>>>
>>>>> 16. While updating table columns from column node. Below SQL
>>>>> generating an error.
>>>>>
>>>>> 21. Job Trigger : Validation missing, so user can't get an idea what
>>>>> is missing while checking the SQL tab
>>>>>
>>>>
>>>> Fixed.
>>>>
>>>>
>>>> I cross checked with pgadmin3. Reverse Engineering SQL looks good to
>>>> me. Please let me know what is missing?
>>>>
>>>>

---

From: Harshal Dhumal <[email-masked]>
Date: Fri, 13 May 2016 17:24:10 +0530

Hi

PFA attached patches for table and it's child nodes with python 2.7
compatibility.

-- 
*Harshal Dhumal*
*Software Engineer *

On Thu, May 12, 2016 at 6:55 PM, Khushboo Vashi <
[email-masked]> wrote:

>>>>>> 3. While Creating/updating table, if the Schema is other than
>>>>>> selected one, then after saving the table, it is not falling under the same
>>>>>> schema. And also in update mode it gives an error.
>>>>>>
>>>>> TODO
>>>>>
>>>>>
>>>>>> 11. While creating the table if auto-vacuume has been enabled by
>>>>>> user, then it should stay enabled in Edit mode also. Currently it is not.
>>>>>>
>>>>> Fixed
>>>>>
>>>>>
>>>>>
>>>>>> 12 .If I just enable 'custom auto activated' and don't update
>>>>>> anything then SQL tab is generating below SQL which is wrong.
>>>>>>
>>>>>> 17. After deleting any column, the properties of the another column
>>>>>> of the same table doesn't show up. Gives below error.
>>>>>>
>>>>>> 19. Please check validation of the Exclusion control, as some JS
>>>>>> error is coming and due to this, we can not close the dialogue.
>>>>>>
>>>>>>     The select2('destroy') method was called on an element that is
>>>>>> not using Select2.
>>>>>>
>>>>>>
>>>>>>>> 1)  I have used initial patch of index constraints node from
>>>>>>>> Harshal & further extend it it to work with table node.
>>>>>>>> 2) I have also used initial patches of rules node and
>>>>>>>> VacuumSettings control from Surinder & further extend them it to work with
>>>>>>>> table node.
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> Regards,
>>>>>>>> Murtuza Zabuawala

---

From: Khushboo Vashi <[email-masked]>
Date: Fri, 13 May 2016 18:55:33 +0530

Hi,

Review Comments:

- Please replace 'can not' with 'cannot'  in all the validation messages.
- PG 9.1+ Inheritance issue as below:

CREATE TABLE public.table1
(
)
(
)
    INHERITS (a)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public.table1
    OWNER to postgres;

brackets are coming twice.

- Please maintain one line spacing between SQL queries In the SQL Tab.
- Foreign Key Grid in Table css issue: Grid columns expands on the
selection of the cell
- Check Constraint: Validated? option should be True by default
- pg 9.4: Exclude constraint does not render in SQL tab
- Missing Security validation
- Vacuum grid should not be editable in properties mode.
- Edit mode, Fill Factor can be allowed to be null.
- While dropping inheritance, related table columns drop SQL are also
populated in the SQL Tab

ALTER TABLE public."Tbl"
    NO INHERIT b;
ALTER TABLE public."Tbl" DROP COLUMN id;
ALTER TABLE public."Tbl" DROP COLUMN name;

And also render error while clicking on the save button.

ERROR: syntax error at or near "["
LINE 2: INHERIT [;
^

- in a Reverse Engineering SQL tab, schema_name.tablename should be there,
currently only table_name displays.
- Column SQL is showing below text with HTML

<html><head></head><body>-- Column: id -- ALTER TABLE public.a DROP COLUMN
id; ALTER TABLE public.a ADD COLUMN id integer;</body></html>

- The column datatype dependency does not get cleared upon selection of
another datatype.
 For example, if I select numeric and gives the length and precision. After
that I change the dat-type then, dependent fields should be get cleared.

- The column datatype does not get displayed in the properties and edit
mode if the length and precision are given while creating a column.

- Statistics is showing null value even after having value.

- if the check constraints are not validated then put proper icon in tree
and also it should be validated in edit mode.

NOTE: I have not checked the Indexes, Triggers and Rules nodes as I do not
have much knowledge about it.

Thanks,
Khushboo

On Fri, May 13, 2016 at 5:24 PM, Harshal Dhumal <
[email-masked]> wrote:

>>> *1)* Adding Primary key in create table mode causes "too much
>>>> recursion" error & Column collection validation error.
>>>>
>>> Fixed.
>>>
>>>
>>>>> 7) If there are no columns on table select2 shows columns of
>>>>> previously fetched objects columns.
>>>>>
>>>>>
>>>>>
>>>>>>> 8. Sometimes while creating table and checking sql table, below
>>>>>>> error is coming
>>>>>>>
>>>>>>>   File
>>>>>>> 9. Please check all the Grid table columns. It should not be
>>>>>>> expanded while editing directly into the grid. For ref: Check constraint
>>>>>>> grid
>>>>>>>
>>>>>> TODO
>>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> 14. In check constraint, change "Don't Validate" to Validated?
>>>>>>> Please refer Domain Constraint for the same.
>>>>>>>
>>>>>> Fixed.
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> <html><head></head><body>-- Column: col3 -- ALTER TABLE
>>>>>>> pem.khushboo1 DROP COLUMN col3; ALTER TABLE pem.khushboo1 ADD COLUMN col3
>>>>>>> integer NOT NULL;</body></html>
>>>>>>>
>>>>>>
>>>>>>>> *Order:  Table Node ----> Index constraint ---> remaining patches
>>>>>>>> in any order.*
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> *Harshal Dhumal*
>>>>>>>> *Software Engineer *
>>>>>>>>
>>>>>>>>
>>>>>>>>

---

From: Harshal Dhumal <[email-masked]>
Date: Sat, 14 May 2016 02:03:42 +0530

Hi,

PFA updated patches (version: 6) for table and it's child nodes.

-- 
*Harshal Dhumal*
*Software Engineer *

Fixed

TODO

Fixed

> - Check Constraint: Validated? option should be True by default
Not sure about this. I cross checked in pgadmin3.

Fixed

Fixed

> - Vacuum grid should not be editable in properties mode.
TODO (It's editable but one cannot save it on server from here as there is
no save button.)

> - Edit mode, Fill Factor can be allowed to be null.
TODO (This is generic issue in Integer and Numeric controls. This issue is
covered in this partial patch
<http://www.postgresql.org/message-id/[email-masked]>
)

Fixed

Fixed

> <html><head></head><body>-- Column: id -- ALTER TABLE public.a DROIP
> COLUMN id; ALTER TABLE public.a ADD COLUMN id integer;</body></html>
>
>
I was not able to reproduce exact issue but still I have fixed other issue
which I found related to column SQL. Hopefully that will fix this issue as
well.

Fixed.

> mode if the length and precision are given while creating a column.

TODO ( I cannot fix this blindly as this might introduce another issue(s)
in column node. I will need Murtuza's help as he has worked on column node)

Fixed

Not reproducible.

---

From: Harshal Dhumal <[email-masked]>
Date: Mon, 16 May 2016 19:02:58 +0530

Hi,

PFA add-on patch for table and it's child node. (please apply this patch on
version 6 patch)

Murtuza and I fixed following issues:

1. SQL formatting
2. Vacuum grid should not be editable in properties mode.
3. Column datatype does not get displayed in the properties and edit mode.
4. Do not allow to add another primary key if one already exist.

And another minor enhancements.

-- 
*Harshal Dhumal*
*Software Engineer *

On Sat, May 14, 2016 at 2:03 AM, Harshal Dhumal <
[email-masked]> wrote:

>> - in a Reverse Engineering SQL tab, schema_name.tablename should be
>> there, currently only table_name displays.
>>
> Fixed
>
>
>
>  For example, if I select numeric and gives the length and precision.
>> After that I change the dat-type then, dependent fields should be get
>> cleared.
>>
> Fixed.
>
>
>>
>> NOTE: I have not checked the Indexes, Triggers and Rules nodes as I do
>> not have much knowledge about it.
>>
>>
>
>
>> Thanks,
>> Khushboo
>>
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
index a51d5eb..c561159 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
@@ -656,10 +656,31 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
                 # Manual Data type formatting
                 # If data type has () with them then we need to remove them
                 # eg bit(1) because we need to match the name with combobox
+                isArray = False
+                if column['cltype'].endswith('[]'):
+                    isArray = True
+                    column['cltype'] = column['cltype'].rstrip('[]')
+
                 idx = column['cltype'].find('(')
                 if idx and column['cltype'].endswith(')'):
                     column['cltype'] = column['cltype'][:idx]
 
+                if isArray:
+                    column['cltype'] += "[]"
+
+                if 'indkey' in column:
+                    # Current column
+                    attnum = str(column['attnum'])
+
+                    # Single/List of primary key column(s)
+                    indkey = str(column['indkey'])
+
+                    # We will check if column is in primary column(s)
+                    if attnum in indkey.split(" "):
+                        column['is_primary_key'] = True
+                    else:
+                        column['is_primary_key'] = False
+
         return data
 
     def _index_constraints_formatter(self, tid, data):
@@ -679,6 +700,8 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
         }
 
         for ctype in index_constraints.keys():
+            data[index_constraints[ctype]] = []
+
             sql = render_template("/".join([self.index_constraint_template_path,
                                             'properties.sql']),
                                   tid=tid,
@@ -1058,6 +1081,23 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
         if not status:
             return internal_server_error(errormsg=res)
         data = res['rows'][0]
+
+        data['vacuum_settings_str'] = ""
+
+        if data['table_vacuum_settings_str'] is not None:
+            data['vacuum_settings_str'] += data[
+                'table_vacuum_settings_str'].replace(',', '\n')
+
+        if data['toast_table_vacuum_settings_str'] is not None:
+            data['vacuum_settings_str'] += '\n' + '\n'.join(
+                    ['toast_' + setting for setting in data[
+                        'toast_table_vacuum_settings_str'
+                    ].split(',')]
+                )
+        data['vacuum_settings_str'] = data[
+            'vacuum_settings_str'
+        ].replace("=", " = ")
+
         data = self._formatter(scid, tid, data)
 
         return ajax_response(
@@ -1615,7 +1655,7 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
 
         try:
             SQL = self.get_sql(scid, tid, data)
-            re.sub('\n{2,}', '\n', SQL)
+            SQL = re.sub('\n{2,}', '\n', SQL)
             SQL = SQL.strip('\n')
             return make_json_response(
                 data=SQL,
@@ -2391,6 +2431,7 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
                                     data=data, conn=self.conn)
 
         # Add into main sql
+        table_sql = re.sub('\n{2,}', '\n\n', table_sql)
         main_sql.append(table_sql.strip('\n'))
 
         """
@@ -2473,6 +2514,7 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
                                          data=data, conn=self.conn)
 
             # Add into main sql
+            index_sql = re.sub('\n{2,}', '\n\n', index_sql)
             main_sql.append(sql_header + '\n\n' + index_sql.strip('\n'))
 
         """
@@ -2525,30 +2567,31 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
 
                 data['columns'] = columns
 
-                data = trigger_definition(data)
+            data = trigger_definition(data)
 
-                sql_header = "\n-- Trigger: {0}\n\n-- ".format(data['name'])
-                sql_header += render_template("/".join([self.trigger_template_path,
-                                                        'delete.sql']),
-                                              data=data, conn=self.conn)
+            sql_header = "\n-- Trigger: {0}\n\n-- ".format(data['name'])
+            sql_header += render_template("/".join([self.trigger_template_path,
+                                                    'delete.sql']),
+                                          data=data, conn=self.conn)
 
-                # If the request for new object which do not have did
-                trigger_sql = render_template("/".join([self.trigger_template_path,
-                                                        'create.sql']),
-                                              data=data, conn=self.conn)
+            # If the request for new object which do not have did
+            trigger_sql = render_template("/".join([self.trigger_template_path,
+                                                    'create.sql']),
+                                          data=data, conn=self.conn)
 
-                trigger_sql = sql_header + '\n\n' + trigger_sql.strip('\n')
+            trigger_sql = sql_header + '\n\n' + trigger_sql.strip('\n')
 
-                # If trigger is disabled then add sql code for the same
-                if not data['is_enable_trigger']:
-                    trigger_sql += '\n\n'
-                    trigger_sql += render_template("/".join([
-                        self.trigger_template_path,
-                        'enable_disable_trigger.sql']),
-                        data=data, conn=self.conn)
+            # If trigger is disabled then add sql code for the same
+            if not data['is_enable_trigger']:
+                trigger_sql += '\n\n'
+                trigger_sql += render_template("/".join([
+                    self.trigger_template_path,
+                    'enable_disable_trigger.sql']),
+                    data=data, conn=self.conn)
 
             # Add into main sql
-            main_sql.append(trigger_sql.strip('\n'))
+            trigger_sql = re.sub('\n{2,}', '\n\n', trigger_sql)
+            main_sql.append(trigger_sql)
 
         """
         #####################################
@@ -2579,6 +2622,7 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
                 data=res_data, display_comments=True)
 
             # Add into main sql
+            rules_sql = re.sub('\n{2,}', '\n\n', rules_sql)
             main_sql.append(rules_sql)
 
         sql = '\n'.join(main_sql)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py
index 1f85b90..49d2c7b 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py
@@ -316,7 +316,7 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
             indkey = str(data['indkey'])
 
             # We will check if column is in primary column(s)
-            if attnum in indkey:
+            if attnum in indkey.split(" "):
                 data['is_pk'] = True
             else:
                 data['is_pk'] = False
@@ -328,13 +328,20 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
         )
 
         import re
+        # If we have length & precision both
         matchObj = re.search(r'(\d+),(\d+)', fulltype)
         if matchObj:
             data['attlen'] = matchObj.group(1)
             data['attprecision'] = matchObj.group(2)
         else:
-            data['attlen'] = None
-            data['attprecision'] = None
+            # If we have length only
+            matchObj = re.search(r'(\d+)', fulltype)
+            if matchObj:
+                data['attlen'] = matchObj.group(1)
+                data['attprecision'] = None
+            else:
+                data['attlen'] = None
+                data['attprecision'] = None
 
         # We need to fetch inherited tables for each table
         SQL = render_template("/".join([self.template_path,
@@ -410,12 +417,20 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
 
         data['edit_types'] = edit_types_list
 
-        # Custom Data type formatting
-        # If data type is bit then db returns bit(1),
-        # So we need to convert those types manually
-        if data['cltype'] == 'bit(1)':
-            data['cltype'] = 'bit'
+        # Manual Data type formatting
+        # If data type has () with them then we need to remove them
+        # eg bit(1) because we need to match the name with combobox
+        isArray = False
+        if data['cltype'].endswith('[]'):
+            isArray = True
+            data['cltype'] = data['cltype'].rstrip('[]')
+
+        idx = data['cltype'].find('(')
+        if idx and data['cltype'].endswith(')'):
+            data['cltype'] = data['cltype'][:idx]
 
+        if isArray:
+            data['cltype'] += "[]"
 
         return data
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/js/column.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/js/column.js
index 23e2d25..15716c8 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/js/column.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/js/column.js
@@ -137,7 +137,6 @@ function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
         }
         ]);
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
       model: pgAdmin.Browser.Node.Model.extend({
         defaults: {
           name: undefined,
@@ -173,6 +172,13 @@ function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
             return _.isUndefined(m.top.node_info['table']);
           },
           disabled: function(m){
+            // If primary key already exist then disable.
+            if (m.top && !_.isUndefined(m.top.get('oid')) &&
+                        m.top.get('primary_key').length > 0 &&
+                        !_.isUndefined(m.top.get('primary_key').first().get('oid'))) {
+              return true;
+            }
+
             var name = m.get('name');
 
             if(!m.inSchemaWithColumnCheck.apply(this, [m]) &&
@@ -187,6 +193,14 @@ function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
             if(m instanceof Backbone.Collection) {
               return true;
             }
+            // If primary key already exist then disable.
+            if (m.top && !_.isUndefined(m.top.get('oid')) &&
+                      m.top.get('primary_key').length > 0 &&
+                      !_.isUndefined(m.top.get('primary_key').first().get('oid'))) {
+
+              return false;
+            }
+
             if(!m.inSchemaWithColumnCheck.apply(this, [m]) &&
               !_.isUndefined(name) && !_.isNull(name) && name !== '') {
               return true;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/alter.sql
index 93f323e..2098ddc 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/alter.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/alter.sql
@@ -1,9 +1,11 @@
 {## Alter index to use cluster type ##}
 {% if data.indisclustered %}
+
 ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
     CLUSTER ON {{conn|qtIdent(data.name)}};
 {% endif %}
 {## Changes description ##}
 {% if data.description %}
+
 COMMENT ON INDEX {{conn|qtIdent(data.name)}}
     IS {{data.description|qtLiteral}};{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js
index 562913b..0258ca7 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js
@@ -265,7 +265,8 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
           coll_inherits: [],
           hastoasttable: true,
           toast_autovacuum_enabled: false,
-          autovacuum_enabled: false
+          autovacuum_enabled: false,
+          primary_key: []
        },
         // Default values!
         initialize: function(attrs, args) {
@@ -366,7 +367,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
             })
         },{
           id: 'fillfactor', label:'{{ _('Fill factor') }}', cell: 'integer',
-          type: 'int', mode: ['properties', 'create', 'edit'], min: 10, max: 100,
+          type: 'int', mode: ['create', 'edit'], min: 10, max: 100,
           disabled: 'inSchema',group: '{{ _('Advanced') }}'
         },{
           id: 'relhasoids', label:'{{ _('Has OIDs?') }}', cell: 'switch',
@@ -687,7 +688,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
         },{
           // Here we will create tab control for auto-vacuum
           type: 'nested', control: 'tab', group: '{{ _('Auto vacuum') }}',
-          mode: ['properties', 'edit', 'create'],
+          mode: ['edit', 'create'],
           schema: Backform.VacuumSettingsSchema
         },{
           id: 'relacl_str', label:'{{ _('Privileges') }}', cell: 'string',
@@ -706,7 +707,11 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
           group: '{{ _('Security') }}', mode: ['edit', 'create'],
           min_version: 90100, canAdd: true,
           canEdit: false, canDelete: true, control: 'unique-col-collection'
-        }],
+        },{
+          id: 'vacuum_settings_str', label: '{{ _('Storage Settings') }}',
+          type: 'multiline', group: '{{ _('Advanced') }}', mode: ['properties']
+        }
+        ],
         validate: function() {
           var err = {},
               changedAttrs = this.changed,
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql
index 9dcbd43..5075404 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql
@@ -88,7 +88,6 @@ TABLESPACE {{ conn|qtIdent(data.spcname) }};
 
 ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
     OWNER to {{conn|qtIdent(data.relowner)}};
-
 {% endif %}
 {### Security Labels on Table ###}
 {% if data.seclabels and data.seclabels|length > 0 %}
@@ -96,20 +95,18 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
 {% for r in data.seclabels %}
 {{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.security_label, data.schema) }}
 {% endfor %}
-
 {% endif %}
 {###  ACL on Table ###}
 {% if data.relacl %}
+
 {% for priv in data.relacl %}
 {{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
 {% endfor %}
 {% endif %}
 {### SQL for COMMENT ###}
 {% if data.description %}
-
 COMMENT ON TABLE {{conn|qtIdent(data.schema, data.name)}}
     IS {{data.description|qtLiteral}};
-
 {% endif %}
 {#===========================================#}
 {#====== MAIN TABLE TEMPLATE ENDS HERE ======#}
@@ -123,14 +120,12 @@ COMMENT ON TABLE {{conn|qtIdent(data.schema, data.name)}}
 
 COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.name, c.name)}}
     IS {{c.description|qtLiteral}};
-
 {% endif %}
 {###  Add variables to column ###}
 {% if c.attoptions and c.attoptions|length > 0 %}
 
 ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
     {{ VARIABLE.SET(conn, 'COLUMN', c.name, c.attoptions) }}
-
 {% endif %}
 {###  ACL ###}
 {% if c.attacl and c.attacl|length > 0 %}
@@ -138,7 +133,6 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
 {% for priv in c.attacl %}
     {{ COLUMN_PRIVILEGE.APPLY(conn, data.schema, data.name, c.name, priv.grantee, priv.without_grant, priv.with_grant) }}
 {% endfor %}
-
 {% endif %}
 {###  Security Lables ###}
 {% if c.seclabels and c.seclabels|length > 0 %}
@@ -146,7 +140,6 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
 {% for r in c.seclabels %}
 {{ COLUMN_SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.name, c.name, r.provider, r.security_label) }}
 {% endfor %}
-
 {% endif %}
 {% endfor %}
 {% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_relations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_relations.sql
index 3e4840e..431ee88 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_relations.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_relations.sql
@@ -2,8 +2,5 @@ SELECT c.oid, quote_ident(n.nspname)||'.'||quote_ident(c.relname) AS like_relati
 FROM pg_class c, pg_namespace n
 WHERE c.relnamespace=n.oid
 AND
--- before PG 9.2
--- c.relkind IN ('r')
--- PG 9.2
 c.relkind IN ('r', 'v', 'f')
 ORDER BY 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/properties.sql
index d819c98..99727c9 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/properties.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/properties.sql
@@ -22,7 +22,8 @@ SELECT rel.oid, rel.relname AS name, rel.reltablespace AS spcoid,rel.relacl AS r
 		WHERE i.inhrelid = rel.oid) AS inherited_tables_cnt,
 	(CASE WHEN rel.relpersistence = 'u' THEN true ELSE false END) AS relpersistence,
 	substring(array_to_string(rel.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor,
-	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS autovacuum_enabled,
+	(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') = 'true')
+	  THEN true ELSE false END) AS autovacuum_enabled,
 	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold,
 	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor,
 	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold,
@@ -32,7 +33,8 @@ SELECT rel.oid, rel.relname AS name, rel.reltablespace AS spcoid,rel.relacl AS r
 	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age,
 	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age,
 	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age,
-	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS toast_autovacuum_enabled,
+	(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') =  'true')
+	  THEN true ELSE false END) AS toast_autovacuum_enabled,
 	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold,
 	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor,
 	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold,
@@ -42,6 +44,8 @@ SELECT rel.oid, rel.relname AS name, rel.reltablespace AS spcoid,rel.relacl AS r
 	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age,
 	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age,
 	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age,
+	array_to_string(rel.reloptions, ',') AS table_vacuum_settings_str,
+	array_to_string(tst.reloptions, ',') AS toast_table_vacuum_settings_str,
 	rel.reloptions AS reloptions, tst.reloptions AS toast_reloptions, rel.reloftype, typ.typname,
 	(CASE WHEN rel.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable,
     -- Added for pgAdmin4
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql
index 904e451..270c6c9 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql
@@ -137,7 +137,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
 ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
 {% set flag = true %}
 {% endif %}
-    {{opt.name}} = {{opt.value}}{% endif %}
+    toast.{{opt.name}} = {{opt.value}}{% endif %}
 {% if loop.index == data.vacuum_toast.changed|length and (flag or (data.toast_autovacuum_enabled and o_data.toast_autovacuum_enabled == false) or (data.toast_autovacuum_enabled == false and o_data.toast_autovacuum_enabled))%}
 
 );
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/acl.sql
index e231ecc..56f1f76 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/acl.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/acl.sql
@@ -26,7 +26,7 @@ FROM
           LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
           LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
           LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
-        WHERE rel.relkind IN ('r','s','t','f') AND rel.relnamespace = {{ scid }}::oid
+        WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ scid }}::oid
             AND rel.oid = {{ tid }}::oid
     ) acl,
     (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
@@ -37,7 +37,7 @@ FROM
           LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
           LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
           LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
-        WHERE rel.relkind IN ('r','s','t','f') AND rel.relnamespace = {{ scid }}::oid
+        WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ scid }}::oid
             AND rel.oid = {{ tid }}::oid
         ) a) d
     ) d
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql
index 5b786ef..5075404 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql
@@ -98,13 +98,13 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
 {% endif %}
 {###  ACL on Table ###}
 {% if data.relacl %}
+
 {% for priv in data.relacl %}
 {{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
 {% endfor %}
 {% endif %}
 {### SQL for COMMENT ###}
 {% if data.description %}
-
 COMMENT ON TABLE {{conn|qtIdent(data.schema, data.name)}}
     IS {{data.description|qtLiteral}};
 {% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oid.sql
index c0d8d58..e9dc772 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oid.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oid.sql
@@ -1,5 +1,5 @@
 SELECT rel.oid as tid
 FROM pg_class rel
-WHERE rel.relkind IN ('r','s','t','f')
+WHERE rel.relkind IN ('r','s','t')
 AND rel.relnamespace = {{ scid }}::oid
 AND rel.relname = {{data.name|qtLiteral}}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/properties.sql
index 6e850bc..99727c9 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/properties.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/properties.sql
@@ -16,10 +16,10 @@ SELECT rel.oid, rel.relname AS name, rel.reltablespace AS spcoid,rel.relacl AS r
     JOIN pg_namespace n ON n.oid=c.relnamespace
     WHERE i.inhrelid = rel.oid ORDER BY inhseqno)) AS coll_inherits,
   (SELECT count(*)
-    FROM pg_inherits i
+		FROM pg_inherits i
       JOIN pg_class c ON c.oid = i.inhparent
       JOIN pg_namespace n ON n.oid=c.relnamespace
-    WHERE i.inhrelid = rel.oid) AS inherited_tables_cnt,
+		WHERE i.inhrelid = rel.oid) AS inherited_tables_cnt,
 	(CASE WHEN rel.relpersistence = 'u' THEN true ELSE false END) AS relpersistence,
 	substring(array_to_string(rel.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor,
 	(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') = 'true')
@@ -44,11 +44,13 @@ SELECT rel.oid, rel.relname AS name, rel.reltablespace AS spcoid,rel.relacl AS r
 	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age,
 	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age,
 	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age,
+	array_to_string(rel.reloptions, ',') AS table_vacuum_settings_str,
+	array_to_string(tst.reloptions, ',') AS toast_table_vacuum_settings_str,
 	rel.reloptions AS reloptions, tst.reloptions AS toast_reloptions, rel.reloftype, typ.typname,
 	(CASE WHEN rel.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable,
     -- Added for pgAdmin4
 	(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean  THEN true ELSE false END) AS autovacuum_custom,
-	(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') = 'true')  OR rel.reltoastrelid != 0 THEN true ELSE false END) AS toast_autovacuum,
+	(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean  AND rel.reltoastrelid != 0 THEN true ELSE false END) AS toast_autovacuum,
 
 	(SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=rel.oid AND sl1.objsubid=0) AS seclabels,
 	(CASE WHEN rel.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_table
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql
index 904e451..270c6c9 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql
@@ -137,7 +137,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
 ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
 {% set flag = true %}
 {% endif %}
-    {{opt.name}} = {{opt.value}}{% endif %}
+    toast.{{opt.name}} = {{opt.value}}{% endif %}
 {% if loop.index == data.vacuum_toast.changed|length and (flag or (data.toast_autovacuum_enabled and o_data.toast_autovacuum_enabled == false) or (data.toast_autovacuum_enabled == false and o_data.toast_autovacuum_enabled))%}
 
 );
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/templates/trigger/js/trigger.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/templates/trigger/js/trigger.js
index b7946d3..bd22795 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/templates/trigger/js/trigger.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/templates/trigger/js/trigger.js
@@ -344,7 +344,7 @@ function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
                 return m.inSchemaWithModelCheck.apply(this, [m]);
             }
         },{
-            id: 'evnt_turncate', label:'{{ _('TURNCATE') }}',
+            id: 'evnt_turncate', label:'{{ _('TRUNCATE') }}',
             type: 'switch', group: '{{ _('Events') }}',
             disabled: function(m) {
             var is_constraint_trigger = m.get('is_constraint_trigger'),
@@ -374,7 +374,7 @@ function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
             mode: ['create', 'edit', 'properties'],
             control: 'sql-field', visible: true, group: '{{ _('Definition') }}'
         },{
-            id: 'columns', label: '{{ _('Columns') }}',
+            id: 'columns', label: '{{ _('Columns') }}', url: 'nodes',
             type: 'collection', control: 'multi-select-ajax',
             deps: ['evnt_update'], node: 'column', group: '{{ _('Definition') }}',
             model: pgBrowser.Node.Model.extend({
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/macros/schemas/privilege.macros b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/macros/schemas/privilege.macros
index 183ec2f..dc18a31 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/macros/schemas/privilege.macros
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/macros/schemas/privilege.macros
@@ -6,6 +6,10 @@
 GRANT {{ privs|join(', ') }} ON {{ type }} {{ conn|qtIdent(schema, param) }} TO {{ conn|qtIdent(role) }};
 {% endif %}
 {% if with_grant_privs %}
+{% if privs %}
+{# This empty if is to add new line in between #}
+
+{% endif %}
 GRANT {{ with_grant_privs|join(', ') }} ON {{ type }} {{ conn|qtIdent(schema, param) }} TO {{ conn|qtIdent(role) }} WITH GRANT OPTION;
 {% endif %}
 {%- endmacro %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js
index d012dbc..587d5f2 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js
@@ -131,7 +131,7 @@ function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
     var VacuumSettingsSchema = Backform.VacuumSettingsSchema =
      [{
             id: 'autovacuum_custom', label: '{{ _("Custom auto-vacuum?") }}',
-              group: '{{ _("Table") }}', mode: ['properties', 'edit', 'create'],
+              group: '{{ _("Table") }}', mode: ['edit', 'create'],
               type: 'switch',
               disabled: function(m) {
                 if(!m.top.inSchema.apply(this, [m])) {
@@ -141,7 +141,7 @@ function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
               }
           },{
               id: 'autovacuum_enabled', label: '{{ _("Enabled?") }}',
-              group: '{{ _("Table") }}', mode: ['properties', 'edit', 'create'],
+              group: '{{ _("Table") }}', mode: ['edit', 'create'],
               type: 'switch',
               deps: ['autovacuum_custom'],
               disabled: function(m) {
@@ -160,7 +160,7 @@ function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
               id: 'vacuum_table', label: '{{ _("Vacuum Table") }}',
               model: Backform.VacuumTableModel, editable: false, type: 'collection',
               canEdit: true, group: '{{ _("Table") }}',
-              mode: ['properties', 'edit', 'create'], url: 'get_table_vacuum',
+              mode: ['edit', 'create'], url: 'get_table_vacuum',
               control: Backform.VacuumCollectionControl.extend({
                 grid_columns :[
                   {
@@ -183,7 +183,7 @@ function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
               deps: ['autovacuum_enabled']
           },{
               id: 'toast_autovacuum', label: '{{ _("Custom auto-vaccum?") }}',
-              group: '{{ _("Toast Table") }}', mode: ['properties', 'edit', 'create'],
+              group: '{{ _("Toast Table") }}', mode: ['edit', 'create'],
               type: 'switch',
               disabled: function(m) {
                 // We need to check additional condition to toggle enable/disable
@@ -191,15 +191,15 @@ function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
                 if(!m.top.inSchema.apply(this, [m]) && m.isNew()) {
                   return false;
                 } else if(!m.top.inSchema.apply(this, [m]) &&
-                    m.get('toast_autovacuum_enabled') === true ||
-                    m.top.get('hastoasttable') === true) {
+                    (m.get('toast_autovacuum_enabled') === true ||
+                    m.top.get('hastoasttable') === true)) {
                   return false;
                 }
                 return true;
               }
           },{
               id: 'toast_autovacuum_enabled', label: '{{ _("Enabled?") }}',
-              group: '{{ _("Toast Table") }}', mode: ['properties', 'edit', 'create'],
+              group: '{{ _("Toast Table") }}', mode: ['edit', 'create'],
               type: 'switch',
               deps:['toast_autovacuum'],
               disabled: function(m) {

---

From: Surinder Kumar <[email-masked]>
Date: Tue, 17 May 2016 11:48:13 +0530

Hi,

Please find attached add-on patch for table's subnode rule.
Apply this patch at end after applying previous patches in email thread.

*Issue fixed*: "Do Instead" on Rule node under View/M-View node not working
in properties mode. Unable to generate proper SQL.

On Mon, May 16, 2016 at 7:02 PM, Harshal Dhumal <
[email-masked]> wrote:

> Hi,
>
> PFA add-on patch for table and it's child node. (please apply this patch
> on version 6 patch)
>
>> TODO (It's editable but one cannot save it on server from here as there
>> is no save button.)
>>
>>
>> TODO (This is generic issue in Integer and Numeric controls. This issue
>> is covered in this partial patch
>> I was not able to reproduce exact issue but still I have fixed other
>> issue which I found related to column SQL. Hopefully that will fix this
>> issue as well.
>>
>>
>>> - if the check constraints are not validated then put proper icon in
>>> tree and also it should be validated in edit mode.
>>>
>> Not reproducible.
>>
>>
>>>

---

From: Murtuza Zabuawala <[email-masked]>
Date: Tue, 17 May 2016 14:52:13 +0530

Hi,

PFA add-on patch to Ver.6 patch,  Which will add CREATE/SELECT etc Script
support into table node.

Regards,
Murtuza

On Tue, May 17, 2016 at 11:48 AM, Surinder Kumar <
[email-masked]> wrote:

> *Issue fixed*: "Do Instead" on Rule node under View/M-View node not
> working in properties mode. Unable to generate proper SQL.
>
>>> TODO ( I cannot fix this blindly as this might introduce another
>>> issue(s) in column node. I will need Murtuza's help as he has worked on
>>> column node)
>>>
>>>
>>>>

---

From: Sanket Mehta <[email-masked]>
Date: Tue, 17 May 2016 22:12:47 +0530

Hi Harshal,

Below are my review comments:

 I got below warning when I tried to apply the patch for table node as
mentioned below:

Table creation:

   -  Trailing white spaces warnings

                    $ git apply
/projects/patches/pgadmin4/Table/table_14_May_V6.patch

/projects/patches/pgadmin4/Table/table_14_May_V6.patch:6008: trailing
whitespace.
                              return false;

 /projects/patches/pgadmin4/Table/table_14_May_V6.patch:6016: trailing
whitespace.
                              return false;
                   warning: 2 lines add whitespace errors.

   - In Table creation dialog, while adding a new primary key, it does not
   allow to change the tablespace to empty. (which is not the case in case of
   tablespace in table)
   - In Table creation dialog, while adding a new column, data type and
   name field must be mandatory. otherwise while clicking on save it gives
   below error

                  File
"/projects/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py",
line 1319, in _parse_format_columns
                   c['cltype'] = self._cltype_formatter(c['cltype'])
                   KeyError: 'cltype

   - In Table creation dialog, While adding a new column, in primary check
   box is needed to click twice in order to check it. Ideally it should be
   checked by only one click.
   - In Table creation dialog, While adding a new column, primary key
   should not be allowed to added unless user has provided name and data type
   for atleast one column.
   currently if user has clicked on add column button and immediately click
   on add primary key button, it will add a row in primary key data grid
   - When delete table/drop cascade is apply on any table, i got a
   javascript error as mentioned below

                 node.js:94 Uncaught TypeError: self.canDrop.apply is not a
function

   - Once the above error generated, every time user tries to open a
   context menu by right clicking on any existing table, that same error comes
   - In table creation dialog, if table is inherited from another table, if
   a new primary key is added manually there, the create sql will not have an
   entry for primary key
   - In table creation dialog, if primary key check box is checked while
   adding the row, a new row is added in primary key datagrid but unchecking
   the primary key checkbox from column datagrid, does not removes that row
   from primary key data grid.
   - In AutoVacuum tab, if user provides any invalid value to any
   parameter, then a error message should be prompted, only background color
   change would not tell user to change the value.
   - In table creation dialog, security label are not being added.
   javascript error is coming as mentioned below:
     {"success": 0, "info": "", "result": null, "data": null, "errormsg":
   "can't adapt type 'Undefined'"}
   - In Table creation dialog, while adding foreign key, in action tab. if
   user click on 'x' button in "on update" or "on delete" select2 control, it
   gives error "Uncaught SyntaxError: Unexpected end of input"
   - In Table creation dialog, while adding a check constraint, "validated"
   button does not work properly
   - After successfully creation of table, "table name cannot be empty"
   error is not getting cleared.
   - In Table creation dialog, if user has added an empty column without
   entering its name or type and trying to add check constraint, it will add
   an empty constraint
   - In Table creation dialog, while adding an exclude constraint, for
   "character varying" column type, no operators are being listed
   - In Table creation dialog, while adding an exclude constraint, below
   mentioned error comes if user removes operator class by clicking 'x' on
   that control Uncaught TypeError: Cannot read property 'id' of undefined
   - In Table creation dialog, SQL is not getting generated for exclude
   constraint
   - In Table creation dialog, schema should be prefixed with table name in
   "of type" control
   - In Table creation dialog, while adding privileges, it always shows
   default privileges even if user has selected different privileges. (This
   works fine once user edit the privileges in edit table mode and shows only
   those privileges which user selects). Ashesh, please confirm the behaviour.

Table edit mode:

   - If in edit mode, any constraint is already having any comment, then
   remove it. It will not create the SQL for the same.
   - Changing Schema will give server error

Column Creation:

   - Save button is enabled by default
   - Data type validation is not provided. Save button is enabled just
   after providing column name
   - Length field limitation is not provided. (i.e. for numeric type,
   length should be allowed greater than 1000)

Exclusion constraint creation:

   - Access method should not be allowed to be empty. (currently by
   clicking 'x' will remove the selection in it)

Index creation:

   - No error message for name field when empty
   - No error message when column name is not provided while adding a
   column in index
   - While adding a column if no name is provided, "None" appears in SQL
   tab which will give error on OK button click
   - when comment is provided while creation, it gives error saying index
   does not exists. because schema name is not added before it.

Rule creation:

   - Name is empty error does not come till user enters something in
   definition tab
   - DO INSTEAD button does not make any difference to SQL (it works in
   edit mode)

Rule edit mode:

   - Add comment in edit mode, check the SQL in sql tab. Now come back to
   general tab and removes comment and check the sql tab again.
   SQL for comment is still there with empty string as comment

Trigger Creation:

   - SQL is not proper when creating a trigger. "()" should be appended to
   function name in SQL.
   It gives error while creating a trigger
   - "+" sign is visible in browser tree in front of trigger.  either On
   expanding trigger, it should show the trigger function name or that "+"
   sign should not appear

Trigger edit node:

   - On removing comment, nothing happens. No sql is being created. Comment
   is still there in properties.

Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

---

From: Harshal Dhumal <[email-masked]>
Date: Wed, 18 May 2016 14:42:01 +0530

Hi,

PFA patch for table node (version 8.1). apply this patch on version 7
patch. Not all issue are fixed in this patch. Murtuza will be sending
version 8.2 witch will have resolution for remaining issues. Apply version
8.2 patch on version 8.1

-- 
*Harshal Dhumal*
*Software Engineer *

>                    warning: 2 lines add whitespace errors.

Fixed (These were introduced due to rules node.)

>
>    - In Table creation dialog, while adding a new primary key, it does
>    not allow to change the tablespace to empty. (which is not the case in case
>    of tablespace in table)
>
> Fixed

Fixed

>
>    - In Table creation dialog, While adding a new column, in primary
>    check box is needed to click twice in order to check it. Ideally it should
>    be checked by only one click.
>
> This is Backgrid behaviour.

>    for at least one column.

>
>    - currently if user has clicked on add column button and immediately
>    click on add primary key button, it will add a row in primary key data grid
>
> Fixed

>                  node.js:94 Uncaught TypeError: self.canDrop.apply is not
> a function
>
Already fixed in other commit.

> Already fixed in other commit.

>
>    - In table creation dialog, if table is inherited from another table,
>    if a new primary key is added manually there, the create sql will not have
>    an entry for primary key
>
> Fixed

> Expected  behaviour.

>
>    - In Table creation dialog, while adding foreign key, in action tab.
>    if user click on 'x' button in "on update" or "on delete" select2 control,
>    it gives error "Uncaught SyntaxError: Unexpected end of input"
>
> Fixed

>
>    - In Table creation dialog, while adding a check constraint,
>    "validated" button does not work properly
>
> Fixed.

>  Expected  behaviour (Columns are not mandatory for check constrains)

>  There are no operators for character varying data type.

>
>    - In Table creation dialog, schema should be prefixed with table name
>    in "of type" control
>
>  Fixed

> Privileges are set correctly. Only it shows in incorrect order.

Fixed

> This is generic issue for each node.

> Access method is optional.

>    - SQL is not proper when creating a trigger. "()" should be appended
>    to function name in SQL.

>    - On removing comment, nothing happens. No sql is being created.
>    Comment is still there in properties.
>
>
>
> Regards,
> Sanket Mehta
> Sr Software engineer
> Enterprisedb
>
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
index c561159..9fcafe8 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
@@ -581,7 +581,7 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
                 seclabels = []
                 for seclbls in column['seclabels']:
                     k, v = seclbls.split('=')
-                    seclabels.append({'provider': k, 'security_label': v})
+                    seclabels.append({'provider': k, 'label': v})
 
                 column['seclabels'] = seclabels
 
@@ -945,7 +945,7 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
             seclabels = []
             for seclbls in data['seclabels']:
                 k, v = seclbls.split('=')
-                seclabels.append({'provider': k, 'security_label': v})
+                seclabels.append({'provider': k, 'label': v})
 
             data['seclabels'] = seclabels
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/templates/check_constraint/js/check_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/templates/check_constraint/js/check_constraint.js
index 6dbaac6..afc5ba9 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/templates/check_constraint/js/check_constraint.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/templates/check_constraint/js/check_constraint.js
@@ -136,7 +136,7 @@ function($, _, S, pgAdmin, pgBrowser, Alertify) {
               !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
           }
         },{
-          id: 'convalidated', label: "{{ _("Validated?") }}", type: 'switch', cell:
+          id: 'convalidated', label: "{{ _("Don't validate") }}", type: 'switch', cell:
           'boolean', group: '{{ _('Definition') }}', min_version: 90200,
           disabled: function(m) {
             if ((_.isFunction(m.isNew) && !m.isNew()) ||
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/js/exclusion_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/js/exclusion_constraint.js
index d6b6acf..26335df 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/js/exclusion_constraint.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/js/exclusion_constraint.js
@@ -156,6 +156,8 @@ function($, _, S, pgAdmin, pgBrowser, Alertify) {
             m = self.model,
             col_type = self.model.get('col_type');
 
+            self.column.set('options', []);
+
             if (url && !_.isUndefined(col_type) && !_.isNull(col_type) && col_type != '') {
               var node = this.column.get('schema_node'),
                   eventHandler = m.top || m,
@@ -194,25 +196,11 @@ function($, _, S, pgAdmin, pgBrowser, Alertify) {
               } else {
                 self.column.set('options', data);
               }
-            } else {
-              self.column.set('options', []);
             }
           }
         })
       }
-    ],
-    validate: function() {
-      var err = {},
-          errmsg = null,
-          data = this.toJSON();
-
-      if (_.isUndefined(data.operator) ||
-        _.isNull(data.operator) ||
-        String(data.operator).replace(/^\s+|\s+$/g, '') == '') {
-        return '{{ _('Please specify value for operator.')}}';
-      }
-      return null;
-    }
+    ]
   });
 
   var ExclusionConstraintColumnControl =  Backform.ExclusionConstraintColumnControl =
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/templates/foreign_key/js/foreign_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/templates/foreign_key/js/foreign_key.js
index a47f3ad..06d6b53 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/templates/foreign_key/js/foreign_key.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/templates/foreign_key/js/foreign_key.js
@@ -879,7 +879,7 @@ function($, _, S, pgAdmin, pgBrowser, Alertify) {
         },{
           id: 'confupdtype', label:'{{ _('On update') }}',
           type:"select2", group: '{{ _('Action') }}', mode: ['edit','create'],
-          select2:{width:"50%"},
+          select2:{width:"50%", allowClear: false},
           options: [
             {label: "NO ACTION", value: "a"},
             {label: "RESTRICT", value: "r"},
@@ -899,7 +899,7 @@ function($, _, S, pgAdmin, pgBrowser, Alertify) {
         },{
           id: 'confdeltype', label:'{{ _('On delete') }}',
           type:"select2", group: '{{ _('Action') }}', mode: ['edit','create'],
-          select2:{width:"50%"},
+          select2:{width:"50%", allowClear: false},
           options: [
             {label: "NO ACTION", value: "a"},
             {label: "RESTRICT", value: "r"},
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/templates/index_constraint/js/index_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/templates/index_constraint/js/index_constraint.js
index d6317ba..c5afe3a 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/templates/index_constraint/js/index_constraint.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/templates/index_constraint/js/index_constraint.js
@@ -149,9 +149,13 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
                 return Backgrid.StringCell.prototype.render.apply(this, arguments);
               },
               remove: function() {
-                var tableCols = this.model.top.get('columns');
+                var tableCols = this.model.top.get('columns'),
+                primary_key_col = this.model.get('columns');
+
+                if (primary_key_col) {
+                  primary_key_col.off('pgadmin:multicolumn:updated');
+                }
 
-                this.model.get('columns').off('pgadmin:multicolumn:updated');
                 {% if node_type == 'primary_key' %}
                 this.stopListening(tableCols, 'remove' , self.resetColOptions);
                 this.stopListening(tableCols, 'change:name' , self.resetColOptions);
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/templates/rules/js/rules.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/templates/rules/js/rules.js
index 715447f..29022a6 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/templates/rules/js/rules.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/templates/rules/js/rules.js
@@ -46,7 +46,7 @@ function($, _, S, pgAdmin, pgBrowser, CodeMirror) {
       canDrop: function(itemData, item, data){
         pgBrowser.Nodes['schema'].canChildDrop.apply(this, [itemData, item, data]);
         if(itemData.label === '_RETURN')
-          return false; 
+          return false;
         else {
           return true;
         }
@@ -54,7 +54,7 @@ function($, _, S, pgAdmin, pgBrowser, CodeMirror) {
       canDropCascade: function(itemData, item, data){
         pgBrowser.Nodes['schema'].canChildDrop.apply(this, [itemData, item, data]);
         if(itemData.label === '_RETURN')
-          return false; 
+          return false;
         else {
           return true;
         }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/update.sql
index 608cbf9..57d81c7 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/update.sql
@@ -1,4 +1,4 @@
-{% if data.comment and data.comment != o_data.comment %}
+{% if data.comment is defined and data.comment != o_data.comment %}
 COMMENT ON CONSTRAINT {{ conn|qtIdent(o_data.name) }} ON {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
     IS {{ data.comment|qtLiteral }};
 {% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/update.sql
index 000047d..6c27923 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/update.sql
@@ -6,7 +6,7 @@ ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
 
 ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
     VALIDATE CONSTRAINT {{ conn|qtIdent(data.name) }};{% endif -%}
-{% if data.comment and data.comment != o_data.comment %}
+{% if data.comment is defined and data.comment != o_data.comment %}
 
 COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
     IS {{ data.comment|qtLiteral }};{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/create.sql
index 2b0d78f..51eea52 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/create.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/create.sql
@@ -21,7 +21,7 @@ COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
 {###  Add variables to column ###}
 {% if data.attoptions %}
 ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
-{{ VARIABLE.SET(conn, 'COLUMN', data.name, data.attoptions) }}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, data.attoptions) }}
 
 {% endif %}
 {###  ACL ###}
@@ -33,6 +33,6 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
 {###  Security Lables ###}
 {% if data.seclabels %}
 {% for r in data.seclabels %}
-{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.security_label) }}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
 {% endfor %}
 {% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/update.sql
index 4c902c5..a36c6fc 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/update.sql
@@ -52,15 +52,15 @@ COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
 {% set variables = data.attoptions %}
 {% if 'deleted' in variables and variables.deleted|length > 0 %}
 ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
-{{ VARIABLE.UNSET(conn, 'COLUMN', data.name, variables.deleted) }}
+    {{ VARIABLE.UNSET(conn, 'COLUMN', data.name, variables.deleted) }}
 {% endif %}
 {% if 'added' in variables and variables.added|length > 0 %}
 ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
-{{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.added) }}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.added) }}
 {% endif %}
 {% if 'changed' in variables and variables.changed|length > 0 %}
 ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
-{{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.changed) }}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.changed) }}
 {% endif %}
 
 {% endif %}
@@ -95,12 +95,12 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
 {% endif %}
 {% if 'added' in seclabels and seclabels.added|length > 0 %}
 {% for r in seclabels.added %}
-{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.security_label) }}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
 {% endfor %}
 {% endif %}
 {% if 'changed' in seclabels and seclabels.changed|length > 0 %}
 {% for r in seclabels.changed %}
-{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.security_label) }}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
 {% endfor %}
 {% endif %}
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/create.sql
index 2b0d78f..51eea52 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/create.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/create.sql
@@ -21,7 +21,7 @@ COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
 {###  Add variables to column ###}
 {% if data.attoptions %}
 ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
-{{ VARIABLE.SET(conn, 'COLUMN', data.name, data.attoptions) }}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, data.attoptions) }}
 
 {% endif %}
 {###  ACL ###}
@@ -33,6 +33,6 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
 {###  Security Lables ###}
 {% if data.seclabels %}
 {% for r in data.seclabels %}
-{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.security_label) }}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
 {% endfor %}
 {% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/update.sql
index 83f21d4..bcfb90c 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/update.sql
@@ -52,15 +52,15 @@ COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
 {% set variables = data.attoptions %}
 {% if 'deleted' in variables and variables.deleted|length > 0 %}
 ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
-{{ VARIABLE.UNSET(conn, 'COLUMN', data.name, variables.deleted) }}
+    {{ VARIABLE.UNSET(conn, 'COLUMN', data.name, variables.deleted) }}
 {% endif %}
 {% if 'added' in variables and variables.added|length > 0 %}
 ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
-{{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.added) }}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.added) }}
 {% endif %}
 {% if 'changed' in variables and variables.changed|length > 0 %}
 ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
-{{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.changed) }}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.changed) }}
 {% endif %}
 {% endif %}
 {### Update column privileges ###}
@@ -94,12 +94,12 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
 {% endif %}
 {% if 'added' in seclabels and seclabels.added|length > 0 %}
 {% for r in seclabels.added %}
-{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.security_label) }}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
 {% endfor %}
 {% endif %}
 {% if 'changed' in seclabels and seclabels.changed|length > 0 %}
 {% for r in seclabels.changed %}
-{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.security_label) }}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
 {% endfor %}
 {% endif %}
 {% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/update.sql
index d062e8b..4d70f21 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/update.sql
@@ -15,7 +15,7 @@ ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
     SET (FILLFACTOR={{ data.fillfactor }});
 {% endif %}
 {# ==== To update exclusion constraint comments ==== #}
-{% if data.comment and data.comment != o_data.comment %}
+{% if data.comment is defined and data.comment != o_data.comment %}
 COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
     IS {{ data.comment|qtLiteral }};
 {% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/update.sql
index d062e8b..4d70f21 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/update.sql
@@ -15,7 +15,7 @@ ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
     SET (FILLFACTOR={{ data.fillfactor }});
 {% endif %}
 {# ==== To update exclusion constraint comments ==== #}
-{% if data.comment and data.comment != o_data.comment %}
+{% if data.comment is defined and data.comment != o_data.comment %}
 COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
     IS {{ data.comment|qtLiteral }};
 {% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/update.sql
index a72f7e5..ee8f8a9 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/update.sql
@@ -11,7 +11,7 @@ ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
     VALIDATE CONSTRAINT {{ conn|qtIdent(data.name) }};
 {% endif %}
 {# ==== To update foreign key comments ==== #}
-{% if data.comment and data.comment != o_data.comment %}
+{% if data.comment is defined and data.comment != o_data.comment %}
 COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
     IS {{ data.comment|qtLiteral }};
 {% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/update.sql
index af76d8e..6618532 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/update.sql
@@ -15,7 +15,7 @@ ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
     SET (FILLFACTOR={{ data.fillfactor }});
 {% endif %}
 {# ==== To update constraint comments ==== #}
-{% if data.comment and data.comment != o_data.comment %}
+{% if data.comment is defined and data.comment != o_data.comment %}
 COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
     IS {{ data.comment|qtLiteral }};
 {% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js
index 0258ca7..c1d8c4f 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js
@@ -308,7 +308,8 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
         },{
           id: 'spcname', label:'{{ _('Tablespace') }}', cell: 'string', control: 'node-list-by-name',
           type: 'text', mode: ['properties', 'create', 'edit'], node: 'tablespace',
-          disabled: 'inSchema', filter: function(d) {
+          disabled: 'inSchema', select2:{allowClear:false},
+          filter: function(d) {
             // If tablespace name is not "pg_global" then we need to exclude them
             if(d && d.label.match(/pg_global/))
             {
@@ -558,6 +559,20 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
                           removedCols = primary_key_column_coll.where({column:column_name});
                         if (removedCols.length > 0) {
                           primary_key_column_coll.remove(removedCols);
+                          _.each(removedCols, function(m) {
+                            m.destroy();
+                          })
+                          if (primary_key_column_coll.length == 0) {
+                            setTimeout(function () {
+                              // There will be only on primary key so remove the first one.
+                              primary_key_coll.remove(primary_key_coll.first());
+                              /* Ideally above line of code should be "primary_key_coll.reset()".
+                               * But our custom DataCollection (extended from Backbone collection in datamodel.js)
+                               * does not respond to reset event, it only supports add, remove, change events.
+                               * And hence no custom event listeners/validators get called for reset event.
+                               */
+                            }, 10);
+                          }
                         }
                         primary_key_column_coll.trigger('pgadmin:multicolumn:updated', primary_key_column_coll);
                       }
@@ -593,10 +608,12 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
                   columns : ['name', 'columns'],
                   canAdd: true,
                   canAddRow: function(m) {
-                   // Use can only add one primary key
+                   // User can only add one primary key
+                   var columns = m.get('columns');
+
                    return (m.get('primary_key') &&
                             m.get('primary_key').length < 1 &&
-                            m.get('columns').length > 0);
+                            _.some(columns.pluck('name')));
                   }
                 },{
                   id: 'foreign_key', label: '{{ _('Foreign Key') }}',
@@ -609,8 +626,9 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
                   canAdd: true,
                   columns : ['name', 'columns'],
                   canAddRow: function(m) {
-                   // Use can only add if there are columns present
-                   return m.get('columns').length > 0;
+                   // User can only add if there is at least one column with name.
+                   var columns = m.get('columns');
+                   return _.some(columns.pluck('name'));
                   }
                 },{
                   id: 'check_constraint', label: '{{ _('Check Constraint') }}',
@@ -621,11 +639,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
                   canEdit: true, canDelete: true,
                   control: 'unique-col-collection',
                   canAdd: true,
-                  columns : ['name', 'consrc'],
-                  canAddRow: function(m) {
-                   // Use can only add if there are columns present
-                   return m.get('columns').length > 0;
-                  }
+                  columns : ['name', 'consrc']
                 },{
                   id: 'unique_constraint', label: '{{ _('Unique Constraint') }}',
                   model: pgBrowser.Nodes['unique_constraint'].model,
@@ -637,8 +651,9 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
                   columns : ['name', 'columns'],
                   canAdd: true,
                   canAddRow: function(m) {
-                   // Use can only add if there are columns present
-                   return m.get('columns').length > 0;
+                   // User can only add if there is at least one column with name.
+                   var columns = m.get('columns');
+                   return _.some(columns.pluck('name'));
                   }
                 },{
                   id: 'exclude_constraint', label: '{{ _('Exclude Constraint') }}',
@@ -651,8 +666,9 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
                   columns : ['name', 'columns', 'constraint'],
                   canAdd: true,
                   canAddRow: function(m) {
-                   // Use can only add if there are columns present
-                   return m.get('columns').length > 0;
+                   // User can only add if there is at least one column with name.
+                   var columns = m.get('columns');
+                   return _.some(columns.pluck('name'));
                   }
             }]
           }]
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql
index 5075404..f45daa4 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql
@@ -17,7 +17,7 @@
 {% endif %}
 CREATE {% if data.relpersistence %}UNLOGGED {% endif %}TABLE {{conn|qtIdent(data.schema, data.name)}}{{empty_bracket}}
 {% if data.typname %}
-    OF {{ conn|qtIdent(data.typname) }}
+    OF {{ data.typname }}
 {% endif %}
 {% if data.like_relation or data.coll_inherits or data.columns|length > 0 %}
 (
@@ -45,16 +45,17 @@ CREATE {% if data.relpersistence %}UNLOGGED {% endif %}TABLE {{conn|qtIdent(data
 {% endif %}
     {{conn|qtIdent(c.name)}} {{c.cltype}}{% if c.attlen %}
 ({{c.attlen}}{% if c.attprecision%}, {{c.attprecision}}{% endif %}){% endif %}{% if c.hasSqrBracket %}
-[]{% endif %}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.attnotnull %} NOT NULL{% endif %}{% if c.defval %} DEFAULT {{c.defval|qtLiteral}}{% endif %}
+[]{% endif %}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.attnotnull %} NOT NULL{% endif %}{% if c.defval %} DEFAULT {{c.defval}}{% endif %}
 {% endif %}
 {% endfor %}
-{# Macro to render for constraints #}
-{% if data.primary_key|length > 0 %}
-{{CONSTRAINTS.PRIMARY_KEY(conn, data.primary_key[0])}}{% endif %}{% if data.unique_constraint|length > 0 %}
-{{CONSTRAINTS.UNIQUE(conn, data.unique_constraint)}}{% endif %}{% if data.foreign_key|length > 0 %}
-{{CONSTRAINTS.FOREIGN_KEY(conn, data.foreign_key)}}{% endif %}{% if data.check_constraint|length > 0 %}
-{{CONSTRAINTS.CHECK(conn, data.check_constraint)}}{% endif %}
 {% endif %}
+{# Macro to render for constraints #}
+{% if data.primary_key|length > 0 %}{% if data.columns|length > 0 %},{% endif %}
+{{CONSTRAINTS.PRIMARY_KEY(conn, data.primary_key[0])}}{% endif %}{% if data.unique_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 %},{% endif %}
+{{CONSTRAINTS.UNIQUE(conn, data.unique_constraint)}}{% endif %}{% if data.foreign_key|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 %},{% endif %}
+{{CONSTRAINTS.FOREIGN_KEY(conn, data.foreign_key)}}{% endif %}{% if data.check_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 %},{% endif %}
+{{CONSTRAINTS.CHECK(conn, data.check_constraint)}}{% endif %}{% if data.exclude_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 %},{% endif %}
+{{CONSTRAINTS.EXCLUDE(conn, data.exclude_constraint)}}{% endif %}
 {% if data.like_relation or data.coll_inherits or data.columns|length > 0 %}
 
 )
@@ -93,7 +94,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
 {% if data.seclabels and data.seclabels|length > 0 %}
 
 {% for r in data.seclabels %}
-{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.security_label, data.schema) }}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
 {% endfor %}
 {% endif %}
 {###  ACL on Table ###}
@@ -138,7 +139,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
 {% if c.seclabels and c.seclabels|length > 0 %}
 
 {% for r in c.seclabels %}
-{{ COLUMN_SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.name, c.name, r.provider, r.security_label) }}
+{{ COLUMN_SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.name, c.name, r.provider, r.label) }}
 {% endfor %}
 {% endif %}
 {% endfor %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_oftype.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_oftype.sql
index bca7f77..aed42f2 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_oftype.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_oftype.sql
@@ -1,4 +1,5 @@
-SELECT t.oid, t.typname
+SELECT t.oid,
+  quote_ident(n.nspname)||'.'||quote_ident(t.typname) AS typname
   FROM pg_type t, pg_namespace n
 WHERE t.typtype='c' AND t.typnamespace=n.oid
   AND NOT (n.nspname like 'pg_%' OR n.nspname='information_schema')
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql
index 270c6c9..a97ac30 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql
@@ -189,12 +189,12 @@ COMMENT ON TABLE {{conn|qtIdent(data.schema, data.name)}}
 {% endif %}
 {% if 'added' in seclabels and seclabels.added|length > 0 %}
 {% for r in seclabels.added %}
-{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.security_label, data.schema) }}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
 {% endfor %}
 {% endif %}
 {% if 'changed' in seclabels and seclabels.changed|length > 0 %}
 {% for r in seclabels.changed %}
-{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.security_label, data.schema) }}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
 {% endfor %}
 {% endif %}
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql
index 5075404..f45daa4 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql
@@ -17,7 +17,7 @@
 {% endif %}
 CREATE {% if data.relpersistence %}UNLOGGED {% endif %}TABLE {{conn|qtIdent(data.schema, data.name)}}{{empty_bracket}}
 {% if data.typname %}
-    OF {{ conn|qtIdent(data.typname) }}
+    OF {{ data.typname }}
 {% endif %}
 {% if data.like_relation or data.coll_inherits or data.columns|length > 0 %}
 (
@@ -45,16 +45,17 @@ CREATE {% if data.relpersistence %}UNLOGGED {% endif %}TABLE {{conn|qtIdent(data
 {% endif %}
     {{conn|qtIdent(c.name)}} {{c.cltype}}{% if c.attlen %}
 ({{c.attlen}}{% if c.attprecision%}, {{c.attprecision}}{% endif %}){% endif %}{% if c.hasSqrBracket %}
-[]{% endif %}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.attnotnull %} NOT NULL{% endif %}{% if c.defval %} DEFAULT {{c.defval|qtLiteral}}{% endif %}
+[]{% endif %}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.attnotnull %} NOT NULL{% endif %}{% if c.defval %} DEFAULT {{c.defval}}{% endif %}
 {% endif %}
 {% endfor %}
-{# Macro to render for constraints #}
-{% if data.primary_key|length > 0 %}
-{{CONSTRAINTS.PRIMARY_KEY(conn, data.primary_key[0])}}{% endif %}{% if data.unique_constraint|length > 0 %}
-{{CONSTRAINTS.UNIQUE(conn, data.unique_constraint)}}{% endif %}{% if data.foreign_key|length > 0 %}
-{{CONSTRAINTS.FOREIGN_KEY(conn, data.foreign_key)}}{% endif %}{% if data.check_constraint|length > 0 %}
-{{CONSTRAINTS.CHECK(conn, data.check_constraint)}}{% endif %}
 {% endif %}
+{# Macro to render for constraints #}
+{% if data.primary_key|length > 0 %}{% if data.columns|length > 0 %},{% endif %}
+{{CONSTRAINTS.PRIMARY_KEY(conn, data.primary_key[0])}}{% endif %}{% if data.unique_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 %},{% endif %}
+{{CONSTRAINTS.UNIQUE(conn, data.unique_constraint)}}{% endif %}{% if data.foreign_key|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 %},{% endif %}
+{{CONSTRAINTS.FOREIGN_KEY(conn, data.foreign_key)}}{% endif %}{% if data.check_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 %},{% endif %}
+{{CONSTRAINTS.CHECK(conn, data.check_constraint)}}{% endif %}{% if data.exclude_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 %},{% endif %}
+{{CONSTRAINTS.EXCLUDE(conn, data.exclude_constraint)}}{% endif %}
 {% if data.like_relation or data.coll_inherits or data.columns|length > 0 %}
 
 )
@@ -93,7 +94,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
 {% if data.seclabels and data.seclabels|length > 0 %}
 
 {% for r in data.seclabels %}
-{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.security_label, data.schema) }}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
 {% endfor %}
 {% endif %}
 {###  ACL on Table ###}
@@ -138,7 +139,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
 {% if c.seclabels and c.seclabels|length > 0 %}
 
 {% for r in c.seclabels %}
-{{ COLUMN_SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.name, c.name, r.provider, r.security_label) }}
+{{ COLUMN_SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.name, c.name, r.provider, r.label) }}
 {% endfor %}
 {% endif %}
 {% endfor %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oftype.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oftype.sql
index bca7f77..aed42f2 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oftype.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oftype.sql
@@ -1,4 +1,5 @@
-SELECT t.oid, t.typname
+SELECT t.oid,
+  quote_ident(n.nspname)||'.'||quote_ident(t.typname) AS typname
   FROM pg_type t, pg_namespace n
 WHERE t.typtype='c' AND t.typnamespace=n.oid
   AND NOT (n.nspname like 'pg_%' OR n.nspname='information_schema')
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql
index 270c6c9..a97ac30 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql
@@ -189,12 +189,12 @@ COMMENT ON TABLE {{conn|qtIdent(data.schema, data.name)}}
 {% endif %}
 {% if 'added' in seclabels and seclabels.added|length > 0 %}
 {% for r in seclabels.added %}
-{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.security_label, data.schema) }}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
 {% endfor %}
 {% endif %}
 {% if 'changed' in seclabels and seclabels.changed|length > 0 %}
 {% for r in seclabels.changed %}
-{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.security_label, data.schema) }}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
 {% endfor %}
 {% endif %}
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/macros/constraints.macro b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/macros/constraints.macro
index 2ac429c..fe7389a 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/macros/constraints.macro
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/macros/constraints.macro
@@ -4,7 +4,8 @@
 {#    CREATE MODE ONLY    #}
 {##########################}
 {% macro PRIMARY_KEY(conn, data) -%}
-{% if data.columns|length > 0 %},
+{% if data.columns|length > 0 %}
+
     {% if data.name %}CONSTRAINT {{conn|qtIdent(data.name)}} {% endif %}PRIMARY KEY ({% for c in data.columns%}
 {% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(c.column)}}{% endfor %}){% if data.fillfactor %}
 
@@ -19,7 +20,8 @@
 {%- endmacro %}
 {% macro UNIQUE(conn, unique_data) -%}
 {% for data in unique_data %}
-{% if data.columns|length > 0 %},
+{% if data.columns|length > 0 %}{% if loop.index !=1 %},{% endif %}
+
     {% if data.name %}CONSTRAINT {{conn|qtIdent(data.name)}} {% endif %}UNIQUE ({% for c in data.columns%}
 {% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(c.column)}}{% endfor %}){% if data.fillfactor %}
 
@@ -34,14 +36,15 @@
 {% endfor %}
 {%- endmacro %}
 {% macro CHECK(conn, check_data) -%}
-{% for data in check_data %}
-,
+{% for data in check_data %}{% if loop.index !=1 %},{% endif %}
+
     {% if data.name %}CONSTRAINT {{ conn|qtIdent(data.name) }} {% endif%}CHECK ({{ data.consrc }}){% if data.convalidated %}
  NOT VALID{% endif %}{% if data.connoinherit %} NO INHERIT{% endif %}
 {% endfor %}
 {%- endmacro %}
 {% macro FOREIGN_KEY(conn, foreign_key_data) -%}
-{% for data in foreign_key_data %},
+{% for data in foreign_key_data %}{% if loop.index != 1 %},{% endif %}
+
     {% if data.name %}CONSTRAINT {{conn|qtIdent(data.name)}} {% endif %}FOREIGN KEY ({% for columnobj in data.columns %}{% if loop.index != 1 %}
 , {% endif %}{{ conn|qtIdent(columnobj.local_column)}}{% endfor %})
         REFERENCES {{ conn|qtIdent(data.remote_schema, data.remote_table) }} ({% for columnobj in data.columns %}{% if loop.index != 1 %}
@@ -71,7 +74,8 @@
 {% endfor %}
 {%- endmacro %}
 {% macro EXCLUDE(conn, exclude_data) -%}
-{% for data in exclude_data %},
+{% for data in exclude_data %}{% if loop.index != 1 %},{% endif %}
+
     {% if data.name %}CONSTRAINT {{ conn|qtIdent(data.name) }} {% endif%}EXCLUDE {% if data.amname and data.amname != '' %}USING {{data.amname}}{% endif %} ({% for col in data.columns %}{% if loop.index != 1 %},
         {% endif %}{{ conn|qtIdent(col.column)}} {% if col.oper_class and col.oper_class != '' %}{{col.oper_class}} {% endif%}{% if col.order %}ASC{% else %}DESC{% endif %} NULLS {% if col.nulls_order %}FIRST{% else %}LAST{% endif %} WITH {{col.operator}}{% endfor %}){% if data.fillfactor %}

---

From: Murtuza Zabuawala <[email-masked]>
Date: Wed, 18 May 2016 15:20:02 +0530

Hi,

PFA table add-on patch version 8.2

On Wed, May 18, 2016 at 2:42 PM, Harshal Dhumal <
[email-masked]> wrote:

>>      {"success": 0, "info": "", "result": null, "data": null,
>>    "errormsg": "can't adapt type 'Undefined'"}
>>
>>  Fixed
>
>>
>> *Fixed*

>>    - when comment is provided while creation, it gives error saying
>>    index does not exists. because schema name is not added before it.
>>
>> *Fixed*

>> *Surinder will send patch*

> Rule edit mode:
>>
>>    - Add comment in edit mode, check the SQL in sql tab. Now come back
>>    to general tab and removes comment and check the sql tab again.
>> *Surinder will send patch*

>> *Fixed*

>> *Fixed*

---

From: Murtuza Zabuawala <[email-masked]>
Date: Wed, 18 May 2016 16:19:28 +0530

Hi,

PFA table add-on patch version 8.3. (apply after 8.2)

On Wed, May 18, 2016 at 3:20 PM, Murtuza Zabuawala <
[email-masked]> wrote:

>>>                  node.js:94 Uncaught TypeError: self.canDrop.apply is
>>> not a function
>>>
>>>    - In table creation dialog, if table is inherited from another
>>>    table, if a new primary key is added manually there, the create sql will
>>>    not have an entry for primary key
>>>
>>> Fixed
>>
>>>
>>>    - In table creation dialog, if primary key check box is checked
>>>    while adding the row, a new row is added in primary key datagrid but
>>>    unchecking the primary key checkbox from column datagrid, does not removes
>>>    that row from primary key data grid.
>>>
>>> Fixed.
>>
>>>
>>>    - In Table creation dialog, if user has added an empty column
>>>    without entering its name or type and trying to add check constraint, it
>>>    will add an empty constraint
>>>
>>>    - In Table creation dialog, while adding an exclude constraint,
>>>    below mentioned error comes if user removes operator class by clicking 'x'
>>>    on that control Uncaught TypeError: Cannot read property 'id' of undefined
>>>
>>>  TODO
>>
>>>
>>>    - In Table creation dialog, schema should be prefixed with table
>>>    name in "of type" control
>>>
>>>  Fixed
>>
>>>
>>>    - If in edit mode, any constraint is already having any comment,
>>>    then remove it. It will not create the SQL for the same.
>>>
>>>
>> Fixed
>>
>>>
>>>    - While adding a column if no name is provided, "None" appears in
>>>    SQL tab which will give error on OK button click
>>>    - "+" sign is visible in browser tree in front of trigger.  either
>>>    On expanding trigger, it should show the trigger function name or that "+"
>>>    sign should not appear
>>>
>>> *Fixed*
>
>> Trigger edit node:
>>>
>>

---

From: Murtuza Zabuawala <[email-masked]>
Date: Wed, 18 May 2016 16:43:06 +0530

Hi,

PFA table add-on patch version 8.4. (apply after 8.3)
This includes changes for trigger node issues.

On Wed, May 18, 2016 at 4:19 PM, Murtuza Zabuawala <
[email-masked]> wrote:

>>>>    - In Table creation dialog, while adding a new column, data type
>>>>    and name field must be mandatory. otherwise while clicking on save it gives
>>>>    below error
>>>>
>>>>                   File
>>>>    - currently if user has clicked on add column button and
>>>>    immediately click on add primary key button, it will add a row in primary
>>>>    key data grid
>>>>
>>>> Fixed
>>>
>>>>
>>>>    - In Table creation dialog, while adding foreign key, in action
>>>>    tab. if user click on 'x' button in "on update" or "on delete" select2
>>>>    control, it gives error "Uncaught SyntaxError: Unexpected end of input"
>>>>
>>>> Fixed
>>>
>>>>
>>>>    - After successfully creation of table, "table name cannot be
>>>>    empty" error is not getting cleared.
>>>>
>>>> Not reproducible.
>>>
>>>>
>>>>    - In Table creation dialog, SQL is not getting generated for
>>>>    exclude constraint
>>>>
>>>> Fixed
>>>
>>>>
>>>>    - In Table creation dialog, while adding privileges, it always
>>>>    shows default privileges even if user has selected different privileges.
>>>>    (This works fine once user edit the privileges in edit table mode and shows
>>>>    only those privileges which user selects). Ashesh, please confirm the
>>>>    behaviour.
>>>>
>>>>    - DO INSTEAD button does not make any difference to SQL (it works
>>>>    in edit mode)
>>>>
>>>>    - SQL is not proper when creating a trigger. "()" should be
>>>>    appended to function name in SQL.

---

From: Surinder Kumar <[email-masked]>
Date: Wed, 18 May 2016 19:47:31 +0530

Hi,

Please find add-on patch for Column node.
Issue fixed:
1.  Column node, 'Primary key' switch should be hidden under view node,
refer column node visible condition for table node
2.  'Inherited from' column should be hidden.
2.  Other columns except Default value should be disabled.

On Wed, May 18, 2016 at 4:43 PM, Murtuza Zabuawala <
[email-masked]> wrote:

>>>>>    - In Table creation dialog, while adding a new primary key, it
>>>>>    does not allow to change the tablespace to empty. (which is not the case in
>>>>>    case of tablespace in table)
>>>>>
>>>>> Fixed
>>>>
>>>>>
>>>>>    for "character varying" column type, no operators are being listed
>>>>>
>>>>>    - Data type validation is not provided. Save button is enabled
>>>>>    just after providing column name
>>>>>    - Add comment in edit mode, check the SQL in sql tab. Now come
>>>>>    back to general tab and removes comment and check the sql tab again.

---

From: Harshal Dhumal <[email-masked]>
Date: Thu, 19 May 2016 22:38:14 +0530

Hi,

PFA updated patch for table and all it's child nodes (Version 9). This
patch does not depend on any of existing table node patch.

Major change in this patch: Unlike pgAdmin3 now in table create mode any
constraint(s) created (but not saved) will listen to table column changes
and adapt themselves accordingly.

For e.g.
In table create mode user adds column definition with name "col1" then adds
constraint which includes column "col1". Now user changes column name to
"col2" then constraint will listen to this change and adapt the column name
from "col1" to "col2" in constraint definition. Also if column "col2" is
removed then constraint will also remove the column "col2" from it's
definition.

-- 
*Harshal Dhumal*
*Software Engineer *

On Wed, May 18, 2016 at 7:47 PM, Surinder Kumar <
[email-masked]> wrote:

>>>>>>  I got below warning when I tried to apply the patch for table node
>>>>>> as mentioned below:
>>>>>>
>>>>>> Table creation:
>>>>>>
>>>>>>    - In Table creation dialog, While adding a new column, primary
>>>>>>    key should not be allowed to added unless user has provided name and data
>>>>>>    type for at least one column.
>>>>>>
>>>>>>  Fixed
>>>>>
>>>>>>
>>>>>>    - Length field limitation is not provided. (i.e. for numeric
>>>>>>    type, length should be allowed greater than 1000)
>>>>>>
>>>>>> *Fixed*
>>>>
>>>>>>    - No error message when column name is not provided while adding
>>>>>>    a column in index
>>>>>>    - "+" sign is visible in browser tree in front of trigger.
>>>>>>     either On expanding trigger, it should show the trigger function name or
>>>>>>    that "+" sign should not appear
>>>>>>
>>>>>> *Fixed*
>>>>
>>>>> Trigger edit node:
>>>>>>
>>>>>
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
new file mode 100644
index 0000000..408660b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
@@ -0,0 +1,2809 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Table Node """
+
+import json
+import re
+from flask import render_template, make_response, request, jsonify
+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.server_groups.servers.databases.schemas.utils \
+    import SchemaChildModule, DataTypeReader, VacuumSettings, \
+    trigger_definition, parse_rule_definition
+import pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+    parse_priv_to_db
+from functools import wraps
+
+
+class TableModule(SchemaChildModule):
+    """
+     class TableModule(SchemaChildModule)
+
+        A module class for Table node derived from SchemaChildModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the Table and it's base module.
+
+    * get_nodes(gid, sid, did, scid, tid)
+      - Method is used to generate the browser collection node.
+
+    * node_inode()
+      - Method is overridden from its base class to make the node as leaf node.
+
+    * script_load()
+      - Load the module script for schema, when any of the server node is
+        initialized.
+    """
+    NODE_TYPE = 'table'
+    COLLECTION_LABEL = gettext("Tables")
+
+    def __init__(self, *args, **kwargs):
+        """
+        Method is used to initialize the TableModule and it's base module.
+
+        Args:
+            *args:
+            **kwargs:
+        """
+        super(TableModule, self).__init__(*args, **kwargs)
+        self.max_ver = None
+        self.min_ver = None
+
+    def get_nodes(self, gid, sid, did, scid):
+        """
+        Generate the collection node
+        """
+        yield self.generate_browser_collection_node(scid)
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for database, when any of the database node is
+        initialized.
+        """
+        return database.DatabaseModule.NODE_TYPE
+
+
+blueprint = TableModule(__name__)
+
+
+class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
+    """
+    This class is responsible for generating routes for Table node
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the TableView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * list()
+      - This function is used to list all the Table nodes within that
+      collection.
+
+    * nodes()
+      - This function will used to create all the child node within that
+        collection, Here it will create all the Table node.
+
+    * properties(gid, sid, did, scid, tid)
+      - This function will show the properties of the selected Table node
+
+    * create(gid, sid, did, scid)
+      - This function will create the new Table object
+
+    * update(gid, sid, did, scid, tid)
+      - This function will update the data for the selected Table node
+
+    * delete(gid, sid, scid, tid):
+      - This function will drop the Table object
+
+    * truncate(gid, sid, scid, tid):
+      - This function will truncate table object
+
+    * set_trigger(gid, sid, scid, tid):
+      - This function will enable/disable trigger(s) on table object
+
+    * reset(gid, sid, scid, tid):
+      - This function will reset table object statistics
+
+    * msql(gid, sid, did, scid, tid)
+      - This function is used to return modified SQL for the selected
+        Table node
+
+    * get_sql(data, scid, tid)
+      - This function will generate sql from model data
+
+    * sql(gid, sid, did, scid, tid):
+      - This function will generate sql to show it in sql pane for the
+        selected Table node.
+
+    * dependency(gid, sid, did, scid, tid):
+      - This function will generate dependency list show it in dependency
+        pane for the selected Table node.
+
+    * dependent(gid, sid, did, scid, tid):
+      - This function will generate dependent list to show it in dependent
+        pane for the selected node.
+
+    * _formatter(data, tid)
+      - It will return formatted output of query result
+        as per client model format
+
+    * get_types(self, gid, sid, did, scid)
+      - This function will return list of types available for columns node
+        via AJAX response
+
+    * get_oftype(self, gid, sid, did, scid, tid)
+      - This function will return list of types available for table node
+        via AJAX response
+
+    * get_inherits(self, gid, sid, did, scid, tid)
+      - This function will return list of tables availablefor inheritance
+        via AJAX response
+
+    * get_relations(self, gid, sid, did, scid, tid)
+      - This function will return list of tables available for like/relation
+        via AJAX response
+
+    * get_columns(gid, sid, did, scid, foid=None):
+      - Returns the Table Columns.
+
+    * get_table_vacuum(gid, sid, did, scid=None, tid=None):
+      - Fetch the default values for table auto-vacuum
+
+    * get_toast_table_vacuum(gid, sid, did, scid=None, tid=None)
+      - Fetch the default values for toast table auto-vacuum
+
+    * _columns_formatter(tid, data):
+      - It will return formatted output of query result
+        as per client model format for column node
+
+    * _index_constraints_formatter(self, tid, data):
+      - It will return formatted output of query result
+        as per client model format for index constraint node
+
+    * _cltype_formatter(self, type):
+      - We need to remove [] from type and append it
+        after length/precision so we will set flag for
+        sql template
+
+    * _parse_format_columns(self, data, mode=None):
+       - This function will parse and return formatted list of columns
+         added by user
+
+    * get_index_constraint_sql(self, tid, data):
+      - This function will generate modified sql for index constraints
+        (Primary Key & Unique)
+
+    * select_sql(gid, sid, did, scid, foid):
+      - Returns sql for Script
+
+    * insert_sql(gid, sid, did, scid, foid):
+      - Returns sql for Script
+
+    * update_sql(gid, sid, did, scid, foid):
+      - Returns sql for Script
+
+    * delete_sql(gid, sid, did, scid, foid):
+      - Returns sql for Script
+"""
+
+    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': 'tid'}
+            ]
+
+    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_oftype': [{'get': 'get_oftype'}, {'get': 'get_oftype'}],
+        'get_inherits': [{'get': 'get_inherits'}, {'get': 'get_inherits'}],
+        'get_relations': [{'get': 'get_relations'}, {'get': 'get_relations'}],
+        'truncate': [{'put': 'truncate'}],
+        'reset': [{'delete': 'reset'}],
+        'set_trigger': [{'put': 'enable_disable_triggers'}],
+        'get_types': [{'get': 'types'}, {'get': 'types'}],
+        'get_columns': [{'get': 'get_columns'}, {'get': 'get_columns'}],
+        'get_table_vacuum': [{}, {'get': 'get_table_vacuum'}],
+        'get_toast_table_vacuum': [{}, {'get': 'get_toast_table_vacuum'}],
+        'all_tables': [{}, {'get': 'get_all_tables'}],
+        'get_access_methods': [{}, {'get': 'get_access_methods'}],
+        'get_oper_class': [{}, {'get': 'get_oper_class'}],
+        'get_operator': [{}, {'get': 'get_operator'}],
+        'select_sql': [{'get': 'select_sql'}],
+        'insert_sql': [{'get': 'insert_sql'}],
+        'update_sql': [{'get': 'update_sql'}],
+        'delete_sql': [{'get': 'delete_sql'}]
+
+    })
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            driver = get_driver(PG_DEFAULT_DRIVER)
+            self.manager = driver.connection_manager(kwargs['sid'])
+            self.conn = self.manager.connection(did=kwargs['did'])
+            self.qtIdent = driver.qtIdent
+            # We need datlastsysoid to check if current table is system table
+            self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    gettext(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            # We need datlastsysoid to check if current index is system index
+            self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+            # we will set template path for sql scripts
+            ver = self.manager.version
+            # Template for Column node
+            if ver >= 90500:
+                self.template_path = 'table/sql/9.5_plus'
+            else:
+                self.template_path = 'table/sql/9.1_plus'
+
+            # Template for Column ,check constraint and exclusion constraint node
+            if ver >= 90200:
+                self.column_template_path = 'column/sql/9.2_plus'
+                self.check_constraint_template_path = 'check_constraint/sql/9.2_plus'
+                self.exclusion_constraint_template_path = 'exclusion_constraint/sql/9.2_plus'
+            else:
+                self.column_template_path = 'column/sql/9.1_plus'
+                self.check_constraint_template_path = 'check_constraint/sql/9.1_plus'
+                self.exclusion_constraint_template_path = 'exclusion_constraint/sql/9.1_plus'
+
+            # Template for PK & Unique constraint node
+            self.index_constraint_template_path = 'index_constraint/sql'
+
+            # Template for foreign key constraint node
+            self.foreign_key_template_path = 'foreign_key/sql'
+
+            # Template for index node
+            self.index_template_path = 'index/sql/9.1_plus'
+
+            # Template for trigger node
+            self.trigger_template_path = 'trigger/sql/9.1_plus'
+
+            # Template for rules node
+            self.rules_template_path = 'rules/sql'
+
+            # Supported ACL for table
+            self.acl = ['a', 'r', 'w', 'd', 'D', 'x', 't']
+
+            # Supported ACL for columns
+            self.column_acl = ['a', 'r', 'w', 'x']
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+    @check_precondition
+    def list(self, gid, sid, did, scid):
+        """
+        This function is used to list all the table nodes within that collection.
+
+        Args:
+            gid: Server group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+
+        Returns:
+            JSON of available table nodes
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              scid=scid, datlastsysoid=self.datlastsysoid)
+        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):
+        """
+        This function is used to list all the table nodes within that collection.
+
+        Args:
+            gid: Server group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+
+        Returns:
+            JSON of available table nodes
+        """
+        res = []
+        SQL = render_template("/".join([self.template_path,
+                                        'nodes.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-table",
+                        tigger_count=row['triggercount'],
+                        has_enable_triggers=row['has_enable_triggers']
+                    ))
+
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    @check_precondition
+    def get_all_tables(self, gid, sid, did, scid, tid=None):
+        """
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+
+        Returns:
+            Returns the lits of tables required for constraints.
+        """
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_tables_for_constraints.sql']),
+                                  show_sysobj=self.blueprint.show_system_objects)
+
+            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 Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_table_vacuum(self, gid, sid, did, scid=None, tid=None):
+        """
+        Fetch the default values for table auto-vacuum
+        fields, return an array of
+          - label
+          - name
+          - setting
+        values
+        """
+        res = self.get_vacuum_table_settings(self.conn)
+        return ajax_response(
+            response=res['rows'],
+            status=200
+            )
+
+    @check_precondition
+    def get_toast_table_vacuum(self, gid, sid, did, scid=None, tid=None):
+        """
+        Fetch the default values for toast table auto-vacuum
+        fields, return an array of
+          - label
+          - name
+          - setting
+        values
+        """
+        res = self.get_vacuum_toast_settings(self.conn)
+        return ajax_response(
+            response=res['rows'],
+            status=200
+            )
+
+    @check_precondition
+    def get_access_methods(self, gid, sid, did, scid, tid=None):
+        """
+        This function returns access methods.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        res = [{'label': '', 'value': ''}]
+        sql = render_template(
+            "/".join([self.exclusion_constraint_template_path,
+                      'get_access_methods.sql']))
+        status, rest = self.conn.execute_2darray(sql)
+
+        if not status:
+            return internal_server_error(errormsg=rest)
+
+        for row in rest['rows']:
+            res.append(
+                {'label': row['amname'], 'value': row['amname']}
+            )
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    @check_precondition
+    def get_oper_class(self, gid, sid, did, scid, tid=None):
+        """
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        data = request.args if request.args else None
+        try:
+            if data and 'indextype' in data:
+                SQL = render_template(
+                    "/".join([self.exclusion_constraint_template_path,
+                              'get_oper_class.sql']),
+                    indextype=data['indextype'])
+
+                status, res = self.conn.execute_2darray(SQL)
+
+                if not status:
+                    return internal_server_error(errormsg=res)
+                result = []
+                for row in res['rows']:
+                    result.append([row['opcname'], row['opcname']])
+                return make_json_response(
+                    data=result,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_operator(self, gid, sid, did, scid, tid=None):
+        """
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        data = request.args if request.args else None
+        try:
+            if data and 'col_type' in data:
+                SQL = render_template(
+                    "/".join([self.exclusion_constraint_template_path,
+                              'get_operator.sql']),
+                    type=data['col_type'],
+                    show_sysobj=self.blueprint.show_system_objects)
+
+                status, res = self.conn.execute_2darray(SQL)
+
+                if not status:
+                    return internal_server_error(errormsg=res)
+                result = []
+                for row in res['rows']:
+                    result.append([row['oprname'], row['oprname']])
+                return make_json_response(
+                    data=result,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def _columns_formatter(self, tid, data):
+        """
+        Args:
+            tid: Table OID
+            data: dict of query result
+
+        Returns:
+            It will return formatted output of query result
+            as per client model format for column node
+        """
+        for column in data['columns']:
+
+            # We need to format variables according to client js collection
+            if 'attoptions' in column and column['attoptions'] is not None:
+                spcoptions = []
+                for spcoption in column['attoptions']:
+                    k, v = spcoption.split('=')
+                    spcoptions.append({'name': k, 'value': v})
+
+                column['attoptions'] = spcoptions
+
+            # Need to format security labels according to client js collection
+            if 'seclabels' in column and column['seclabels'] is not None:
+                seclabels = []
+                for seclbls in column['seclabels']:
+                    k, v = seclbls.split('=')
+                    seclabels.append({'provider': k, 'label': v})
+
+                column['seclabels'] = seclabels
+
+            if 'attnum' in column and column['attnum'] is not None and \
+                    column['attnum'] > 0:
+                # We need to parse & convert ACL coming from database to json format
+                SQL = render_template("/".join([self.column_template_path, 'acl.sql']),
+                                      tid=tid, clid=column['attnum'])
+                status, acl = self.conn.execute_dict(SQL)
+
+                if not status:
+                    return internal_server_error(errormsg=acl)
+
+                # We will set get privileges from acl sql so we don't need
+                # it from properties sql
+                column['attacl'] = []
+
+                for row in acl['rows']:
+                    priv = parse_priv_from_db(row)
+                    column.setdefault(row['deftype'], []).append(priv)
+
+                # we are receiving request when in edit mode
+                # we will send filtered types related to current type
+                present_type = column['cltype']
+
+                type_id = column['atttypid']
+
+                fulltype = self.get_full_type(
+                    column['typnspname'], column['typname'],
+                    column['isdup'], column['attndims'], column['atttypmod']
+                )
+
+                import re
+                # If we have length & precision both
+                matchObj = re.search(r'(\d+),(\d+)', fulltype)
+                if matchObj:
+                    column['attlen'] = matchObj.group(1)
+                    column['attprecision'] = matchObj.group(2)
+                else:
+                    # If we have length only
+                    matchObj = re.search(r'(\d+)', fulltype)
+                    if matchObj:
+                        column['attlen'] = matchObj.group(1)
+                        column['attprecision'] = None
+                    else:
+                        column['attlen'] = None
+                        column['attprecision'] = None
+
+                SQL = render_template("/".join([self.column_template_path,
+                                                'is_referenced.sql']),
+                                      tid=tid, clid=column['attnum'])
+
+                status, is_reference = self.conn.execute_scalar(SQL)
+
+                edit_types_list = list()
+                # We will need present type in edit mode
+                edit_types_list.append(present_type)
+
+                if int(is_reference) == 0:
+                    SQL = render_template("/".join([self.column_template_path,
+                                                    'edit_mode_types.sql']),
+                                          type_id=type_id)
+                    status, rset = self.conn.execute_2darray(SQL)
+
+                    for row in rset['rows']:
+                        edit_types_list.append(row['typname'])
+                else:
+                    edit_types_list.append(present_type)
+
+                column['edit_types'] = edit_types_list
+
+                # Manual Data type formatting
+                # If data type has () with them then we need to remove them
+                # eg bit(1) because we need to match the name with combobox
+                isArray = False
+                if column['cltype'].endswith('[]'):
+                    isArray = True
+                    column['cltype'] = column['cltype'].rstrip('[]')
+
+                idx = column['cltype'].find('(')
+                if idx and column['cltype'].endswith(')'):
+                    column['cltype'] = column['cltype'][:idx]
+
+                if isArray:
+                    column['cltype'] += "[]"
+
+                if 'indkey' in column:
+                    # Current column
+                    attnum = str(column['attnum'])
+
+                    # Single/List of primary key column(s)
+                    indkey = str(column['indkey'])
+
+                    # We will check if column is in primary column(s)
+                    if attnum in indkey.split(" "):
+                        column['is_primary_key'] = True
+                    else:
+                        column['is_primary_key'] = False
+
+        return data
+
+    def _index_constraints_formatter(self, tid, data):
+        """
+        Args:
+            tid: Table OID
+            data: dict of query result
+
+        Returns:
+            It will return formatted output of query result
+            as per client model format for index constraint node
+        """
+
+        # We will fetch all the index constraints for the table
+        index_constraints = {
+            'p': 'primary_key', 'u': 'unique_constraint'
+        }
+
+        for ctype in index_constraints.keys():
+            data[index_constraints[ctype]] = []
+
+            sql = render_template("/".join([self.index_constraint_template_path,
+                                            'properties.sql']),
+                                  tid=tid,
+                                  constraint_type=ctype)
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            for row in res['rows']:
+                result = row
+                sql = render_template(
+                    "/".join([self.index_constraint_template_path,
+                              'get_constraint_cols.sql']),
+                    cid=row['oid'],
+                    colcnt=row['indnatts'])
+                status, res = self.conn.execute_dict(sql)
+
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                columns = []
+                for r in res['rows']:
+                    columns.append({"column":   r['column'].strip('"')})
+
+                result['columns'] = columns
+
+                # If not exists then create list and/or append into
+                # existing list [ Adding into main data dict]
+                data.setdefault(index_constraints[ctype], []).append(result)
+
+        return data
+
+    def _foreign_key_formatter(self, tid, data):
+        """
+        Args:
+            tid: Table OID
+            data: dict of query result
+
+        Returns:
+            It will return formatted output of query result
+            as per client model format for foreign key constraint node
+        """
+
+        # We will fetch all the index constraints for the table
+        sql = render_template("/".join([self.foreign_key_template_path,
+                                        'properties.sql']),
+                                  tid=tid)
+
+        status, result = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=result)
+
+        for fk in result['rows']:
+
+            sql = render_template("/".join([self.foreign_key_template_path,
+                                        'get_constraint_cols.sql']),
+                              tid=tid,
+                              keys=zip(fk['confkey'], fk['conkey']),
+                              confrelid=fk['confrelid'])
+
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            columns = []
+            cols = []
+            for row in res['rows']:
+                columns.append({"local_column": row['conattname'],
+                                "references": fk['confrelid'],
+                                "referenced": row['confattname']})
+                cols.append(row['conattname'])
+
+            fk['columns'] = columns
+
+            SQL = render_template("/".join([self.foreign_key_template_path,
+                                            'get_parent.sql']),
+                                  tid=fk['columns'][0]['references'])
+
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            fk['remote_schema'] = rset['rows'][0]['schema']
+            fk['remote_table'] = rset['rows'][0]['table']
+
+            coveringindex = self.search_coveringindex(tid, cols)
+
+            fk['coveringindex'] = coveringindex
+            if coveringindex:
+                fk['autoindex'] = True
+                fk['hasindex'] = True
+            else:
+                fk['autoindex'] = False
+                fk['hasindex'] = False
+            # If not exists then create list and/or append into
+            # existing list [ Adding into main data dict]
+            data.setdefault('foreign_key', []).append(fk)
+
+        return data
+
+    def _check_constraint_formatter(self, tid, data):
+        """
+        Args:
+            tid: Table OID
+            data: dict of query result
+
+        Returns:
+            It will return formatted output of query result
+            as per client model format for check constraint node
+        """
+
+        # We will fetch all the index constraints for the table
+        SQL = render_template("/".join([self.check_constraint_template_path,
+                                        'properties.sql']),
+                              tid=tid)
+
+        status, res = self.conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+        # If not exists then create list and/or append into
+        # existing list [ Adding into main data dict]
+
+        data['check_constraint'] = res['rows']
+
+        return data
+
+    def _exclusion_constraint_formatter(self, tid, data):
+        """
+        Args:
+            tid: Table OID
+            data: dict of query result
+
+        Returns:
+            It will return formatted output of query result
+            as per client model format for exclusion constraint node
+        """
+
+        # We will fetch all the index constraints for the table
+        sql = render_template("/".join([self.exclusion_constraint_template_path,
+                                        'properties.sql']),
+                              tid=tid)
+
+        status, result = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=result)
+
+        for ex in result['rows']:
+
+            sql = render_template("/".join([self.exclusion_constraint_template_path,
+                                            'get_constraint_cols.sql']),
+                                  cid=ex['oid'],
+                                  colcnt=ex['indnatts'])
+
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            columns = []
+            for row in res['rows']:
+                if row['options'] & 1:
+                    order = False
+                    nulls_order = True if (row['options'] & 2) else False
+                else:
+                    order = True
+                    nulls_order = True if (row['options'] & 2) else False
+
+                columns.append({"column": row['coldef'].strip('"'),
+                                "oper_class": row['opcname'],
+                                "order": order,
+                                "nulls_order": nulls_order,
+                                "operator": row['oprname'],
+                                "col_type": row['datatype']
+                                })
+
+            ex['columns'] = columns
+            # If not exists then create list and/or append into
+            # existing list [ Adding into main data dict]
+            data.setdefault('exclude_constraint', []).append(ex)
+
+        return data
+
+    def search_coveringindex(self, tid, cols):
+        """
+
+        Args:
+          tid: Table id
+          cols: column list
+
+        Returns:
+
+        """
+
+        cols = set(cols)
+        SQL = render_template("/".join([self.foreign_key_template_path,
+                                        'get_constraints.sql']),
+                              tid=tid)
+        status, constraints = self.conn.execute_dict(SQL)
+
+        if not status:
+            raise Exception(constraints)
+
+        for costrnt in constraints['rows']:
+
+            sql = render_template(
+                "/".join([self.foreign_key_template_path, 'get_cols.sql']),
+                cid=costrnt['oid'],
+                colcnt=costrnt['indnatts'])
+            status, rest = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=rest)
+
+            indexcols = set()
+            for r in rest['rows']:
+                indexcols.add(r['column'].strip('"'))
+
+            if len(cols - indexcols) == len(indexcols - cols) == 0:
+                return costrnt["idxname"]
+
+        return None
+
+    def _formatter(self, scid, tid, data):
+        """
+        Args:
+            data: dict of query result
+            scid: schema oid
+            tid: table oid
+
+        Returns:
+            It will return formatted output of query result
+            as per client model format
+        """
+        # Need to format security labels according to client js collection
+        if 'seclabels' in data and data['seclabels'] is not None:
+            seclabels = []
+            for seclbls in data['seclabels']:
+                k, v = seclbls.split('=')
+                seclabels.append({'provider': k, 'label': v})
+
+            data['seclabels'] = seclabels
+
+        # We need to parse & convert ACL coming from database to json format
+        SQL = render_template("/".join([self.template_path, 'acl.sql']),
+                              tid=tid, scid=scid)
+        status, acl = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=acl)
+
+        # We will set get privileges from acl sql so we don't need
+        # it from properties sql
+        for row in acl['rows']:
+            priv = parse_priv_from_db(row)
+            if row['deftype'] in data:
+                data[row['deftype']].append(priv)
+            else:
+                data[row['deftype']] = [priv]
+
+        # We will add Auto vacuum defaults with out result for grid
+        data['vacuum_table'] = self.parse_vacuum_data(self.conn, data, 'table')
+        data['vacuum_toast'] = self.parse_vacuum_data(self.conn, data, 'toast')
+
+        # Fetch columns for the table logic
+        #
+        # 1) Check if of_type and inherited tables are present?
+        # 2) If yes then Fetch all the columns for of_type and inherited tables
+        # 3) Add columns in columns collection
+        # 4) Find all the columns for tables and filter out columns which are
+        #   not inherited from any table & format them one by one
+
+        # Get of_type table columns and add it into columns dict
+        if data['typname']:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_columns_for_table.sql']),
+                                  tname=data['typname'])
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+            data['columns'] = res['rows']
+
+        # Get inherited table(s) columns and add it into columns dict
+        elif data['coll_inherits'] and len(data['coll_inherits']) > 0:
+            columns = []
+            # Return all tables which can be inherited & do not show
+            # system columns
+            SQL = render_template("/".join([self.template_path, 'get_inherits.sql']),
+                                  show_system_objects=False
+                                  )
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            for row in rset['rows']:
+                if row['inherits'] in data['coll_inherits']:
+                    # Fetch columns using inherited table OID
+                    SQL = render_template("/".join([self.template_path,
+                                                    'get_columns_for_table.sql']),
+                                          tid=row['oid'])
+                    status, res = self.conn.execute_dict(SQL)
+                    if not status:
+                        return internal_server_error(errormsg=res)
+                    columns.extend(res['rows'][:])
+            data['columns'] = columns
+
+        # We will fetch all the columns for the table using
+        # columns properties.sql, so we need to set template path
+        SQL = render_template("/".join([self.column_template_path,
+                                        'properties.sql']),
+                              tid=tid,
+                              show_sys_objects=False
+                              )
+
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+        all_columns = res['rows']
+
+        # Filter inherited columns from all columns
+        if 'columns' in data and len(data['columns']) > 0 \
+            and len(all_columns) > 0:
+            columns = []
+            for row in data['columns']:
+                for i, col in enumerate(all_columns):
+                    # If both name are same then remove it
+                    # as it is inherited from other table
+                    if col['name'] == row['name']:
+                        # Remove same column from all_columns as
+                        # already have it columns collection
+                        del all_columns[i]
+
+            # If any column is added then update columns collection
+            if len(all_columns) > 0:
+                data['columns'] += all_columns
+        # If no inherited columns found then add all columns
+        elif len(all_columns) > 0:
+            data['columns'] = all_columns
+
+        if 'columns' in data and len(data['columns']) > 0:
+            data = self._columns_formatter(tid, data)
+
+        # Here we will add constraint in our output
+        data = self._index_constraints_formatter(tid, data)
+        data = self._foreign_key_formatter(tid, data)
+        data = self._check_constraint_formatter(tid, data)
+        data = self._exclusion_constraint_formatter(tid, data)
+
+        return data
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid):
+        """
+        This function will show the properties of the selected table node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did:  Database ID
+            scid: Schema ID
+            scid: Schema ID
+            tid: Table ID
+
+        Returns:
+            JSON of selected table node
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              scid=scid, tid=tid,
+                              datlastsysoid=self.datlastsysoid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+        data = res['rows'][0]
+
+        data['vacuum_settings_str'] = ""
+
+        if data['table_vacuum_settings_str'] is not None:
+            data['vacuum_settings_str'] += data[
+                'table_vacuum_settings_str'].replace(',', '\n')
+
+        if data['toast_table_vacuum_settings_str'] is not None:
+            data['vacuum_settings_str'] += '\n' + '\n'.join(
+                    ['toast_' + setting for setting in data[
+                        'toast_table_vacuum_settings_str'
+                    ].split(',')]
+                )
+        data['vacuum_settings_str'] = data[
+            'vacuum_settings_str'
+        ].replace("=", " = ")
+
+        data = self._formatter(scid, tid, data)
+
+        return ajax_response(
+                response=data,
+                status=200
+                )
+
+    @check_precondition
+    def types(self, gid, sid, did, scid, tid=None, clid=None):
+        """
+        Returns:
+            This function will return list of types available for column node
+            for node-ajax-control
+        """
+        condition = render_template("/".join([self.template_path,
+                                              'get_types_where_condition.sql']),
+                                    show_system_objects=self.blueprint.show_system_objects)
+
+        status, types = self.get_types(self.conn, condition)
+
+        if not status:
+            return internal_server_error(errormsg=types)
+
+        return make_json_response(
+            data=types,
+            status=200
+        )
+
+    @check_precondition
+    def get_columns(self, gid, sid, did, scid, tid=None):
+        """
+        Returns the Table Columns.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+
+        Returns:
+              JSON Array with below parameters.
+              name: Column Name
+              ctype: 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 'tid' in data:
+                SQL = render_template("/".join([self.template_path,
+                                                'get_columns_for_table.sql']),
+                                      tid=data['tid'])
+            elif data and 'tname' in data:
+                SQL = render_template("/".join([self.template_path,
+                                                'get_columns_for_table.sql']),
+                                      tname=data['tname'])
+
+            if SQL:
+                status, res = self.conn.execute_dict(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+                res = res['rows']
+
+            return make_json_response(
+                data=res,
+                status=200
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_oftype(self, gid, sid, did, scid, tid=None):
+        """
+        Returns:
+            This function will return list of types available for table node
+            for node-ajax-control
+        """
+        res = [{'label': '', 'value': ''}]
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_oftype.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['typname'], 'value': row['typname'],
+                             'tid': row['oid']
+                             }
+                        )
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_inherits(self, gid, sid, did, scid, tid=None):
+        """
+        Returns:
+            This function will return list of tables available for inheritance
+            while creating new table
+        """
+        try:
+            res = []
+            SQL = render_template("/".join([self.template_path, 'get_inherits.sql']),
+                                  show_system_objects=self.blueprint.show_system_objects
+                                  )
+            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['inherits'], 'value': row['inherits'],
+                             'tid': row['oid']
+                             }
+                        )
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_relations(self, gid, sid, did, scid, tid=None):
+        """
+        Returns:
+            This function will return list of tables available for like/relation
+            combobox while creating new table
+        """
+        res = [{'label': '', 'value': ''}]
+        try:
+            SQL = render_template("/".join([self.template_path, 'get_relations.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['like_relation'], 'value': row['like_relation']}
+                        )
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def _cltype_formatter(self, type):
+        """
+
+        Args:
+            data: Type string
+
+        Returns:
+            We need to remove [] from type and append it
+            after length/precision so we will set flag for
+            sql template
+        """
+        if '[]' in type:
+            type = type.replace('[]', '')
+            self.hasSqrBracket = True
+        else:
+            self.hasSqrBracket = False
+
+        return type
+
+    def _parse_format_columns(self, data, mode=None):
+        """
+        data:
+            Data coming from client side
+
+        Returns:
+            This function will parse and return formatted list of columns
+            added by user
+        """
+        columns = data['columns']
+        # 'EDIT' mode
+        if mode is not None:
+            for action in ['added', 'changed']:
+                if action in columns:
+                    final_columns = []
+                    for c in columns[action]:
+                        if 'inheritedfrom' not in c:
+                            final_columns.append(c)
+
+                    for c in final_columns:
+                        if 'attacl' in c:
+                            c['attacl'] = parse_priv_to_db(c['attacl'], self.column_acl)
+
+                        # check type for '[]' in it
+                        c['cltype'] = self._cltype_formatter(c['cltype'])
+                        c['hasSqrBracket'] = self.hasSqrBracket
+
+                    data['columns'][action] = final_columns
+        else:
+            # We need to exclude all the columns which are inherited from other tables
+            # 'CREATE' mode
+            final_columns = []
+
+            for c in columns:
+                if 'inheritedfrom' not in c:
+                    final_columns.append(c)
+
+            # Now we have all lis of columns which we need
+            # to include in our create definition, Let's format them
+            for c in final_columns:
+                if 'attacl' in c:
+                    c['attacl'] = parse_priv_to_db(c['attacl'], self.column_acl)
+
+                # check type for '[]' in it
+                c['cltype'] = self._cltype_formatter(c['cltype'])
+                c['hasSqrBracket'] = self.hasSqrBracket
+
+            data['columns'] = final_columns
+
+        return data
+
+    @check_precondition
+    def create(self, gid, sid, did, scid):
+        """
+        This function will creates new the table object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        for k, v in data.items():
+            try:
+                data[k] = json.loads(v)
+            except (ValueError, TypeError):
+                data[k] = v
+
+        required_args = [
+            'name'
+        ]
+
+        for arg in required_args:
+            if arg not in data:
+                return make_json_response(
+                    status=410,
+                    success=0,
+                    errormsg=gettext(
+                        "Couldn't find the required parameter (%s)." % arg
+                    )
+                )
+
+        # Parse privilege data coming from client according to database format
+        if 'relacl' in data:
+            data['relacl'] = parse_priv_to_db(data['relacl'], self.acl)
+
+        # Parse & format columns
+        data = self._parse_format_columns(data)
+
+        # 'coll_inherits' is Array but it comes as string from browser
+        # We will convert it again to list
+        if 'coll_inherits' in data and \
+                isinstance(data['coll_inherits'], str):
+            data['coll_inherits'] = json.loads(data['coll_inherits'])
+
+        if 'foreign_key' in data:
+            for c in data['foreign_key']:
+                SQL = render_template("/".join([self.foreign_key_template_path,
+                                                'get_parent.sql']),
+                                      tid=c['columns'][0]['references'])
+                status, rset = self.conn.execute_2darray(SQL)
+                if not status:
+                    return internal_server_error(errormsg=rset)
+
+                c['remote_schema'] = rset['rows'][0]['schema']
+                c['remote_table'] = rset['rows'][0]['table']
+
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'create.sql']),
+                                  data=data, conn=self.conn)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # we need oid to to add object in tree at browser
+            SQL = render_template("/".join([self.template_path,
+                                            'get_oid.sql']), scid=scid, data=data)
+            status, tid = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=tid)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    tid,
+                    scid,
+                    data['name'],
+                    icon="icon-table"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid):
+        """
+        This function will update an existing table object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+        """
+        data = request.form if request.form else json.loads(
+            request.data.decode()
+        )
+
+        for k, v in data.items():
+            try:
+                data[k] = json.loads(v)
+            except (ValueError, TypeError):
+                data[k] = v
+
+        try:
+            SQL = self.get_sql(scid, tid, data)
+
+            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="Table updated",
+                    data={
+                        'id': tid,
+                        'scid': scid,
+                        'did': did
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': tid,
+                        'scid': scid,
+                        'did': did
+                    }
+                )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid):
+        """
+        This function will deletes the table object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+        """
+        # Below will decide if it's simple drop or drop with cascade call
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  scid=scid, tid=tid,
+                                  datlastsysoid=self.datlastsysoid)
+            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,
+                                            'delete.sql']),
+                                  data=data, cascade=cascade,
+                                  conn=self.conn)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=gettext("Table dropped"),
+                data={
+                    'id': tid,
+                    'scid': scid
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def truncate(self, gid, sid, did, scid, tid):
+        """
+        This function will truncate the table object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+        """
+        # Below will decide if it's simple drop or drop with cascade call
+        data = request.form if request.form else json.loads(request.data.decode())
+        # Convert str 'true' to boolean type
+        is_cascade = json.loads(data['cascade'])
+
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  scid=scid, tid=tid,
+                                  datlastsysoid=self.datlastsysoid)
+            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,
+                                            'truncate.sql']),
+                                  data=data, cascade=is_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("Table truncated"),
+                data={
+                    'id': tid,
+                    'scid': scid
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def enable_disable_triggers(self, gid, sid, did, scid, tid):
+        """
+        This function will enable/disable trigger(s) on the table object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+        """
+        # Below will decide if it's simple drop or drop with cascade call
+        data = request.form if request.form else json.loads(request.data.decode())
+        # Convert str 'true' to boolean type
+        is_enable = json.loads(data['enable'])
+
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  scid=scid, tid=tid,
+                                  datlastsysoid=self.datlastsysoid)
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+            data = res['rows'][0]
+            # TODO://
+            # Find SQL which can enable all or disable all triggers
+            SQL = render_template("/".join([self.template_path,
+                                            'enable_disable_trigger.sql']),
+                                  data=data, is_enable_trigger=is_enable)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=gettext("Trigger(s) has been enabled") if is_enable
+                else gettext("Trigger(s) has been disabled"),
+                data={
+                    'id': tid,
+                    'scid': scid
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def reset(self, gid, sid, did, scid, tid):
+        """
+        This function will reset statistics of table
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+        """
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'reset_stats.sql']),
+                                  tid=tid)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=gettext("Table statistics has been reset"),
+                data={
+                    'id': tid,
+                    'scid': scid
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid=None):
+        """
+        This function will create modified sql for table object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+        """
+        data = dict()
+        for k, v in request.args.items():
+            try:
+                data[k] = json.loads(v)
+            except (ValueError, TypeError):
+                data[k] = v
+
+        try:
+            SQL = self.get_sql(scid, tid, data)
+            SQL = re.sub('\n{2,}', '\n', SQL)
+            SQL = SQL.strip('\n')
+            return make_json_response(
+                data=SQL,
+                status=200
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def get_index_constraint_sql(self, tid, data):
+        """
+         Args:
+           tid: Table ID
+           data: data dict coming from the client
+
+        Returns:
+        This function will generate modified sql for index constraints
+        (Primary Key & Unique)
+        """
+        sql = []
+        # We will fetch all the index constraints for the table
+        index_constraints = {
+            'p': 'primary_key', 'u': 'unique_constraint'
+        }
+
+        for ctype in index_constraints.keys():
+            # Check if constraint is in data
+            # If yes then we need to check for add/change/delete
+            if index_constraints[ctype] in data:
+                constraint = data[index_constraints[ctype]]
+                # If constraint(s) is/are deleted
+                if 'deleted' in constraint:
+                    for c in constraint['deleted']:
+                        c['schema'] = data['schema']
+                        c['table'] = data['name']
+
+                        # Sql for drop
+                        sql.append(
+                            render_template("/".join(
+                            [self.index_constraint_template_path,
+                             'delete.sql']),
+                                data=c, conn=self.conn).strip('\n')
+                        )
+
+                if 'changed' in constraint:
+                    for c in constraint['changed']:
+                        c['schema'] = data['schema']
+                        c['table'] = data['name']
+
+                        properties_sql = render_template("/".join(
+                            [self.index_constraint_template_path, 'properties.sql']),
+                            tid=tid, cid=c['oid'], constraint_type=ctype)
+                        status, res = self.conn.execute_dict(properties_sql)
+                        if not status:
+                            return internal_server_error(errormsg=res)
+
+                        old_data = res['rows'][0]
+                        # Sql to update object
+                        sql.append(
+                            render_template("/".join([
+                                self.index_constraint_template_path,
+                                'update.sql']), data=c, o_data=old_data,
+                                conn=self.conn).strip('\n')
+                        )
+
+                if 'added' in constraint:
+                    for c in constraint['added']:
+                        c['schema'] = data['schema']
+                        c['table'] = data['name']
+
+                        # Sql to add object
+                        if self.validate_constrains(index_constraints[ctype], c):
+                            sql.append(
+                                render_template(
+                                    "/".join([self.index_constraint_template_path,
+                                              'create.sql']),
+                                    data=c, conn=self.conn,
+                                    constraint_name='PRIMARY KEY'
+                                    if ctype == 'p' else 'UNIQUE'
+                                    ).strip('\n')
+                            )
+                            # sql to update comments
+                            sql.append(
+                                render_template(
+                                    "/".join([self.index_constraint_template_path,
+                                              'alter.sql']),
+                                    data=c, conn=self.conn
+                                    ).strip('\n')
+                            )
+                        else:
+                            sql.append(
+                                gettext(
+                                    '-- incomplete definition for {0} constraint'.format(index_constraints[ctype])
+                                )
+                            )
+        if len(sql) > 0:
+            # Join all the sql(s) as single string
+            return '\n\n'.join(sql)
+        else:
+            return None
+
+    def get_foreign_key_sql(self, tid, data):
+        """
+         Args:
+           tid: Table ID
+           data: data dict coming from the client
+
+        Returns:
+        This function will generate modified sql for foreign key
+        """
+        sql = []
+        # Check if constraint is in data
+        # If yes then we need to check for add/change/delete
+        if 'foreign_key' in data:
+            constraint = data['foreign_key']
+            # If constraint(s) is/are deleted
+            if 'deleted' in constraint:
+                for c in constraint['deleted']:
+                    c['schema'] = data['schema']
+                    c['table'] = data['name']
+
+                    # Sql for drop
+                    sql.append(
+                        render_template("/".join(
+                        [self.foreign_key_template_path,
+                         'delete.sql']),
+                            data=c, conn=self.conn).strip('\n')
+                    )
+
+            if 'changed' in constraint:
+                for c in constraint['changed']:
+                    c['schema'] = data['schema']
+                    c['table'] = data['name']
+
+                    properties_sql = render_template("/".join(
+                        [self.foreign_key_template_path, 'properties.sql']),
+                        tid=tid, cid=c['oid'])
+                    status, res = self.conn.execute_dict(properties_sql)
+                    if not status:
+                        return internal_server_error(errormsg=res)
+
+                    old_data = res['rows'][0]
+                    # Sql to update object
+                    sql.append(
+                        render_template("/".join([
+                            self.foreign_key_template_path,
+                            'update.sql']), data=c, o_data=old_data,
+                            conn=self.conn).strip('\n')
+                    )
+
+                    if not self.validate_constrains('foreign_key', c):
+                        sql.append(
+                            gettext(
+                                '-- incomplete definition for foreign_key constraint'
+                            )
+                        )
+                        return '\n\n'.join(sql)
+
+                    cols = []
+                    for col in c['columns']:
+                        cols.append(col['local_column'])
+
+                    coveringindex = self.search_coveringindex(tid, cols)
+
+                    if coveringindex is None and 'autoindex' in c and c['autoindex'] and\
+                        ('coveringindex' in c and
+                             c['coveringindex'] != ''):
+                        sql.append(render_template(
+                            "/".join([self.foreign_key_template_path, 'create_index.sql']),
+                            data=c, conn=self.conn).strip('\n')
+                        )
+
+            if 'added' in constraint:
+                for c in constraint['added']:
+                    c['schema'] = data['schema']
+                    c['table'] = data['name']
+
+                    # Sql to add object
+                    # Columns
+
+                    if not self.validate_constrains('foreign_key', c):
+                        sql.append(
+                            gettext(
+                                '-- incomplete definition for foreign_key constraint'
+                            )
+                        )
+                        return '\n\n'.join(sql)
+
+                    SQL = render_template("/".join([self.foreign_key_template_path,
+                                                    'get_parent.sql']),
+                                          tid=c['columns'][0]['references'])
+                    status, rset = self.conn.execute_2darray(SQL)
+                    if not status:
+                        return internal_server_error(errormsg=rset)
+
+                    c['remote_schema'] = rset['rows'][0]['schema']
+                    c['remote_table'] = rset['rows'][0]['table']
+
+                    sql.append(
+                        render_template(
+                            "/".join([self.foreign_key_template_path,
+                                      'create.sql']),
+                            data=c, conn=self.conn
+                            ).strip('\n')
+                    )
+                    # sql to update comments
+                    sql.append(
+                        render_template(
+                            "/".join([self.foreign_key_template_path,
+                                      'alter.sql']),
+                            data=c, conn=self.conn
+                            ).strip('\n')
+                    )
+
+                    if c['autoindex']:
+                        sql.append(
+                            render_template(
+                                "/".join([self.foreign_key_template_path,
+                                          'create_index.sql']),
+                                data=c, conn=self.conn).strip('\n')
+                        )
+
+        if len(sql) > 0:
+            # Join all the sql(s) as single string
+            return '\n\n'.join(sql)
+        else:
+            return None
+
+    def get_check_constraint_sql(self, tid, data):
+        """
+         Args:
+           tid: Table ID
+           data: data dict coming from the client
+
+        Returns:
+          This function will generate modified sql for check constraint
+        """
+        sql = []
+        # Check if constraint is in data
+        # If yes then we need to check for add/change/delete
+        if 'check_constraint' in data:
+            constraint = data['check_constraint']
+            # If constraint(s) is/are deleted
+            if 'deleted' in constraint:
+                for c in constraint['deleted']:
+                    c['schema'] = data['schema']
+                    c['table'] = data['name']
+
+                    # Sql for drop
+                    sql.append(
+                        render_template("/".join(
+                            [self.check_constraint_template_path,
+                             'delete.sql']),
+                            data=c, conn=self.conn).strip('\n')
+                    )
+
+            if 'changed' in constraint:
+                for c in constraint['changed']:
+                    c['schema'] = data['schema']
+                    c['table'] = data['name']
+
+                    properties_sql = render_template("/".join(
+                        [self.check_constraint_template_path, 'properties.sql']),
+                        tid=tid, cid=c['oid'])
+                    status, res = self.conn.execute_dict(properties_sql)
+                    if not status:
+                        return internal_server_error(errormsg=res)
+
+                    old_data = res['rows'][0]
+                    # Sql to update object
+                    sql.append(
+                        render_template("/".join([
+                            self.check_constraint_template_path,
+                            'update.sql']), data=c, o_data=old_data,
+                            conn=self.conn).strip('\n')
+                    )
+
+            if 'added' in constraint:
+                for c in constraint['added']:
+                    c['schema'] = data['schema']
+                    c['table'] = data['name']
+
+                    if not self.validate_constrains('check_constraint', c):
+                        sql.append(
+                            gettext(
+                                '-- incomplete definition for check_constraint'
+                            )
+                        )
+                        return '\n\n'.join(sql)
+
+                    sql.append(
+                        render_template(
+                            "/".join([self.check_constraint_template_path,
+                                      'create.sql']),
+                            data=c, conn=self.conn
+                            ).strip('\n')
+                    )
+                    # sql to update comments
+                    sql.append(
+                        render_template(
+                            "/".join([self.check_constraint_template_path,
+                                      'alter.sql']),
+                            data=c, conn=self.conn
+                            ).strip('\n')
+                    )
+
+        if len(sql) > 0:
+            # Join all the sql(s) as single string
+            return '\n\n'.join(sql)
+        else:
+            return None
+
+    def get_exclusion_constraint_sql(self, tid, data):
+        """
+         Args:
+           tid: Table ID
+           data: data dict coming from the client
+
+        Returns:
+          This function will generate modified sql for exclusion constraint
+        """
+        sql = []
+        # Check if constraint is in data
+        # If yes then we need to check for add/change/delete
+        if 'exclude_constraint' in data:
+            constraint = data['exclude_constraint']
+            # If constraint(s) is/are deleted
+            if 'deleted' in constraint:
+                for c in constraint['deleted']:
+                    c['schema'] = data['schema']
+                    c['table'] = data['name']
+
+                    # Sql for drop
+                    sql.append(
+                        render_template("/".join(
+                            [self.exclusion_constraint_template_path,
+                             'delete.sql']),
+                            data=c, conn=self.conn).strip('\n')
+                    )
+
+            if 'changed' in constraint:
+                for c in constraint['changed']:
+                    c['schema'] = data['schema']
+                    c['table'] = data['name']
+
+                    properties_sql = render_template("/".join(
+                        [self.exclusion_constraint_template_path, 'properties.sql']),
+                        tid=tid, cid=c['oid'])
+                    status, res = self.conn.execute_dict(properties_sql)
+                    if not status:
+                        return internal_server_error(errormsg=res)
+
+                    old_data = res['rows'][0]
+                    # Sql to update object
+                    sql.append(
+                        render_template("/".join([
+                            self.exclusion_constraint_template_path,
+                            'update.sql']), data=c, o_data=old_data,
+                            conn=self.conn).strip('\n')
+                    )
+
+            if 'added' in constraint:
+                for c in constraint['added']:
+                    c['schema'] = data['schema']
+                    c['table'] = data['name']
+
+                    if not self.validate_constrains('exclude_constraint', c):
+                        sql.append(
+                            gettext(
+                                '-- incomplete definition for exclusion_constraint'
+                            )
+                        )
+                        return '\n\n'.join(sql)
+
+                    sql.append(
+                        render_template(
+                            "/".join([self.exclusion_constraint_template_path,
+                                      'create.sql']),
+                            data=c, conn=self.conn
+                            ).strip('\n')
+                    )
+                    # sql to update comments
+                    sql.append(
+                        render_template(
+                            "/".join([self.exclusion_constraint_template_path,
+                                      'alter.sql']),
+                            data=c, conn=self.conn
+                            ).strip('\n')
+                    )
+
+        if len(sql) > 0:
+            # Join all the sql(s) as single string
+            return '\n\n'.join(sql)
+        else:
+            return None
+
+    def get_sql(self, scid, tid, data):
+        """
+        This function will generate create/update sql from model data
+        coming from client
+        """
+        if tid is not None:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  scid=scid, tid=tid,
+                                  datlastsysoid=self.datlastsysoid)
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            old_data = res['rows'][0]
+            old_data = self._formatter(scid, tid, old_data)
+
+            # We will convert privileges coming from client required
+            if 'relacl' in data:
+                for mode in ['added', 'changed', 'deleted']:
+                    if mode in data['relacl']:
+                        data['relacl'][mode] = parse_priv_to_db(
+                            data['relacl'][mode], self.acl
+                        )
+
+            # If name if not present
+            if 'name' not in data:
+                data['name'] = old_data['name']
+            # If name if not present
+            if 'schema' not in data:
+                data['schema'] = old_data['schema']
+
+            # Filter out new tables from list, we will send complete list
+            # and not newly added tables in the list from client
+            # so we will filter new tables here
+            if 'coll_inherits' in data:
+                p_len = len(old_data['coll_inherits'])
+                c_len = len(data['coll_inherits'])
+                # If table(s) added
+                if c_len > p_len:
+                    data['coll_inherits_added'] = list(
+                        set(data['coll_inherits']) - set(old_data['coll_inherits'])
+                    )
+                # If table(s)removed
+                elif c_len < p_len:
+                    data['coll_inherits_removed'] = list(
+                        set(old_data['coll_inherits']) - set(data['coll_inherits'])
+                    )
+                # Safe side verification,In case it happens..
+                # If user removes and adds same number of table
+                # eg removed one table and added one new table
+                elif c_len == p_len:
+                    data['coll_inherits_added'] = list(
+                        set(data['coll_inherits']) - set(old_data['coll_inherits'])
+                    )
+                    data['coll_inherits_removed'] = list(
+                        set(old_data['coll_inherits']) - set(data['coll_inherits'])
+                    )
+
+            SQL = render_template("/".join([self.template_path, 'update.sql']),
+                                  o_data=old_data, data=data, conn=self.conn)
+            # Removes training new lines
+            SQL = SQL.strip('\n') + '\n\n'
+
+            # Parse/Format columns & create sql
+            if 'columns' in data:
+                # Parse the data coming from client
+                data = self._parse_format_columns(data, mode='edit')
+
+                columns = data['columns']
+                column_sql = '\n'
+
+                # If column(s) is/are deleted
+                if 'deleted' in columns:
+                    for c in columns['deleted']:
+                        c['schema'] = data['schema']
+                        c['table'] = data['name']
+                        # Sql for drop column
+                        if 'inheritedfrom' not in c:
+                            column_sql += render_template("/".join(
+                                [self.column_template_path, 'delete.sql']),
+                                data=c, conn=self.conn).strip('\n') + '\n\n'
+
+                # If column(s) is/are changed
+                # Here we will be needing previous properties of column
+                # so that we can compare & update it
+                if 'changed' in columns:
+                    for c in columns['changed']:
+                        c['schema'] = data['schema']
+                        c['table'] = data['name']
+                        if 'attacl' in c:
+                            c['attacl'] = parse_priv_to_db(c['attacl'],
+                                                           self.column_acl)
+
+                        properties_sql = render_template("/".join([self.column_template_path,
+                                            'properties.sql']),
+                                            tid=tid,
+                                            clid=c['attnum'],
+                                            show_sys_objects=self.blueprint.show_system_objects
+                                            )
+
+                        status, res = self.conn.execute_dict(properties_sql)
+                        if not status:
+                            return internal_server_error(errormsg=res)
+                        old_data = res['rows'][0]
+
+                        old_data['cltype'] = self._cltype_formatter(old_data['cltype'])
+                        old_data['hasSqrBracket'] = self.hasSqrBracket
+
+                        # Sql for alter column
+                        if 'inheritedfrom' not in c:
+                            column_sql += render_template("/".join(
+                                [self.column_template_path, 'update.sql']),
+                                data=c, o_data=old_data, conn=self.conn).strip('\n') + '\n\n'
+
+                # If column(s) is/are added
+                if 'added' in columns:
+                    for c in columns['added']:
+                        c['schema'] = data['schema']
+                        c['table'] = data['name']
+                        # Sql for create column
+                        if 'attacl' in c:
+                            c['attacl'] = parse_priv_to_db(c['attacl'],
+                                                           self.column_acl)
+                        if 'inheritedfrom' not in c:
+                            column_sql += render_template("/".join(
+                                [self.column_template_path, 'create.sql']),
+                                data=c, conn=self.conn).strip('\n') + '\n\n'
+
+                # Combine all the SQL together
+                SQL += column_sql.strip('\n')
+
+            # Check if index constraints are added/changed/deleted
+            index_constraint_sql = self.get_index_constraint_sql(tid, data)
+            # If we have index constraint sql then ad it in main sql
+            if index_constraint_sql is not None:
+                SQL += '\n' + index_constraint_sql
+
+            # Check if foreign key(s) is/are added/changed/deleted
+            foreign_key_sql = self.get_foreign_key_sql(tid, data)
+            # If we have foreign key sql then ad it in main sql
+            if foreign_key_sql is not None:
+                SQL += '\n' + foreign_key_sql
+
+            # Check if check constraint(s) is/are added/changed/deleted
+            check_constraint_sql = self.get_check_constraint_sql(tid, data)
+            # If we have check constraint sql then ad it in main sql
+            if check_constraint_sql is not None:
+                SQL += '\n' + check_constraint_sql
+
+            # Check if exclusion constraint(s) is/are added/changed/deleted
+            exclusion_constraint_sql = self.get_exclusion_constraint_sql(tid, data)
+            # If we have check constraint sql then ad it in main sql
+            if exclusion_constraint_sql is not None:
+                SQL += '\n' + exclusion_constraint_sql
+
+        else:
+            required_args = [
+                'name'
+            ]
+
+            for arg in required_args:
+                if arg not in data:
+                    return gettext('-- incomplete definition')
+
+            # validate constraint data.
+            for key in ['primary_key', 'unique_constraint',
+                        'foreign_key', 'check_constraint',
+                        'exclude_constraint']:
+                if key in data and len(data[key]) > 0:
+                    for constraint in data[key]:
+                        if not self.validate_constrains(key, constraint):
+                            return gettext('-- incomplete definition for {0}'.format(key))
+
+            # We will convert privileges coming from client required
+            # in server side format
+            if 'relacl' in data:
+                data['relacl'] = parse_priv_to_db(data['relacl'], self.acl)
+
+            # Parse & format columns
+            data = self._parse_format_columns(data)
+
+            if 'foreign_key' in data:
+                for c in data['foreign_key']:
+                    SQL = render_template("/".join([self.foreign_key_template_path,
+                                                    'get_parent.sql']),
+                                          tid=c['columns'][0]['references'])
+                    status, rset = self.conn.execute_2darray(SQL)
+                    if not status:
+                        return internal_server_error(errormsg=rset)
+
+                    c['remote_schema'] = rset['rows'][0]['schema']
+                    c['remote_table'] = rset['rows'][0]['table']
+
+            # If the request for new object which do not have did
+            SQL = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=data, conn=self.conn)
+        SQL = re.sub('\n{2,}', '\n', SQL)
+        SQL = SQL.strip('\n')
+
+        return SQL
+
+    def validate_constrains(self, key, data):
+
+        if key == 'primary_key' or key == 'unique_constraint':
+            if 'columns' in data and len(data['columns']) > 0:
+                return True
+            else:
+                return False
+        elif key == 'foreign_key':
+            for arg in ['columns']:
+                if arg not in data:
+                    return False
+                elif isinstance(data[arg], list) and len(data[arg]) < 1:
+                    return False
+
+            if data['autoindex'] and ('coveringindex' not in data or
+                                      data['coveringindex'] == ''):
+                return False
+
+            return True
+
+        elif key == 'check_constraint':
+            for arg in ['consrc']:
+                if arg not in data or data[arg] == '':
+                    return False
+            return True
+
+        elif key == 'exclude_constraint':
+            pass
+
+        return True
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, tid):
+        """
+        This function get the dependents and return ajax response
+        for the table node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+        """
+        # Specific condition for column which we need to append
+        where = "WHERE dep.refobjid={0}::OID".format(tid)
+
+        dependents_result = self.get_dependents(
+            self.conn, tid
+        )
+
+        # Specific sql to run againt column to fetch dependents
+        SQL = render_template("/".join([self.template_path,
+                                        'depend.sql']), where=where)
+
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        for row in res['rows']:
+            ref_name = row['refname']
+            if ref_name is None:
+                continue
+
+            dep_type = ''
+            dep_str = row['deptype']
+            if dep_str == 'a':
+                dep_type = 'auto'
+            elif dep_str == 'n':
+                dep_type = 'normal'
+            elif dep_str == 'i':
+                dep_type = 'internal'
+
+            dependents_result.append({'type': 'sequence', 'name': ref_name, 'field': dep_type})
+
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, tid):
+        """
+        This function get the dependencies and return ajax response
+        for the table node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+
+        """
+        dependencies_result = self.get_dependencies(
+            self.conn, tid
+        )
+
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, tid):
+        """
+        This function will creates reverse engineered sql for
+        the table object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+        """
+        main_sql = []
+
+        """
+        #####################################
+        # 1) Reverse engineered sql for TABLE
+        #####################################
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              scid=scid, tid=tid,
+                              datlastsysoid=self.datlastsysoid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+
+        # Table & Schema declaration so that we can use them in child nodes
+        schema = data['schema']
+        table = data['name']
+
+        data = self._formatter(scid, tid, data)
+
+        # Now we have all lis of columns which we need
+        # to include in our create definition, Let's format them
+        if 'columns' in data:
+            for c in data['columns']:
+                if 'attacl' in c:
+                    c['attacl'] = parse_priv_to_db(c['attacl'], self.column_acl)
+
+                # check type for '[]' in it
+                c['cltype'] = self._cltype_formatter(c['cltype'])
+                c['hasSqrBracket'] = self.hasSqrBracket
+
+        sql_header = "-- Table: {0}\n\n-- ".format(self.qtIdent(self.conn,
+                                                                data['schema'],
+                                                                data['name']))
+        sql_header += render_template("/".join([self.template_path,
+                                                'delete.sql']),
+                                      data=data, conn=self.conn)
+
+        sql_header = sql_header.strip('\n')
+        sql_header += '\n'
+
+        # Add into main sql
+        main_sql.append(sql_header)
+
+        # Parse privilege data
+        if 'relacl' in data:
+            data['relacl'] = parse_priv_to_db(data['relacl'], self.acl)
+
+        # If the request for new object which do not have did
+        table_sql = render_template("/".join([self.template_path,
+                                              'create.sql']),
+                                    data=data, conn=self.conn)
+
+        # Add into main sql
+        table_sql = re.sub('\n{2,}', '\n\n', table_sql)
+        main_sql.append(table_sql.strip('\n'))
+
+        """
+        ######################################
+        # 2) Reverse engineered sql for INDEX
+        ######################################
+        """
+
+        SQL = render_template("/".join([self.index_template_path,
+                                        'nodes.sql']), tid=tid)
+        status, rset = self.conn.execute_2darray(SQL)
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        for row in rset['rows']:
+
+            SQL = render_template("/".join([self.index_template_path,
+                                            'properties.sql']),
+                                  tid=tid, idx=row['oid'],
+                                  datlastsysoid=self.datlastsysoid)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = dict(res['rows'][0])
+            # Adding parent into data dict, will be using it while creating sql
+            data['schema'] = schema
+            data['table'] = table
+            # We also need to fecth columns of index
+            SQL = render_template("/".join([self.index_template_path,
+                                            'column_details.sql']),
+                                  idx=row['oid'])
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            # 'attdef' comes with quotes from query so we need to strip them
+            # 'options' we need true/false to render switch ASC(false)/DESC(true)
+            columns = []
+            cols = []
+            for row in rset['rows']:
+                # We need all data as collection for ColumnsModel
+                cols_data = {
+                    'colname': row['attdef'].strip('"'),
+                    'collspcname': row['collnspname'],
+                    'op_class': row['opcname'],
+                }
+                if row['options'][0] == 'DESC':
+                    cols_data['sort_order'] = True
+                columns.append(cols_data)
+
+                # We need same data as string to display in properties window
+                # If multiple column then separate it by colon
+                cols_str = row['attdef']
+                if row['collnspname']:
+                    cols_str += ' COLLATE ' + row['collnspname']
+                if row['opcname']:
+                    cols_str += ' ' + row['opcname']
+                if row['options'][0] == 'DESC':
+                    cols_str += ' DESC'
+                cols.append(cols_str)
+
+            # Push as collection
+            data['columns'] = columns
+            # Push as string
+            data['cols'] = ', '.join(cols)
+
+            sql_header = "\n-- Index: {0}\n\n-- ".format(data['name'])
+            sql_header += render_template("/".join([self.index_template_path,
+                                                    'delete.sql']),
+                                          data=data, conn=self.conn)
+
+            index_sql = render_template("/".join([self.index_template_path,
+                                                  'create.sql']),
+                                        data=data, conn=self.conn)
+            index_sql += "\n"
+            index_sql += render_template("/".join([self.index_template_path,
+                                                   'alter.sql']),
+                                         data=data, conn=self.conn)
+
+            # Add into main sql
+            index_sql = re.sub('\n{2,}', '\n\n', index_sql)
+            main_sql.append(sql_header + '\n\n' + index_sql.strip('\n'))
+
+        """
+        ########################################
+        # 3) Reverse engineered sql for TRIGGERS
+        ########################################
+        """
+        SQL = render_template("/".join([self.trigger_template_path,
+                                        'nodes.sql']), tid=tid)
+        status, rset = self.conn.execute_2darray(SQL)
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        for row in rset['rows']:
+            trigger_sql = ''
+
+            SQL = render_template("/".join([self.trigger_template_path,
+                                            'properties.sql']),
+                                  tid=tid, trid=row['oid'],
+                                  datlastsysoid=self.datlastsysoid)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = dict(res['rows'][0])
+            # Adding parent into data dict, will be using it while creating sql
+            data['schema'] = schema
+            data['table'] = table
+
+            if data['tgnargs'] > 1:
+                # We know that trigger has more than 1 arguments, let's join them
+                data['tgargs'] = ', '.join(data['tgargs'])
+
+            if len(data['tgattr']) > 1:
+                columns = ', '.join(data['tgattr'].split(' '))
+
+                SQL = render_template("/".join([self.trigger_template_path,
+                                                'get_columns.sql']),
+                                      tid=tid, clist=columns)
+
+                status, rset = self.conn.execute_2darray(SQL)
+                if not status:
+                    return internal_server_error(errormsg=rset)
+                # 'tgattr' contains list of columns from table used in trigger
+                columns = []
+
+                for row in rset['rows']:
+                    columns.append({'column': row['name']})
+
+                data['columns'] = columns
+
+            data = trigger_definition(data)
+
+            sql_header = "\n-- Trigger: {0}\n\n-- ".format(data['name'])
+            sql_header += render_template("/".join([self.trigger_template_path,
+                                                    'delete.sql']),
+                                          data=data, conn=self.conn)
+
+            # If the request for new object which do not have did
+            trigger_sql = render_template("/".join([self.trigger_template_path,
+                                                    'create.sql']),
+                                          data=data, conn=self.conn)
+
+            trigger_sql = sql_header + '\n\n' + trigger_sql.strip('\n')
+
+            # If trigger is disabled then add sql code for the same
+            if not data['is_enable_trigger']:
+                trigger_sql += '\n\n'
+                trigger_sql += render_template("/".join([
+                    self.trigger_template_path,
+                    'enable_disable_trigger.sql']),
+                    data=data, conn=self.conn)
+
+            # Add into main sql
+            trigger_sql = re.sub('\n{2,}', '\n\n', trigger_sql)
+            main_sql.append(trigger_sql)
+
+        """
+        #####################################
+        # 4) Reverse engineered sql for RULES
+        #####################################
+        """
+
+        SQL = render_template("/".join(
+          [self.rules_template_path, 'properties.sql']), tid=tid)
+
+        status, rset = self.conn.execute_2darray(SQL)
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        for row in rset['rows']:
+            rules_sql = '\n'
+            SQL = render_template("/".join(
+                [self.rules_template_path, 'properties.sql']
+              ), rid=row['oid'], datlastsysoid=self.datlastsysoid)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            res_data = parse_rule_definition(res)
+            rules_sql += render_template("/".join(
+                [self.rules_template_path, 'create.sql']),
+                data=res_data, display_comments=True)
+
+            # Add into main sql
+            rules_sql = re.sub('\n{2,}', '\n\n', rules_sql)
+            main_sql.append(rules_sql)
+
+        sql = '\n'.join(main_sql)
+
+        return ajax_response(response=sql.strip('\n'))
+
+    @check_precondition
+    def select_sql(self, gid, sid, did, scid, tid):
+        """
+        SELECT script sql for the object
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+
+        Returns:
+            SELECT Script sql for the object
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              scid=scid, tid=tid,
+                              datlastsysoid=self.datlastsysoid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+        data = self._formatter(scid, tid, data)
+
+        columns = []
+
+        # Now we have all list of columns which we need
+        if 'columns' in data:
+            for c in data['columns']:
+                columns.append(self.qtIdent(self.conn, c['attname']))
+
+        if len(columns) > 0:
+            columns = ", ".join(columns)
+        else:
+            columns = '*'
+
+        sql = "SELECT {0}\n\tFROM {1};".format(
+            columns,
+            self.qtIdent(self.conn, data['schema'], data['name'])
+        )
+        return ajax_response(response=sql)
+
+    @check_precondition
+    def insert_sql(self, gid, sid, did, scid, tid):
+        """
+        INSERT script sql for the object
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+
+        Returns:
+            INSERT Script sql for the object
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              scid=scid, tid=tid,
+                              datlastsysoid=self.datlastsysoid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+        data = self._formatter(scid, tid, data)
+
+        columns = []
+        values = []
+
+        # Now we have all list of columns which we need
+        if 'columns' in data:
+            for c in data['columns']:
+                columns.append(self.qtIdent(self.conn, c['attname']))
+                values.append('?')
+
+        if len(columns) > 0:
+            columns = ", ".join(columns)
+            values = ", ".join(values)
+            sql = "INSERT INTO {0}(\n\t{1})\n\tVALUES ({2});".format(
+                self.qtIdent(self.conn, data['schema'], data['name']),
+                columns, values
+            )
+        else:
+            sql = gettext('-- Please create column(s) first...')
+
+        return ajax_response(response=sql)
+
+    @check_precondition
+    def update_sql(self, gid, sid, did, scid, tid):
+        """
+        UPDATE script sql for the object
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+
+        Returns:
+            UPDATE Script sql for the object
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              scid=scid, tid=tid,
+                              datlastsysoid=self.datlastsysoid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+        data = self._formatter(scid, tid, data)
+
+        columns = []
+
+        # Now we have all list of columns which we need
+        if 'columns' in data:
+            for c in data['columns']:
+                columns.append(self.qtIdent(self.conn, c['attname']))
+
+        if len(columns) > 0:
+            if len(columns) == 1:
+                columns = columns[0]
+                columns += "=?"
+            else:
+                columns = "=?, ".join(columns)
+
+            sql = "UPDATE {0}\n\tSET {1}\n\tWHERE <condition>;".format(
+                self.qtIdent(self.conn, data['schema'], data['name']),
+                columns
+            )
+        else:
+            sql = gettext('-- Please create column(s) first...')
+
+        return ajax_response(response=sql)
+
+    @check_precondition
+    def delete_sql(self, gid, sid, did, scid, tid):
+        """
+        DELETE script sql for the object
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+
+        Returns:
+            DELETE Script sql for the object
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              scid=scid, tid=tid,
+                              datlastsysoid=self.datlastsysoid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+
+        sql = "DELETE FROM {0}\n\tWHERE <condition>;".format(
+            self.qtIdent(self.conn, data['schema'], data['name'])
+        )
+
+        return ajax_response(response=sql)
+
+
+TableView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py
new file mode 100644
index 0000000..5bcb456
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py
@@ -0,0 +1,906 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Column Node """
+
+from flask import render_template, make_response, request, jsonify
+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.server_groups.servers.databases.schemas.utils \
+    import DataTypeReader
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+    parse_priv_to_db
+from functools import wraps
+import json
+
+
+class ColumnsModule(CollectionNodeModule):
+    """
+     class ColumnsModule(CollectionNodeModule)
+
+        A module class for Column node derived from CollectionNodeModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the Column and it's base module.
+
+    * get_nodes(gid, sid, did, scid, tid)
+      - Method is used to generate the browser collection node.
+
+    * node_inode()
+      - Method is overridden from its base class to make the node as leaf node.
+
+    * script_load()
+      - Load the module script for schema, when any of the server node is
+        initialized.
+    """
+
+    NODE_TYPE = 'column'
+    COLLECTION_LABEL = gettext("Columns")
+
+    def __init__(self, *args, **kwargs):
+        """
+        Method is used to initialize the ColumnModule and it's base module.
+
+        Args:
+            *args:
+            **kwargs:
+        """
+        self.min_ver = None
+        self.max_ver = None
+        super(ColumnsModule, self).__init__(*args, **kwargs)
+
+    def get_nodes(self, gid, sid, did, scid, **kwargs):
+        """
+        Generate the collection node
+        """
+        assert('tid' in kwargs or 'vid' in kwargs)
+        yield self.generate_browser_collection_node(
+            kwargs['tid'] if 'tid' in kwargs else kwargs['vid']
+        )
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for server, when any of the server-group node is
+        initialized.
+        """
+        return database.DatabaseModule.NODE_TYPE
+
+    @property
+    def node_inode(self):
+        """
+        Load the module node as a leaf node
+        """
+        return False
+
+
+blueprint = ColumnsModule(__name__)
+
+
+class ColumnsView(PGChildNodeView, DataTypeReader):
+    """
+    This class is responsible for generating routes for Column node
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the ColumnView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * list()
+      - This function is used to list all the Column nodes within that
+      collection.
+
+    * nodes()
+      - This function will used to create all the child node within that
+        collection, Here it will create all the Column node.
+
+    * properties(gid, sid, did, scid, tid, clid)
+      - This function will show the properties of the selected Column node
+
+    * create(gid, sid, did, scid, tid)
+      - This function will create the new Column object
+
+    * update(gid, sid, did, scid, tid, clid)
+      - This function will update the data for the selected Column node
+
+    * delete(self, gid, sid, scid, tid, clid):
+      - This function will drop the Column object
+
+    * msql(gid, sid, did, scid, tid, clid)
+      - This function is used to return modified SQL for the selected
+        Column node
+
+    * get_sql(data, scid, tid)
+      - This function will generate sql from model data
+
+    * sql(gid, sid, did, scid):
+      - This function will generate sql to show it in sql pane for the
+        selected Column node.
+
+    * dependency(gid, sid, did, scid):
+      - This function will generate dependency list show it in dependency
+        pane for the selected Column node.
+
+    * dependent(gid, sid, did, scid):
+      - This function will generate dependent list to show it in dependent
+        pane for the selected Column node.
+    """
+
+    node_type = blueprint.node_type
+
+    parent_ids = [
+            {'type': 'int', 'id': 'gid'},
+            {'type': 'int', 'id': 'sid'},
+            {'type': 'int', 'id': 'did'},
+            {'type': 'int', 'id': 'scid'},
+            {'type': 'int', 'id': 'tid'}
+            ]
+    ids = [
+            # Here we specify type as any because table
+            # are also has '-' in them if they are system table
+            {'type': 'string', 'id': 'clid'}
+            ]
+
+    operations = dict({
+        'obj': [
+            {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+            {'get': 'list', 'post': 'create'}
+        ],
+        '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'}],
+    })
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            driver = get_driver(PG_DEFAULT_DRIVER)
+            self.manager = driver.connection_manager(
+                kwargs['sid']
+            )
+            self.conn = self.manager.connection(did=kwargs['did'])
+            self.qtIdent = driver.qtIdent
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    gettext(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            ver = self.manager.version
+            # we will set template path for sql scripts
+            if ver >= 90200:
+                self.template_path = 'column/sql/9.2_plus'
+            else:
+                self.template_path = 'column/sql/9.1_plus'
+            # Allowed ACL for column 'Select/Update/Insert/References'
+            self.acl = ['a', 'r', 'w', 'x']
+
+            # We need parent's name eg table name and schema name
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=kwargs['tid'])
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            for row in rset['rows']:
+                self.schema = row['schema']
+                self.table = row['table']
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+    @check_precondition
+    def list(self, gid, sid, did, scid, tid):
+        """
+        This function is used to list all the schema nodes within that collection.
+
+        Args:
+            gid: Server group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+
+        Returns:
+            JSON of available column nodes
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']), tid=tid,
+                              show_sys_objects=self.blueprint.show_system_objects)
+        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, tid):
+        """
+        This function will used to create all the child node within that collection.
+        Here it will create all the schema node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+
+        Returns:
+            JSON of available schema child nodes
+        """
+        res = []
+        SQL = render_template("/".join([self.template_path,
+                                        'nodes.sql']), tid=tid,
+                              show_sys_objects=self.blueprint.show_system_objects)
+        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'],
+                    tid,
+                    row['name'],
+                    icon="icon-column",
+                    datatype=row['datatype']  # We need datatype somewhere in
+            ))                                # exclusion constraint.
+
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    def _formatter(self, scid, tid, clid, data):
+        """
+        Args:
+             scid: schema oid
+             tid: table oid
+             clid: position of column in table
+             data: dict of query result
+
+        Returns:
+            It will return formatted output of collections
+        """
+        # To check if column is primary key
+        if 'attnum' in data and 'indkey' in data:
+            # Current column
+            attnum = str(data['attnum'])
+
+            # Single/List of primary key column(s)
+            indkey = str(data['indkey'])
+
+            # We will check if column is in primary column(s)
+            if attnum in indkey.split(" "):
+                data['is_pk'] = True
+            else:
+                data['is_pk'] = False
+
+        # Find length & precision of column data type
+        fulltype = self.get_full_type(
+            data['typnspname'], data['typname'],
+            data['isdup'], data['attndims'], data['atttypmod']
+        )
+
+        import re
+        # If we have length & precision both
+        matchObj = re.search(r'(\d+),(\d+)', fulltype)
+        if matchObj:
+            data['attlen'] = matchObj.group(1)
+            data['attprecision'] = matchObj.group(2)
+        else:
+            # If we have length only
+            matchObj = re.search(r'(\d+)', fulltype)
+            if matchObj:
+                data['attlen'] = matchObj.group(1)
+                data['attprecision'] = None
+            else:
+                data['attlen'] = None
+                data['attprecision'] = None
+
+        # We need to fetch inherited tables for each table
+        SQL = render_template("/".join([self.template_path,
+                                        'get_inherited_tables.sql']),
+                              tid=tid)
+        status, inh_res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=inh_res)
+        for row in inh_res['rows']:
+            if row['attrname'] == data['name']:
+                data['is_inherited'] = True
+                data['tbls_inherited'] = row['inhrelname']
+
+        # We need to format variables according to client js collection
+        if 'attoptions' in data and data['attoptions'] is not None:
+            spcoptions = []
+            for spcoption in data['attoptions']:
+                k, v = spcoption.split('=')
+                spcoptions.append({'name': k, 'value': v})
+
+            data['attoptions'] = spcoptions
+
+        # Need to format security labels according to client js collection
+        if 'seclabels' in data and data['seclabels'] is not None:
+            seclabels = []
+            for seclbls in data['seclabels']:
+                k, v = seclbls.split('=')
+                seclabels.append({'provider': k, 'label': v})
+
+            data['seclabels'] = seclabels
+
+        # We need to parse & convert ACL coming from database to json format
+        SQL = render_template("/".join([self.template_path, 'acl.sql']),
+                              tid=tid, clid=clid)
+        status, acl = self.conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=acl)
+
+        # We will set get privileges from acl sql so we don't need
+        # it from properties sql
+        data['attacl'] = []
+
+        for row in acl['rows']:
+            priv = parse_priv_from_db(row)
+            data.setdefault(row['deftype'], []).append(priv)
+
+        # we are receiving request when in edit mode
+        # we will send filtered types related to current type
+        present_type = data['cltype']
+        type_id = data['atttypid']
+
+        SQL = render_template("/".join([self.template_path,
+                                        'is_referenced.sql']),
+                              tid=tid, clid=clid)
+
+        status, is_reference = self.conn.execute_scalar(SQL)
+
+        edit_types_list = list()
+        # We will need present type in edit mode
+        edit_types_list.append(present_type)
+
+        if int(is_reference) == 0:
+            SQL = render_template("/".join([self.template_path,
+                                            'edit_mode_types.sql']),
+                                  type_id=type_id)
+            status, rset = self.conn.execute_2darray(SQL)
+
+            for row in rset['rows']:
+                edit_types_list.append(row['typname'])
+        else:
+            edit_types_list.append(present_type)
+
+        data['edit_types'] = edit_types_list
+
+        # Manual Data type formatting
+        # If data type has () with them then we need to remove them
+        # eg bit(1) because we need to match the name with combobox
+        isArray = False
+        if data['cltype'].endswith('[]'):
+            isArray = True
+            data['cltype'] = data['cltype'].rstrip('[]')
+
+        idx = data['cltype'].find('(')
+        if idx and data['cltype'].endswith(')'):
+            data['cltype'] = data['cltype'][:idx]
+
+        if isArray:
+            data['cltype'] += "[]"
+
+        return data
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid, clid):
+        """
+        This function will show the properties of the selected schema node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did:  Database ID
+            scid: Schema ID
+            scid: Schema ID
+            tid: Table ID
+            clid: Column ID
+
+        Returns:
+            JSON of selected schema node
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']), tid=tid, clid=clid
+                              , show_sys_objects=self.blueprint.show_system_objects)
+
+        status, res = self.conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        # Making copy of output for future use
+        data = dict(res['rows'][0])
+        data = self._formatter(scid, tid, clid, data)
+
+        return ajax_response(
+                response=data,
+                status=200
+                )
+
+    def _cltype_formatter(self, type):
+        """
+
+        Args:
+            data: Type string
+
+        Returns:
+            We need to remove [] from type and append it
+            after length/precision so we will set flag for
+            sql template
+        """
+        if '[]' in type:
+            type = type.replace('[]', '')
+            self.hasSqrBracket = True
+        else:
+            self.hasSqrBracket = False
+
+        return type
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, tid):
+        """
+        This function will creates new the schema object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+        """
+        data = request.form if request.form else json.loads(
+            request.data.decode()
+        )
+
+        for k, v in data.items():
+            try:
+                data[k] = json.loads(v)
+            except (ValueError, TypeError):
+                data[k] = v
+
+        required_args = {
+            'name': 'Name',
+            'cltype': 'Type'
+        }
+
+        for arg in required_args:
+            if arg not in data:
+                return make_json_response(
+                    status=410,
+                    success=0,
+                    errormsg=gettext(
+                        "Couldn't find the required parameter (%s)." %
+                        required_args[arg]
+                    )
+                )
+
+        # Parse privilege data coming from client according to database format
+        if 'attacl' in data:
+            data['attacl'] = parse_priv_to_db(data['attacl'], self.acl)
+
+        # Adding parent into data dict, will be using it while creating sql
+        data['schema'] = self.schema
+        data['table'] = self.table
+
+        # check type for '[]' in it
+        data['cltype'] = self._cltype_formatter(data['cltype'])
+        data['hasSqrBracket'] = self.hasSqrBracket
+
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'create.sql']),
+                                  data=data, conn=self.conn)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # we need oid to to add object in tree at browser
+            SQL = render_template("/".join([self.template_path,
+                                            'get_position.sql']),
+                                  tid=tid, data=data)
+            status, clid = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=tid)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    clid,
+                    scid,
+                    data['name'],
+                    icon="icon-column"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid, clid):
+        """
+        This function will updates existing the schema object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           clid: Column ID
+        """
+        # We will first fetch the column name for current request
+        # so that we create template for dropping column
+        try:
+
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']), tid=tid, clid=clid
+                                  , show_sys_objects=self.blueprint.show_system_objects)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = dict(res['rows'][0])
+            # We will add table & schema as well
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            SQL = render_template("/".join([self.template_path,
+                                            'delete.sql']),
+                                  data=data, conn=self.conn)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=gettext("Column is dropped"),
+                data={
+                    'id': clid,
+                    'tid': tid
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid, clid):
+        """
+        This function will updates existing the schema object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           clid: Column ID
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        # Adding parent into data dict, will be using it while creating sql
+        data['schema'] = self.schema
+        data['table'] = self.table
+
+        # check type for '[]' in it
+        if 'cltype' in data:
+            data['cltype'] = self._cltype_formatter(data['cltype'])
+            data['hasSqrBracket'] = self.hasSqrBracket
+
+        try:
+            SQL = self.get_sql(scid, tid, clid, data)
+            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="Column updated",
+                    data={
+                        'id': clid,
+                        'tid': tid,
+                        'scid': scid
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': clid,
+                        'tid': tid,
+                        'scid': scid
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid, clid=None):
+        """
+        This function will generates modified sql for schema object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           clid: Column ID (When working with existing column)
+        """
+        data = dict()
+        for k, v in request.args.items():
+            try:
+                data[k] = json.loads(v)
+            except ValueError:
+                data[k] = v
+
+        # Adding parent into data dict, will be using it while creating sql
+        data['schema'] = self.schema
+        data['table'] = self.table
+
+        # check type for '[]' in it
+        if 'cltype' in data:
+            data['cltype'] = self._cltype_formatter(data['cltype'])
+            data['hasSqrBracket'] = self.hasSqrBracket
+
+        try:
+            SQL = self.get_sql(scid, tid, clid, data)
+
+            if SQL and SQL.strip('\n') and SQL.strip(' '):
+                return make_json_response(
+                        data=SQL,
+                        status=200
+                        )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def get_sql(self, scid, tid, clid, data):
+        """
+        This function will genrate sql from model data
+        """
+        if clid is not None:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']), tid=tid, clid=clid
+                                  , show_sys_objects=self.blueprint.show_system_objects)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            old_data = dict(res['rows'][0])
+            # We will add table & schema as well
+            old_data = self._formatter(scid, tid, clid, old_data)
+
+            # If name is not present in data then
+            # we will fetch it from old data, we also need schema & table name
+            if 'name' not in data:
+                data['name'] = old_data['name']
+
+            # Convert acl coming from client in db parsing format
+            key = 'attacl'
+            if key in data and data[key] is not None:
+                if 'added' in data[key]:
+                    data[key]['added'] = parse_priv_to_db(
+                      data[key]['added'], self.acl
+                    )
+                if 'changed' in data[key]:
+                    data[key]['changed'] = parse_priv_to_db(
+                      data[key]['changed'], self.acl
+                    )
+                if 'deleted' in data[key]:
+                    data[key]['deleted'] = parse_priv_to_db(
+                      data[key]['deleted'], self.acl
+                    )
+
+            SQL = render_template(
+                "/".join([self.template_path, 'update.sql']),
+                data=data, o_data=old_data, conn=self.conn
+                )
+        else:
+            required_args = [
+                'name',
+                'cltype'
+            ]
+
+            for arg in required_args:
+                if arg not in data:
+                    return gettext('-- incomplete definition')
+
+            # We will convert privileges coming from client required
+            # in server side format
+            if 'attacl' in data:
+                data['attacl'] = parse_priv_to_db(data['attacl'],
+                                                  self.acl)
+            # If the request for new object which do not have did
+            SQL = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=data, conn=self.conn)
+        return SQL
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, tid, clid):
+        """
+        This function will generates reverse engineered sql for schema object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           clid: Column ID
+        """
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']), tid=tid, clid=clid
+                                  , show_sys_objects=self.blueprint.show_system_objects)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = dict(res['rows'][0])
+            # We do not want to display length as -1 in create query
+            if 'attlen' in data and data['attlen'] == -1:
+                data['attlen'] = ''
+            # Adding parent into data dict, will be using it while creating sql
+            data['schema'] = self.schema
+            data['table'] = self.table
+            # check type for '[]' in it
+            if 'cltype' in data:
+                data['cltype'] = self._cltype_formatter(data['cltype'])
+                data['hasSqrBracket'] = self.hasSqrBracket
+
+            # We will add table & schema as well
+            data = self._formatter(scid, tid, clid, data)
+
+            SQL = self.get_sql(scid, tid, None, data)
+
+            sql_header = "-- Column: {0}\n\n-- ".format(self.qtIdent(self.conn,
+                                                                     data['schema'],
+                                                                     data['table'],
+                                                                     data['name']))
+            sql_header += render_template("/".join([self.template_path,
+                                                    'delete.sql']),
+                                          data=data, conn=self.conn)
+            SQL = sql_header + '\n\n' + SQL
+
+            return SQL
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, tid, clid):
+        """
+        This function get the dependents and return ajax response
+        for the column node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+            clid: Column ID
+        """
+        # Specific condition for column which we need to append
+        where = "WHERE dep.refobjid={0}::OID AND dep.refobjsubid={1}".format(
+            tid, clid
+        )
+
+        dependents_result = self.get_dependents(
+            self.conn, clid, where=where
+        )
+
+        # Specific sql to run againt column to fetch dependents
+        SQL = render_template("/".join([self.template_path,
+                                        'depend.sql']), where=where)
+
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        for row in res['rows']:
+            ref_name = row['refname']
+            if ref_name is None:
+                continue
+
+            dep_type = ''
+            dep_str = row['deptype']
+            if dep_str == 'a':
+                dep_type = 'auto'
+            elif dep_str == 'n':
+                dep_type = 'normal'
+            elif dep_str == 'i':
+                dep_type = 'internal'
+
+            dependents_result.append({'type': 'sequence', 'name': ref_name, 'field': dep_type})
+
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, tid, clid):
+        """
+        This function get the dependencies and return ajax response
+        for the column node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+            clid: Column ID
+
+        """
+        # Specific condition for column which we need to append
+        dependencies_result = self.get_dependencies(
+            self.conn, clid
+        )
+
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+
+ColumnsView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/img/coll-column.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/img/coll-column.png
new file mode 100644
index 0000000000000000000000000000000000000000..89d758834d4176c1df2548db10b46b1f6b2e4ec5
GIT binary patch
literal 400
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}cz{ocE09*4`h3Ev&s(m&yL<QU
z)2C0LJ$u%^|9SG<<M-~}d-(9-vSrKOym_;3-MW`AU#?uaa_!o+>({S;_3G90=g%KJ
zc<}o5>kl73eEaro$BrEvHf-3qapU*z-`~D{TatgM6KFJJNswPKgTu2MX+REVfk$L9
zkoEv$x0Bg+Kt_S5i(`ny<=FG?VhsvBt`}W4E@ZQg__p6qm@nby;qrG1j0_I@c^(;r
zuhP)2X<D_P?dti2$vzyv(k(V*o?f?H<i?gXiRA)mhTUpwofQn*P8`|LWw=McNaXQ!
zy+q@@Yd0m1ylF3f{(An_-EV$!P4L^#_`09vh+=cW8=&2)C9V-ADTyViR>?)FK#IZ0
zz|cU~&`8(7FvQ5f%EZ{p#6;V`)XKoXVy3DbiiX_$l+3hBhz0{oum+H7D+4o#hEvl+
R*8nvzc)I$ztaD0e0s#BYr!@co

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/img/column.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/img/column.png
new file mode 100644
index 0000000000000000000000000000000000000000..bd9f81df98fe27d81ade5144d66b3b09b96123c1
GIT binary patch
literal 435
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE09*4`h3Ev&s(m&yL<QU
zy?giW-@pI#>C<P=p0)3PzHHgD=g*(Nc=6)Rn>Xv$t$X?M<;s;SSFBjEcJ12r>({?}
z_3FWc2M-@UeDvti>eZ{)tXZ>Z)20s}KD>VY`qQURpFe;8`t|F#Z{K$8*s)>5hK(CH
ze*gac?c2BS-o49eKe!ZVF=I)PUoeBivm0qZ4rhT!WHFHT0Ash4*>*risi%u$h{WaE
z^B0Ah6a-om4Rm-iuV3+X<o2rm?|w$}?!!eN_y0*dcT_ORb6x7A={6cu<^6X}kY2Ib
zZ0TMHrih1I^CUh^d%@<y)3TvOv&K6iH~-RCTdVU6czMe9*h~1l{VS1_(D3~9P6_q4
zgXi+))!6E^|9xfrB*GzI-TA)^=m6Cc*NBpo#FA92<f2p{#b9J$XrOCoq-$UpVq{=t
zVr*q%qHSPmWnf@2Q&kQ{LvDUbW?CgggMlSj14y-%ff+=@sp+9>fEpM)UHx3vIVCg!
E0NRqnZ~y=R

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/js/column.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/js/column.js
new file mode 100644
index 0000000..5f8edc6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/js/column.js
@@ -0,0 +1,586 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser',
+        'backform', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
+
+  if (!pgBrowser.Nodes['coll-column']) {
+    var databases = pgAdmin.Browser.Nodes['coll-column'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'column',
+        label: '{{ _('Columns') }}',
+        type: 'coll-column',
+        sqlAlterHelp: 'sql-altertable.html',
+        sqlCreateHelp: 'sql-altertable.html',
+        columns: ['name', 'atttypid', 'description']
+      });
+  };
+
+  // Switch Cell for Primary Key selection
+  var SwitchDepCell = Backgrid.BooleanCell.extend({
+      initialize: function() {
+        Backgrid.BooleanCell.prototype.initialize.apply(this, arguments);
+        Backgrid.Extension.DependentCell.prototype.initialize.apply(this, arguments);
+      },
+      dependentChanged: function () {
+        var model = this.model,
+          column = this.column,
+          editable = this.column.get("editable"),
+          input = this.$el.find('input[type=checkbox]').first();
+
+        is_editable = _.isFunction(editable) ? !!editable.apply(column, [model]) : !!editable;
+        if (is_editable) {
+           this.$el.addClass("editable");
+           input.prop('disabled', false);
+         } else {
+           this.$el.removeClass("editable");
+           input.prop('disabled', true);
+         }
+
+        this.delegateEvents();
+        return this;
+      },
+      remove: Backgrid.Extension.DependentCell.prototype.remove
+    });
+
+   // This Node model will be used for variable control for column
+   var VariablesModel = Backform.VariablesModel = pgAdmin.Browser.Node.Model.extend({
+    defaults: {
+      name: null,
+      value: null
+    },
+    schema: [{
+      id: 'name', label: '{{ _('Name') }}', cell: 'select2',
+      type: 'text', disabled: false, node: 'column',
+      options: [['n_distinct', 'n_distinct'],
+      ['n_distinct_inherited','n_distinct_inherited']],
+      select2: {placeholder: "Select variable"},
+      cellHeaderClasses:'width_percent_50'
+    },{
+      id: 'value', label: '{{ _('Value') }}',
+      type: 'text', disabled: false,
+      cellHeaderClasses:'width_percent_50'
+    }],
+    validate: function() {
+      var err = {},
+          errmsg = null;
+
+      if (_.isUndefined(this.get('value')) ||
+        _.isNull(this.get('value')) ||
+        String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
+            errmsg =  '{{ _('Please provide input for variable.')}}';
+            this.errorModel.set('value', errmsg);
+            return errmsg;
+          } else {
+            this.errorModel.unset('value');
+          }
+      return null;
+    }
+  });
+
+  if (!pgBrowser.Nodes['column']) {
+    pgAdmin.Browser.Nodes['column'] = pgAdmin.Browser.Node.extend({
+      parent_type: ['table', 'view', 'mview'],
+      collection_type: ['coll-table', 'coll-view', 'coll-mview'],
+      type: 'column',
+      label: '{{ _('Column') }}',
+      hasSQL:  true,
+      canDrop: function(itemData, item, data){
+        if (pgBrowser.Nodes['schema'].canChildDrop.apply(this, [itemData, item, data])) {
+          var t = pgBrowser.tree, i = item, d = itemData, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+
+          // Check if menu is allowed ?
+          if(_.indexOf(parents, 'catalog') > -1 ||
+             _.indexOf(parents, 'view') > -1 ||
+             _.indexOf(parents, 'mview') > -1) {
+            return false;
+          } else if(_.indexOf(parents, 'table') > -1) {
+            return true;
+          }
+        } else {
+          return false;
+        }
+      },
+      hasDepends: true,
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_column_on_coll', node: 'coll-column', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Column...') }}',
+          icon: 'wcTabIcon icon-column', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_column', node: 'column', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Column...') }}',
+          icon: 'wcTabIcon icon-column', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_column_onTable', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Column...') }}',
+          icon: 'wcTabIcon icon-column', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_column_onView', node: 'view', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Column...') }}',
+          icon: 'wcTabIcon icon-column', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }
+        ]);
+      },
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          attowner: undefined,
+          atttypid: undefined,
+          attnum: undefined,
+          cltype: undefined,
+          collspcname: undefined,
+          attacl: undefined,
+          description: undefined,
+          parent_tbl: undefined,
+          min_val: undefined,
+          max_val: undefined,
+          edit_types: undefined,
+          is_primary_key: false,
+          inheritedfrom: undefined,
+          attstattarget:undefined
+        },
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', cell: 'string',
+          type: 'text', disabled: 'inSchemaWithColumnCheck',
+          cellHeaderClasses:'width_percent_30',
+          editable: 'editable_check_for_table'
+        },{
+          // Need to show this field only when creating new table [in SubNode control]
+          id: 'is_primary_key', label: '{{ _('Is primary key?') }}', cell: SwitchDepCell,
+          type: 'switch', deps:['name'],
+          options: {
+            onText: 'Yes', offText: 'No', onColor: 'success',
+            offColor: 'primary', size: 'small'},
+          cellHeaderClasses:'width_percent_5',
+          visible: function(m) {
+            return _.isUndefined(m.top.node_info['table'] || m.top.node_info['view'] || m.top.node_info['mview']);
+          },
+          disabled: function(m){
+            // If primary key already exist then disable.
+            if (m.top && !_.isUndefined(m.top.get('oid')) &&
+                        m.top.get('primary_key').length > 0 &&
+                        !_.isUndefined(m.top.get('primary_key').first().get('oid'))) {
+              return true;
+            }
+
+            var name = m.get('name');
+
+            if(!m.inSchemaWithColumnCheck.apply(this, [m]) &&
+            (_.isUndefined(name)  || _.isNull(name) || name == '')) {
+              return true;
+            }
+            return false;
+          },
+          editable: function(m){
+            var name = m.get('name');
+            // If HeaderCell then allow True
+            if(m instanceof Backbone.Collection) {
+              return true;
+            }
+            // If primary key already exist then disable.
+            if (m.top && !_.isUndefined(m.top.get('oid')) &&
+                      m.top.get('primary_key').length > 0 &&
+                      !_.isUndefined(m.top.get('primary_key').first().get('oid'))) {
+
+              return false;
+            }
+
+            if(!m.inSchemaWithColumnCheck.apply(this, [m]) &&
+              !_.isUndefined(name) && !_.isNull(name) && name !== '') {
+              return true;
+            }
+            return false;
+            // Set to false if no condition is met
+            m.set('is_primary_key', false);
+          }
+        },{
+          id: 'attnum', label:'{{ _('Position') }}', cell: 'string',
+          type: 'text', disabled: 'inSchema', mode: ['properties']
+        },{
+          id: 'cltype', label:'{{ _('Data type') }}', cell: 'node-ajax-options',
+          type: 'text', disabled: 'inSchemaWithColumnCheck',
+          control: 'node-ajax-options', url: 'get_types', node: 'table',
+          cellHeaderClasses:'width_percent_30', first_empty: true,
+          select2: { allowClear: false }, group: '{{ _('Definition') }}',
+          transform: function(data, cell) {
+            /* 'transform' function will be called by control, and cell both.
+             * The way, we use the transform in cell, and control is different.
+             * Because - options are shared using 'column' object in backgrid,
+             * hence - the cell is passed as second parameter, while the control
+             * uses (this) as a object.
+             */
+            var control = cell || this,
+                m = control.model;
+
+            /* We need different data in create mode & in edit mode
+             * if we are in create mode then return data as it is
+             * if we are in edit mode then we need to filter data
+             */
+            control.model.datatypes = data;
+            var edit_types = m.get('edit_types'),
+                result = [];
+
+            // If called from Table, We will check if in edit mode
+            // then send edit_types only
+            if( !_.isUndefined(m.top) && !m.top.isNew() ) {
+              _.each(data, function(t) {
+                if (_.indexOf(edit_types, t.value) != -1) {
+                  result.push(t);
+                }
+            });
+              // There may be case that user adds new column in  existing collection
+              // we will not have edit types then
+              return result.length > 0 ? result : data;
+            }
+
+            // If called from Column
+            if(m.isNew()) {
+              return data;
+            } else {
+              //edit mode
+              _.each(data, function(t) {
+                if (_.indexOf(edit_types, t.value) != -1) {
+                  result.push(t);
+                }
+              });
+
+              return result;
+            }
+          },
+          editable: 'editable_check_for_table'
+        },{
+          // Need to show this field only when creating new table [in SubNode control]
+          id: 'inheritedfrom', label: '{{ _('Inherited from table') }}',
+          type: 'text', disabled: true, editable: false,
+          cellHeaderClasses:'width_percent_30',
+          visible: function(m) {
+            return _.isUndefined(m.top.node_info['table'] || m.top.node_info['view'] || m.top.node_info['mview']);
+          }
+        },{
+          id: 'attlen', label:'{{ _('Length') }}', cell: 'string',
+           deps: ['cltype'], type: 'int', group: '{{ _('Definition') }}',
+           disabled: function(m) {
+             var of_type = m.get('cltype'),
+               flag = true;
+              _.each(m.datatypes, function(o) {
+                if ( of_type == o.value ) {
+                    if(o.length)
+                    {
+                      m.set('min_val', o.min_val, {silent: true});
+                      m.set('max_val', o.max_val, {silent: true});
+                      flag = false;
+                    }
+                }
+              });
+
+              flag && setTimeout(function() {
+                  m.set('attlen', null);
+                },10);
+
+              return flag;
+           }
+        },{
+          id: 'attprecision', label:'{{ _('Precision') }}', cell: 'string',
+           deps: ['cltype'], type: 'int', group: '{{ _('Definition') }}',
+           disabled: function(m) {
+             var of_type = m.get('cltype'),
+               flag = true;
+              _.each(m.datatypes, function(o) {
+                if ( of_type == o.value ) {
+                    if(o.precision)
+                    {
+                      m.set('min_val', o.min_val, {silent: true});
+                      m.set('max_val', o.max_val, {silent: true});
+                      flag = false;
+                    }
+                }
+              });
+
+              flag && setTimeout(function() {
+                  m.set('attprecision', null);
+                },10);
+              return flag;
+           }
+         },{
+          id: 'collspcname', label:'{{ _('Collation') }}', cell: 'string',
+          type: 'text', control: 'node-ajax-options', url: 'get_collations',
+          group: '{{ _('Definition') }}', node: 'collation',
+          deps: ['cltype'], disabled: function(m) {
+             var of_type = m.get('cltype'),
+               flag = true;
+              _.each(m.datatypes, function(o) {
+                if ( of_type == o.value ) {
+                    if(o.is_collatable)
+                    {
+                      flag = false;
+                    }
+                }
+              });
+              if (flag) {
+                setTimeout(function(){
+                  m.set('collspcname', "");
+                }, 10);
+              }
+              return flag;
+          }
+        },{
+          id: 'defval', label:'{{ _('Default Value') }}', cell: 'string',
+          type: 'text', disabled: 'inSchemaWithColumnCheck',
+          group: '{{ _('Definition') }}'
+        },{
+          id: 'attnotnull', label:'{{ _('Not NULL?') }}', cell: 'string',
+          type: 'switch', disabled: 'inSchemaWithColumnCheck',
+          group: '{{ _('Definition') }}'
+        },{
+          id: 'attstattarget', label:'{{ _('Statistics') }}', cell: 'string',
+          type: 'text', disabled: 'inSchemaWithColumnCheck', mode: ['properties', 'edit'],
+          group: '{{ _('Definition') }}'
+        },{
+          id: 'attstorage', label:'{{ _('Storage') }}', group: '{{ _('Definition') }}',
+          type: 'text', mode: ['properties', 'edit'],
+          cell: 'string', disabled: 'inSchemaWithColumnCheck', first_empty: true,
+          control: 'select2', select2: { placeholder: "Select storage",
+            allowClear: false,
+            width: "100%"
+          },
+          options: [
+            {label: "PLAIN", value: "p"},
+            {label: "MAIN", value: "m"},
+            {label: "EXTERNAL", value: "e"},
+            {label: "EXTENDED", value: "x"},
+           ]
+        },{
+          id: 'is_pk', label:'{{ _('Primary key?') }}',
+          type: 'switch', disabled: true, mode: ['properties']
+        },{
+          id: 'is_fk', label:'{{ _('Foreign key?') }}',
+          type: 'switch', disabled: true, mode: ['properties']
+        },{
+          id: 'is_inherited', label:'{{ _('Inherited?') }}',
+          type: 'switch', disabled: true, mode: ['properties']
+        },{
+          id: 'tbls_inherited', label:'{{ _('Inherited from table(s)') }}',
+          type: 'text', disabled: true, mode: ['properties'], deps: ['is_inherited'],
+          visible: function(m) {
+              if (!_.isUndefined(m.get('is_inherited')) && m.get('is_inherited')) {
+                return true;
+              } else {
+                return false;
+              }
+          }
+        },{
+          id: 'is_sys_column', label:'{{ _('System Column?') }}', cell: 'string',
+          type: 'switch', disabled: true, mode: ['properties']
+        },{
+          id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema'
+        },{
+          id: 'attacl', label: 'Privileges', type: 'collection',
+          group: '{{ _('Security') }}', control: 'unique-col-collection',
+          model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({
+          privileges: ['a','r','w','x']}),
+          mode: ['edit'], canAdd: true, canDelete: true,
+          uniqueCol : ['grantee']
+        },{
+          id: 'attoptions', label: 'Variables', type: 'collection',
+          group: '{{ _('Security') }}', control: 'unique-col-collection',
+          model: VariablesModel, uniqueCol : ['name'],
+          mode: ['edit', 'create'], canAdd: true, canEdit: false,
+          canDelete: true
+        },{
+          id: 'seclabels', label: '{{ _('Security Labels') }}',
+          model: pgAdmin.Browser.SecurityModel,
+          editable: false, type: 'collection',
+          group: '{{ _('Security') }}', mode: ['edit', 'create'],
+          min_version: 90100, canAdd: true,
+          canEdit: false, canDelete: true, control: 'unique-col-collection'
+        }
+        ],
+        validate: function(keys) {
+          var err = {},
+              changedAttrs = this.changed,
+              msg = undefined;
+
+          // Nothing to validate
+          if (keys && keys.length == 0) {
+            this.errorModel.clear();
+            return null;
+          } else {
+            this.errorModel.clear();
+          }
+
+          if (_.isUndefined(this.get('name'))
+              || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Column name can not be empty.') }}';
+            this.errorModel.set('name', msg);
+            return msg;
+          }
+
+          if (_.isUndefined(this.get('cltype'))
+              || String(this.get('cltype')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Column type can not be empty.') }}';
+            this.errorModel.set('cltype', msg);
+            return msg;
+          }
+
+          if (!_.isUndefined(this.get('cltype'))
+                && !_.isUndefined(this.get('attlen'))
+                && !_.isNull(this.get('attlen'))
+                && this.get('attlen') !== '') {
+            // Validation for Length field
+            if (this.get('attlen') < this.get('min_val'))
+              msg = '{{ _('Length should not be less than: ') }}' + this.get('min_val');
+            if (this.get('attlen') > this.get('max_val'))
+              msg = '{{ _('Length should not be greater than: ') }}' + this.get('max_val');
+            // If we have any error set then throw it to user
+            if(msg) {
+              this.errorModel.set('attlen', msg)
+              return msg;
+            }
+          }
+
+          if (!_.isUndefined(this.get('cltype'))
+                && !_.isUndefined(this.get('attprecision'))
+                && !_.isNull(this.get('attprecision'))
+                && this.get('attprecision') !== '') {
+            // Validation for precision field
+            if (this.get('attprecision') < this.get('min_val'))
+              msg = '{{ _('Precision should not be less than: ') }}' + this.get('min_val');
+            if (this.get('attprecision') > this.get('max_val'))
+              msg = '{{ _('Precision should not be greater than: ') }}' + this.get('max_val');
+            // If we have any error set then throw it to user
+            if(msg) {
+              this.errorModel.set('attprecision', msg)
+              return msg;
+            }
+          }
+
+          return null;
+        },
+        isInhertedColumn: function() {
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchema: function() {
+          if(this.node_info &&  'catalog' in this.node_info)
+          {
+            return true;
+          }
+          return false;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchemaWithModelCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info)
+          {
+            // We will disable control if it's in 'edit' mode
+            if (m.isNew()) {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          return true;
+        },
+        // Checks weather to enable/disable control
+        inSchemaWithColumnCheck: function(m) {
+          var node_info = this.node_info || m.node_info || m.top.node_info;
+
+          // disable all fields if column is listed under view or mview
+          if ('view' in node_info || 'mview' in node_info) {
+            if (this && _.has(this, 'name') && (this.name != 'defval')) {
+              return true;
+            }
+          }
+
+          if(node_info &&  'schema' in node_info)
+          {
+            // We will disable control if it's system columns
+            // inheritedfrom check is useful when we use this schema in table node
+            // inheritedfrom has value then we should disable it
+            if(!_.isUndefined(m.get('inheritedfrom'))) {
+                return true;
+            }
+            // ie: it's position is less then 1
+            if (m.isNew()) {
+              return false;
+            }
+            // if we are in edit mode
+            if (!_.isUndefined(m.get('attnum')) && m.get('attnum') > 0 ) {
+              return false;
+            } else {
+              return true;
+            }
+           }
+          return true;
+        },
+        editable_check_for_table: function(arg) {
+            if (arg instanceof Backbone.Collection) {
+                return !arg.model.prototype.inSchemaWithColumnCheck.apply(
+                         this, [arg.top]
+                       );
+            } else {
+                return !arg.inSchemaWithColumnCheck.apply(
+                         this, [arg]
+                       );
+            }
+        }
+      }),
+      // Below function will enable right click menu for creating column
+      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, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to create table
+            if (_.indexOf(['schema'], d._type) > -1) {
+              return true;
+            }
+            else if (_.indexOf(['view', 'coll-view',
+                                'mview',
+                                'coll-mview'], d._type) > -1) {
+              parents.push(d._type);
+              break;
+            }
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1 ||
+              _.indexOf(parents, 'coll-view') > -1 ||
+              _.indexOf(parents, 'coll-mview') > -1 ||
+              _.indexOf(parents, 'mview') > -1 ||
+              _.indexOf(parents, 'view') > -1) {
+            return false;
+          } else {
+            return true;
+          }
+      }
+  });
+ }
+
+  return pgBrowser.Nodes['column'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/macros/privilege.macros b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/macros/privilege.macros
new file mode 100644
index 0000000..7eafd60
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/macros/privilege.macros
@@ -0,0 +1,13 @@
+{% macro APPLY(conn, schema_name, table_object, column_object, role, privs, with_grant_privs) -%}
+{% if privs %}
+GRANT {% for p in privs %}{% if loop.index != 1 %}, {% endif %}{{p}}({{conn|qtIdent(column_object)}}){% endfor %}
+ ON {{ conn|qtIdent(schema_name, table_object) }} TO {{ conn|qtIdent(role) }};
+{% endif %}
+{% if with_grant_privs %}
+GRANT {% for p in with_grant_privs %}{% if loop.index != 1 %}, {% endif %}{{p}}({{conn|qtIdent(column_object)}}){% endfor %}
+ ON {{ conn|qtIdent(schema_name, table_object) }} TO {{ conn|qtIdent(role) }} WITH GRANT OPTION;
+{% endif %}
+{%- endmacro %}
+{% macro RESETALL(conn, schema_name, table_object, column_object, role) -%}
+REVOKE ALL({{ conn|qtIdent(column_object) }}) ON {{ conn|qtIdent(schema_name, table_object) }} FROM {{ conn|qtIdent(role) }};
+{%- endmacro %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/macros/security.macros b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/macros/security.macros
new file mode 100644
index 0000000..39587c3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/templates/column/macros/security.macros
@@ -0,0 +1,6 @@
+{% macro APPLY(conn, type, schema_name, parent_object, child_object, provider, label) -%}
+SECURITY LABEL FOR {{ conn|qtIdent(provider) }} ON {{ type }} {{ conn|qtIdent(schema_name, parent_object, child_object) }} IS {{ label|qtLiteral }};
+{%- endmacro %}
+{% macro DROP(conn, type, schema_name, parent_object, child_object, provider) -%}
+SECURITY LABEL FOR {{ conn|qtIdent(provider) }} ON {{ type }} {{ conn|qtIdent(schema_name, parent_object, child_object) }} IS NULL;
+{%- endmacro %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/__init__.py
new file mode 100644
index 0000000..475c71f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/__init__.py
@@ -0,0 +1,133 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements Constraint Node"""
+
+from flask.ext.babel import gettext
+from flask import render_template, make_response
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.utils.ajax import make_json_response, \
+     make_response as ajax_response
+from .type import ConstraintRegistry
+
+
+class ConstraintsModule(CollectionNodeModule):
+    """
+    class ConstraintsModule(CollectionNodeModule)
+
+        A module class for Constraint node derived from CollectionNodeModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the ConstraintsModule and it's base module.
+
+    * get_nodes(gid, sid, did)
+      - Method is used to generate the browser collection node.
+
+    * node_inode()
+      - Method is overridden from its base class to make the node as leaf node.
+
+    * script_load()
+      - Load the module script for constraint node, when any of the database node is
+        initialized.
+    """
+
+    NODE_TYPE = 'constraints'
+    COLLECTION_LABEL = gettext("Constraints")
+
+    def __init__(self, *args, **kwargs):
+        self.min_ver = None
+        self.max_ver = None
+        super(ConstraintsModule, self).__init__(*args, **kwargs)
+
+    def get_nodes(self, gid, sid, did, scid, tid):
+        """
+        Generate the collection node
+        """
+        yield self.generate_browser_collection_node(tid)
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for constraints, when any of the table node is
+        initialized.
+        """
+        return database.DatabaseModule.NODE_TYPE
+
+blueprint = ConstraintsModule(__name__)
+
+
[email-masked]('/nodes/<int:gid>/<int:sid>/<int:did>/<int:scid>/<int:tid>/')
+def nodes(**kwargs):
+    """
+    Returns all constraint as a tree node.
+
+    Args:
+      **kwargs:
+
+    Returns:
+
+    """
+
+    cmd = {"cmd": "nodes"}
+    res = []
+    for name in ConstraintRegistry.registry:
+        module = (ConstraintRegistry.registry[name])['nodeview']
+        view = module(**cmd)
+        res = res + view.get_nodes(**kwargs)
+
+    return make_json_response(
+                data=res,
+                status=200
+                )
+
+
[email-masked]('/obj/<int:gid>/<int:sid>/<int:did>/<int:scid>/<int:tid>/')
+def proplist(**kwargs):
+    """
+    Returns all constraint with properties.
+    Args:
+      **kwargs:
+
+    Returns:
+
+    """
+
+    cmd = {"cmd": "obj"}
+    res = []
+    for name in ConstraintRegistry.registry:
+        module = (ConstraintRegistry.registry[name])['nodeview']
+        view = module(**cmd)
+        res = res + view.get_node_list(**kwargs)
+
+    return ajax_response(
+                response=res,
+                status=200
+                )
+
+
[email-masked]('/module.js')
+def module_js():
+    """
+      This property defines whether javascript exists for this node.
+
+    """
+    return make_response(
+            render_template(
+                "constraints/js/constraints.js",
+                _=gettext,
+                constraints=[
+                  (ConstraintRegistry.registry[n])['blueprint'].NODE_TYPE \
+                  for n in ConstraintRegistry.registry
+                  ]
+                ),
+            200, {'Content-Type': 'application/x-javascript'}
+            )
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/__init__.py
new file mode 100644
index 0000000..f2eb81b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/__init__.py
@@ -0,0 +1,833 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements the Check Constraint Module."""
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext as _
+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 as database
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.constraints.type \
+    import ConstraintRegistry, ConstraintTypeModule
+
+
+class CheckConstraintModule(CollectionNodeModule):
+    """
+    class CheckConstraintModule(CollectionNodeModule):
+
+        This class represents The Check Constraint Module.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Initialize the Check Constraint Module.
+
+    * get_nodes(gid, sid, did, scid)
+      - Generate the Check Constraint collection node.
+
+    * node_inode(gid, sid, did, scid)
+      - Returns Check Constraint node as leaf node.
+
+    * script_load()
+      - Load the module script for the Check Constraint, when any of the
+        Check node is initialized.
+    """
+    NODE_TYPE = 'check_constraints'
+    COLLECTION_LABEL = _("Check Constraints")
+
+    def __init__(self, *args, **kwargs):
+        super(CheckConstraintModule, self).__init__(*args, **kwargs)
+        self.min_ver = None
+        self.max_ver = None
+
+    def get_nodes(self, gid, sid, did, scid, doid):
+        """
+        Generate the Check Constraint collection node.
+        """
+        yield self.generate_browser_collection_node(doid)
+
+    @property
+    def node_inode(self):
+        """
+        Returns Check Constraint node as leaf node.
+        """
+        return False
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for the Check Constraint, when any of the
+        Check node is initialized.
+        """
+        return database.DatabaseModule.NODE_TYPE
+
+    @property
+    def csssnippets(self):
+        """
+        Returns a snippet of css to include in the page
+        """
+        return [
+                render_template(
+                    "check_constraint/css/check_constraint.css",
+                    node_type=self.node_type
+                    )
+                ]
+
+
+blueprint = CheckConstraintModule(__name__)
+
+
+class CheckConstraintView(PGChildNodeView):
+    """
+    class CheckConstraintView(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 Check Constraint.
+
+    Methods:
+    -------
+
+    * module_js():
+      - Load JS file (check-constraints.js) for this module.
+
+    * check_precondition(f):
+      - Works as a decorator.
+      -  Checks database connection status.
+      -  Attach connection object and template path.
+
+    * list(gid, sid, did, scid, doid):
+      - List the Check Constraints.
+
+    * nodes(gid, sid, did, scid):
+      - Returns all the Check Constraints to generate Nodes in the browser.
+
+    * properties(gid, sid, did, scid, doid):
+      - Returns the Check Constraint properties.
+
+    * create(gid, sid, did, scid):
+      - Creates a new Check Constraint object.
+
+    * update(gid, sid, did, scid, doid):
+      - Updates the Check Constraint object.
+
+    * delete(gid, sid, did, scid, doid):
+      - Drops the Check Constraint object.
+
+    * sql(gid, sid, did, scid, doid=None):
+      - Returns the SQL for the Check Constraint object.
+
+    * msql(gid, sid, did, scid, doid=None):
+      - Returns the modified SQL.
+
+    * get_sql(gid, sid, data, scid, tid=None):
+      - Generates the SQL statements to create/update the Check Constraint.
+        object.
+
+    * dependents(gid, sid, did, scid, tid, cid):
+      - Returns the dependents for the Check Constraint object.
+
+    * dependencies(gid, sid, did, scid, tid, cid):
+      - Returns the dependencies for the Check Constraint object.
+
+    * validate_check_constraint(gid, sid, did, scid, tid, cid):
+      - Validate check constraint.
+    """
+    node_type = blueprint.node_type
+
+    parent_ids = [
+        {'type': 'int', 'id': 'gid'},
+        {'type': 'int', 'id': 'sid'},
+        {'type': 'int', 'id': 'did'},
+        {'type': 'int', 'id': 'scid'},
+        {'type': 'int', 'id': 'tid'}
+    ]
+    ids = [
+        {'type': 'int', 'id': 'cid'}
+    ]
+
+    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'}],
+        'validate': [{'get': 'validate_check_constraint'}],
+    })
+
+    def module_js(self):
+        """
+        Load JS file (check_constraint.js) for this module.
+        """
+        return make_response(
+            render_template(
+                "check_constraint/js/check_constraint.js",
+                _=_
+            ),
+            200, {'Content-Type': 'application/x-javascript'}
+        )
+
+    def check_precondition(f):
+        """
+        Works as a decorator.
+        Checks database connection status.
+        Attach connection object and template path.
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            self = args[0]
+            driver = get_driver(PG_DEFAULT_DRIVER)
+            self.manager = driver.connection_manager(kwargs['sid'])
+            self.conn = self.manager.connection(did=kwargs['did'])
+            self.qtIdent = driver.qtIdent
+
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    _("Connection to the server has been lost!")
+                )
+
+            ver = self.manager.version
+
+            # we will set template path for sql scripts
+            if ver >= 90200:
+                self.template_path = 'check_constraint/sql/9.2_plus'
+            elif ver >= 90100:
+                self.template_path = 'check_constraint/sql/9.1_plus'
+
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=kwargs['tid'])
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            self.schema = rset['rows'][0]['schema']
+            self.table = rset['rows'][0]['table']
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+    def end_transaction(self):
+        """
+            End database transaction.
+        Returns:
+
+        """
+        SQL = "END;"
+        self.conn.execute_scalar(SQL)
+
+    @check_precondition
+    def list(self, gid, sid, did, scid, tid, cid=None):
+        """
+        List the Check Constraints.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+            cid: Check Id
+        """
+        try:
+            res = self.get_node_list(gid, sid, did, scid, tid, cid)
+            return ajax_response(
+                    response=res,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_node_list(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function returns all check constraints
+        nodes within that collection as a list.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Cehck constraint ID
+
+        Returns:
+
+        """
+        SQL = render_template("/".join([self.template_path, 'properties.sql']),
+                              tid=tid)
+        status, res = self.conn.execute_dict(SQL)
+
+        return res['rows']
+
+    @check_precondition
+    def nodes(self, gid, sid, did, scid, tid, cid=None):
+        """
+        Returns all the Check Constraints.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+            cid: Check constraint Id.
+        """
+        try:
+            res = self.get_nodes(gid, sid, did, scid, tid, cid)
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_nodes(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function returns all event check constraint as a list.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Check constraint ID
+
+        Returns:
+
+        """
+        res = []
+        SQL = render_template("/".join([self.template_path,
+                                        'nodes.sql']),
+                              tid=tid)
+        status, rset = self.conn.execute_2darray(SQL)
+
+        for row in rset['rows']:
+            if "convalidated" in row and row["convalidated"]:
+                icon = "icon-check_constraints_bad"
+                valid = False
+            else:
+                icon = "icon-check_constraints"
+                valid = True
+            res.append(
+                    self.blueprint.generate_browser_node(
+                        row['oid'],
+                        tid,
+                        row['name'],
+                        icon=icon,
+                        valid=valid
+                    ))
+        return res
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid, cid):
+        """
+        Returns the Check Constraints property.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Check Id
+            cid: Check Constraint Id
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              tid=tid, cid=cid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+        return ajax_response(
+            response=data,
+            status=200
+        )
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function will create a primary key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Check constraint ID
+
+        Returns:
+
+        """
+        required_args = ['consrc']
+
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        for k, v in data.items():
+            try:
+                data[k] = json.loads(v)
+            except (ValueError, TypeError):
+                data[k] = v
+
+        for arg in required_args:
+            if arg not in data or data[arg] == '':
+                return make_json_response(
+                    status=400,
+                    success=0,
+                    errormsg=_(
+                        "Couldn't find the required parameter (%s)." % arg
+                    )
+                )
+
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            if 'name' not in data or data['name'] == "":
+                SQL = "BEGIN;"
+                # Start transaction.
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+            # The below SQL will execute CREATE DDL only
+            SQL = render_template(
+                "/".join([self.template_path, 'create.sql']),
+                data=data
+                )
+
+            status, msg = self.conn.execute_scalar(SQL)
+            if not status:
+                self.end_transaction()
+                return internal_server_error(errormsg=msg)
+
+            if 'name' not in data or data['name'] == "":
+                sql = render_template(
+                    "/".join([self.template_path,
+                              'get_oid_with_transaction.sql'],
+                             ),
+                    tid=tid)
+
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+                self.end_transaction()
+
+                data['name'] = res['rows'][0]['name']
+
+            else:
+                sql = render_template("/".join([self.template_path, 'get_oid.sql']),
+                                      tid=tid,
+                                      name=data['name'])
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+            if "convalidated" in res['rows'][0] and res['rows'][0]["convalidated"]:
+                icon = "icon-check_constraints_bad"
+                valid = False
+            else:
+                icon = "icon-check_constraints"
+                valid = True
+
+            sql = render_template("/".join([self.template_path, 'alter.sql']),
+                                  data=data,
+                                  conn=self.conn)
+            sql = sql.strip('\n').strip(' ')
+
+            if sql != '':
+                status, result = self.conn.execute_scalar(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=result)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    res['rows'][0]['oid'],
+                    tid,
+                    data['name'],
+                    icon=icon,
+                    valid=valid
+                    )
+                )
+
+        except Exception as e:
+            self.end_transaction()
+            return make_json_response(
+                status=400,
+                success=0,
+                errormsg=e
+            )
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid, cid):
+        """
+        Drops the Check Constraint object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Check Id
+            cid: Check Constraint Id
+        """
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  tid=tid, cid=cid)
+            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,
+                                            'delete.sql']),
+                                  data=data)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=_("Check Constraint dropped"),
+                data={
+                    'id': tid,
+                    'scid': scid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid, cid):
+        """
+        Updates the Check Constraint object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+            cid: Check Constraint Id
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        try:
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            SQL = self.get_sql(gid, sid, data, scid, tid, cid)
+            SQL = SQL.strip('\n').strip(' ')
+            if SQL != "":
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                sql = render_template("/".join([self.template_path, 'get_name.sql']),
+                                      cid=cid)
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                if "convalidated" in res['rows'][0] and res['rows'][0]["convalidated"]:
+                    icon = 'icon-check_constraints_)bad'
+                    valid = False
+                else:
+                    icon = 'icon-check_constraints'
+                    valid = True
+
+                return make_json_response(
+                    success=1,
+                    info="Check Constraint updated",
+                    data={
+                        'id': cid,
+                        'tid': tid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did,
+                        'icon': icon,
+                        'val    id': valid
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': cid,
+                        'tid': tid,
+                        '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, tid, cid=None):
+        """
+        Returns the SQL for the Check Constraint object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+            cid: Check Constraint Id
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              tid=tid, cid=cid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+        data['schema'] = self.schema
+        data['table'] = self.table
+
+        SQL = render_template("/".join([self.template_path,
+                                        'create.sql']),
+                              data=data)
+        SQL += "\n"
+        SQL += render_template(
+                "/".join([self.template_path, 'alter.sql']),
+                data=data)
+
+        sql_header = "-- Constraint: {0}\n\n-- ".format(data['name'])
+
+        sql_header += render_template(
+                "/".join([self.template_path, 'delete.sql']),
+                data=data)
+        sql_header += "\n"
+
+        SQL = sql_header + SQL
+
+        return ajax_response(response=SQL)
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid, cid=None):
+        """
+        Returns the modified SQL.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+            cid: Check Constraint Id
+
+        Returns:
+            Check Constraint object in json format.
+        """
+        data = {}
+        for k, v in request.args.items():
+            try:
+                data[k] = json.loads(v)
+            except ValueError:
+                data[k] = v
+
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            sql = self.get_sql(gid, sid, data, scid, tid, cid)
+            sql = sql.strip('\n').strip(' ')
+
+            return make_json_response(
+                    data=sql,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def get_sql(self, gid, sid, data, scid, tid, cid=None):
+        """
+        Generates the SQL statements to create/update the Check Constraint.
+
+         Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+            cid: Check Constraint Id
+        """
+        try:
+            if cid is not None:
+                SQL = render_template("/".join([self.template_path,
+                                                'properties.sql']),
+                                      tid=tid, cid=cid)
+                status, res = self.conn.execute_dict(SQL)
+
+                if not status:
+                    return False, internal_server_error(errormsg=res)
+
+                old_data = res['rows'][0]
+                required_args = ['name']
+                for arg in required_args:
+                    if arg not in data:
+                        data[arg] = old_data[arg]
+
+                SQL = render_template(
+                    "/".join([self.template_path, 'update.sql']),
+                    data=data, o_data=old_data, conn=self.conn
+                    )
+            else:
+                required_args = ['consrc']
+
+                for arg in required_args:
+                    if arg not in data:
+                        return _('-- definition incomplete')
+                    elif isinstance(data[arg], list) and len(data[arg]) < 1:
+                        return _('-- definition incomplete')
+
+                SQL = render_template("/".join([self.template_path,
+                                                'create.sql']),
+                                      data=data)
+                SQL += "\n"
+                SQL += render_template("/".join([self.template_path, 'alter.sql']),
+                                       data=data)
+
+            return SQL
+        except Exception as e:
+            return False, internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, tid, cid):
+        """
+        This function get the dependents and return ajax response
+        for the Check Constraint node.
+
+        Args:
+            gid:  Server Group Id
+            sid:  Server Id
+            did:  Database Id
+            scid: Schema Id
+            tid: Table Id
+            cid: Check Constraint Id
+        """
+        dependents_result = self.get_dependents(self.conn, cid)
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, tid, cid):
+        """
+        This function get the dependencies and return ajax response
+        for the Check Constraint node.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+            cid: Check Constraint Id
+        """
+        dependencies_result = self.get_dependencies(self.conn, cid)
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+    @check_precondition
+    def validate_check_constraint(self, gid, sid, did, scid, tid, cid):
+        """
+        Validate check constraint.
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            tid: Table Id
+            cid: Check Constraint Id
+
+        Returns:
+
+        """
+        data = {}
+        try:
+            data['schema'] = self.schema
+            data['table'] = self.table
+            sql = render_template("/".join([self.template_path, 'get_name.sql']), cid=cid)
+            status, res = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data['name'] = res
+            sql = render_template("/".join([self.template_path, 'validate.sql']), data=data)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=_("Check constraint updated."),
+                data={
+                    'id': cid,
+                    'tid': tid,
+                    'scid': scid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+constraint = ConstraintRegistry(
+    'check_constraint', CheckConstraintModule, CheckConstraintView
+    )
+CheckConstraintView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/img/check-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/img/check-constraints-bad.png
new file mode 100644
index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd
GIT binary patch
literal 579
zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp)
z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n
zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e
zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB
z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ)
zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX
zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b
z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg
z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1
z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i?
z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM
R$bbL<002ovPDHLkV1min0OSAw

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/img/check-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/img/check-constraints.png
new file mode 100644
index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf
GIT binary patch
literal 406
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d
zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4
zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P<
zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O
zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D
zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf
zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{
T)b!9bKn)C@u6{1-oD!M<=4O^k

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/templates/check_constraint/css/check_constraint.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/templates/check_constraint/css/check_constraint.css
new file mode 100644
index 0000000..0691868
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/templates/check_constraint/css/check_constraint.css
@@ -0,0 +1,15 @@
+.icon-check_bad, .icon-check_constraints_bad {
+  background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/check-constraints-bad.png' )}}') !important;
+  background-repeat: no-repeat;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
+
+.icon-check, .icon-check_constraints {
+  background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/check-constraints.png' )}}') !important;
+  background-repeat: no-repeat;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/templates/check_constraint/js/check_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/templates/check_constraint/js/check_constraint.js
new file mode 100644
index 0000000..afc5ba9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/templates/check_constraint/js/check_constraint.js
@@ -0,0 +1,216 @@
+// Check Constraint Module: Node
+define(
+  [
+   'jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser',
+   'alertify', 'pgadmin.browser.collection'
+  ],
+function($, _, S, pgAdmin, pgBrowser, Alertify) {
+
+  // Check Constraint Node
+  if (!pgBrowser.Nodes['check_constraints']) {
+    pgAdmin.Browser.Nodes['check_constraints'] = pgBrowser.Node.extend({
+      type: 'check_constraints',
+      label: '{{ _('Check') }}',
+      collection_type: 'coll-constraints',
+      sqlAlterHelp: 'ddl-alter.html',
+      sqlCreateHelp: 'ddl-constraints.html',
+      hasSQL: true,
+      hasDepends: true,
+      parent_type: ['table'],
+      Init: function() {
+        // Avoid mulitple registration of menus
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_check_constraints_on_coll', node: 'coll-constraints', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 5, label: '{{ _('Check...') }}',
+          icon: 'wcTabIcon icon-check_constraints', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'validate_check_constraint', node: 'check_constraints', module: this,
+          applies: ['object', 'context'], callback: 'validate_check_constraint',
+          category: 'validate', priority: 4, label: '{{ _('Validate check constraint') }}',
+          icon: 'fa fa-link', enable : 'is_not_valid', data: {action: 'edit', check: true}
+        }
+        ]);
+
+      },
+      is_not_valid: function(itemData, item, data) {
+        if (this.canCreate(itemData, item, data)) {
+          return (itemData && !itemData.valid);
+        } else {
+          return false;
+        }
+      },
+      callbacks: {
+        validate_check_constraint: function(args) {
+          var input = args || {};
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+          if (!d) {
+            return false;
+          }
+          var data = d;
+          $.ajax({
+            url: obj.generate_url(i, 'validate', d, true),
+            type:'GET',
+            success: function(res) {
+              if (res.success == 1) {
+                Alertify.success("{{ _('" + res.info + "') }}");
+                t.removeIcon(i);
+                data.valid = true;
+                data.icon = 'icon-check_constraints';
+                t.addIcon(i, {icon: data.icon});
+                setTimeout(function() {t.deselect(i);}, 10);
+                setTimeout(function() {t.select(i);}, 100);
+              }
+            },
+            error: function(xhr, status, error) {
+              try {
+                var err = $.parseJSON(xhr.responseText);
+                if (err.success == 0) {
+                  msg = S('{{ _(' + err.errormsg + ')}}').value();
+                  Alertify.error("{{ _('" + err.errormsg + "') }}");
+                }
+              } catch (e) {}
+              t.unload(i);
+            }
+            });
+
+          return false;
+        }
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          description: undefined,
+          consrc: undefined,
+          connoinherit: undefined,
+          convalidated: true
+        },
+        // Check Constraint Schema
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string',
+          disabled: 'isDisabled'
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'comment', label: '{{ _('Comment') }}', type: 'multiline', cell:
+          'string', mode: ['properties', 'create', 'edit'],
+          deps:['name'], disabled:function(m) {
+            var name = m.get('name');
+            if (!(name && name != '')) {
+              setTimeout(function(){
+                m.set('comment', null);
+              },10);
+              return true;
+            } else {
+              return false;
+            }
+          }
+        },{
+          id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cell:
+          'string', group: '{{ _('Definition') }}', mode: ['properties',
+          'create', 'edit'], disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          }, editable: false
+        },{
+          id: 'connoinherit', label: '{{ _('No Inherit') }}', type:
+          'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode:
+          ['properties', 'create', 'edit'], min_version: 90200,
+          disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          }
+        },{
+          id: 'convalidated', label: "{{ _("Don't validate") }}", type: 'switch', cell:
+          'boolean', group: '{{ _('Definition') }}', min_version: 90200,
+          disabled: function(m) {
+            if ((_.isFunction(m.isNew) && !m.isNew()) ||
+                  (_.has(m, 'handler') &&
+                  !_.isUndefined(m.handler) &&
+                  !_.isUndefined(m.get('oid')))) {
+
+              return !m.get("convalidated");
+            } else {
+              return false;
+            }
+          },
+          mode: ['properties', 'create', 'edit']
+        }],
+        // Client Side Validation
+        validate: function() {
+          var err = {},
+              errmsg;
+
+          if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') {
+            err['consrc'] = '{{ _('Check can not be empty!') }}';
+            errmsg = errmsg || err['consrc'];
+          }
+
+          this.errorModel.clear().set(err);
+
+          if (_.size(err)) {
+            this.trigger('on-status', {msg: errmsg});
+            return errmsg;
+          }
+
+          return null;
+
+        },
+        isDisabled: function(m){
+          if ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) ||
+            (_.isFunction(m.isNew) && !m.isNew())) {
+            var server = (this.node_info || m.top.node_info).server;
+            if (server.version < 90200)
+            {
+              return true;
+            }
+          }
+          return false;
+        }
+      }),
+      // Below function will enable right click menu for creating check constraint.
+      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, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to c reate table
+            if (_.indexOf(['schema'], d._type) > -1)
+              return true;
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1) {
+            return false;
+          } else {
+            return true;
+          }
+      }
+  });
+
+  }
+
+  return pgBrowser.Nodes['check_constraints'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/__init__.py
new file mode 100644
index 0000000..cf599e5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/__init__.py
@@ -0,0 +1,820 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements Exclusion constraint Node"""
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext as _
+from pgadmin.utils.ajax import make_json_response, \
+    make_response as ajax_response, internal_server_error
+from pgadmin.browser.utils import PGChildNodeView
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+import pgadmin.browser.server_groups.servers.databases as database
+from functools import wraps
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.constraints.type \
+    import ConstraintRegistry, ConstraintTypeModule
+
+
+class ExclusionConstraintModule(ConstraintTypeModule):
+    """
+    class ForeignKeyConstraintModule(CollectionNodeModule)
+
+        A module class for Exclusion constraint node derived from ConstraintTypeModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the ForeignKeyConstraintModule and it's base module.
+
+    * get_nodes(gid, sid, did)
+      - Method is used to generate the browser collection node.
+
+    * node_inode()
+      - Method is overridden from its base class to make the node as leaf node.
+
+    * script_load()
+      - Load the module script for language, when any of the database node is
+        initialized.
+    """
+
+    NODE_TYPE = 'exclusion_constraint'
+    COLLECTION_LABEL = _("Foreign Keys")
+
+    def __init__(self, *args, **kwargs):
+        """
+        Method is used to initialize the ForeignKeyConstraintModule and it's base module.
+
+        Args:
+          *args:
+          **kwargs:
+
+        Returns:
+
+        """
+        self.min_ver = None
+        self.max_ver = None
+        super(ExclusionConstraintModule, self).__init__(*args, **kwargs)
+
+    def get_nodes(self, gid, sid, did, scid, tid):
+        """
+        Generate the collection node
+        """
+        pass
+
+    @property
+    def node_inode(self):
+        """
+        Override this property to make the node a leaf node.
+
+        Returns: False as this is the leaf node
+        """
+        return False
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for exclusion_constraint, when any of the table node is
+        initialized.
+
+        Returns: node type of the server module.
+        """
+        return database.DatabaseModule.NODE_TYPE
+
+blueprint = ExclusionConstraintModule(__name__)
+
+
+class ExclusionConstraintView(PGChildNodeView):
+    """
+    class ExclusionConstraintView(PGChildNodeView)
+
+        A view class for Exclusion constraint node derived from PGChildNodeView. This class is
+        responsible for all the stuff related to view like creating, updating Exclusion constraint
+        node, showing properties, showing sql in sql pane.
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the ForeignKeyConstraintView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * end_transaction()
+      - To end any existing database transaction.
+
+    * list()
+      - This function returns Exclusion constraint nodes within that
+        collection as http response.
+
+    * get_list()
+      - This function is used to list all the language nodes within that collection
+        and return list of Exclusion constraint nodes.
+
+    * nodes()
+      - This function returns child node within that collection.
+        Here return all Exclusion constraint node as http response.
+
+    * get_nodes()
+      - returns all Exclusion constraint nodes' list.
+
+    * properties()
+      - This function will show the properties of the selected Exclusion.
+
+    * update()
+      - This function will update the data for the selected Exclusion.
+
+    * msql()
+      - This function is used to return modified SQL for the selected Exclusion.
+
+    * get_sql()
+      - This function will generate sql from model data.
+
+    * sql():
+      - This function will generate sql to show it in sql pane for the selected Exclusion.
+
+    * get_access_methods():
+      - Returns access methods for exclusion constraint.
+
+    * get_oper_class():
+      - Returns operator classes for selected access method.
+
+    * get_operator():
+      - Returns operators for selected column.
+
+    """
+
+    node_type = 'exclusion_constraint'
+
+    parent_ids = [
+            {'type': 'int', 'id': 'gid'},
+            {'type': 'int', 'id': 'sid'},
+            {'type': 'int', 'id': 'did'},
+            {'type': 'int', 'id': 'scid'},
+            {'type': 'int', 'id': 'tid'}
+            ]
+    ids = [{'type': 'int', 'id': 'exid'}
+            ]
+
+    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'}],
+        'module.js': [{}, {}, {'get': 'module_js'}]
+    })
+
+    def module_js(self):
+        """
+        This property defines (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        return make_response(
+                render_template(
+                    "exclusion_constraint/js/exclusion_constraint.js",
+                    _=_
+                    ),
+                200, {'Content-Type': 'application/x-javascript'}
+                )
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+                kwargs['sid']
+                )
+            self.conn = self.manager.connection(did=kwargs['did'])
+
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    _(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            ver = self.manager.version
+
+            if ver >= 90200:
+                self.template_path = 'exclusion_constraint/sql/9.2_plus'
+            elif ver >= 90100:
+                self.template_path = 'exclusion_constraint/sql/9.1_plus'
+
+            # We need parent's name eg table name and schema name
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=kwargs['tid'])
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            for row in rset['rows']:
+                self.schema = row['schema']
+                self.table = row['table']
+            return f(*args, **kwargs)
+
+        return wrap
+
+    def end_transaction(self):
+        SQL = render_template(
+                "/".join([self.template_path, 'end.sql']))
+        # End transaction if any.
+        self.conn.execute_scalar(SQL)
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function is used to list all the Exclusion constraint
+        nodes within that collection.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        try:
+            sql = render_template("/".join([self.template_path, 'properties.sql']),
+                                  tid=tid, cid=exid)
+
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            result = res['rows'][0]
+
+            sql = render_template(
+                "/".join([self.template_path, 'get_constraint_cols.sql']),
+                cid=exid,
+                colcnt=result['indnatts'])
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            columns = []
+            for row in res['rows']:
+                if row['options'] & 1:
+                    order = False
+                    nulls_order = True if (row['options'] & 2) else False
+                else:
+                    order = True
+                    nulls_order = True if (row['options'] & 2) else False
+
+                columns.append({"column": row['coldef'].strip('"'),
+                                "oper_class": row['opcname'],
+                                "order": order,
+                                "nulls_order": nulls_order,
+                                "operator": row['oprname'],
+                                "col_type": row['datatype']
+                                })
+
+            result['columns'] = columns
+
+            return ajax_response(
+                    response=result,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def list(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns all exclusion constraints
+        nodes within that collection as a http response.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        try:
+            res = self.get_node_list(gid, sid, did, scid, tid, exid)
+            return ajax_response(
+                    response=res,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_node_list(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns all exclusion constraints
+        nodes within that collection as a list.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                                        tid=tid)
+        status, res = self.conn.execute_dict(SQL)
+
+        return res['rows']
+
+    @check_precondition
+    def nodes(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns all Exclusion constraint nodes as a
+        http response.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        try:
+            res = self.get_nodes(gid, sid, did, scid, tid, exid)
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_nodes(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns all Exclusion constraint nodes as a list.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        res = []
+        SQL = render_template("/".join([self.template_path,
+                                        'nodes.sql']),
+                              tid=tid)
+        status, rset = self.conn.execute_2darray(SQL)
+
+        for row in rset['rows']:
+            res.append(
+                    self.blueprint.generate_browser_node(
+                        row['oid'],
+                        tid,
+                        row['name'],
+                        icon="icon-exclusion_constraint"
+                    ))
+        return res
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function will create a Exclusion constraint.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        required_args = ['columns']
+
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        for k, v in data.items():
+            try:
+                data[k] = json.loads(v)
+            except (ValueError, TypeError):
+                data[k] = v
+
+        for arg in required_args:
+            if arg not in data:
+                return make_json_response(
+                      status=400,
+                      success=0,
+                      errormsg=_(
+                          "Couldn't find required parameter (%s)." % str(arg)
+                      )
+                    )
+            elif isinstance(data[arg], list) and len(data[arg]) < 1:
+                return make_json_response(
+                      status=400,
+                      success=0,
+                      errormsg=_(
+                          "Couldn't find required parameter (%s)." % str(arg)
+                      )
+                    )
+
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            if 'name' not in data or data['name'] == "":
+                SQL = render_template(
+                    "/".join([self.template_path, 'begin.sql']))
+                # Start transaction.
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+            # The below SQL will execute CREATE DDL only
+            SQL = render_template(
+                "/".join([self.template_path, 'create.sql']),
+                data=data, conn=self.conn
+                )
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                self.end_transaction()
+                return internal_server_error(errormsg=res)
+
+            if 'name' not in data or data['name'] == "":
+                sql = render_template(
+                    "/".join([self.template_path,
+                              'get_oid_with_transaction.sql']),
+                    tid=tid)
+
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+                self.end_transaction()
+
+                data['name'] = res['rows'][0]['name']
+
+            else:
+                sql = render_template("/".join([self.template_path, 'get_oid.sql']), name=data['name'])
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+            if 'name' in data and data['name'] != '':
+                sql = render_template("/".join([self.template_path, 'alter.sql']), data=data, conn=self.conn)
+                sql = sql.strip('\n').strip(' ')
+
+                if sql != '':
+                    status, result = self.conn.execute_scalar(sql)
+                    if not status:
+                        self.end_transaction()
+                        return internal_server_error(errormsg=result)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    res['rows'][0]['oid'],
+                    tid,
+                    data['name'],
+                    icon="icon-exclusion_constraint"
+                    )
+                )
+
+        except Exception as e:
+            self.end_transaction()
+
+            return make_json_response(
+                status=400,
+                success=0,
+                errormsg=e
+            )
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function will update the data for the selected
+        Exclusion constraint.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        try:
+            data['schema'] = self.schema
+            data['table'] = self.table
+            sql = self.get_sql(data, tid, exid)
+            sql = sql.strip('\n').strip(' ')
+            if sql != "":
+                status, res = self.conn.execute_scalar(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                sql = render_template("/".join([self.template_path, 'get_oid.sql']), name=data['name'])
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                return make_json_response(
+                    success=1,
+                    info="Exclusion constraint updated",
+                    data={
+                        'id': res['rows'][0]['oid'],
+                        'tid': tid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did,
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': exid,
+                        'tid': tid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function will delete an existing Exclusion.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        # Below code will decide if it's simple drop or drop with cascade call
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+        try:
+            sql = render_template("/".join([self.template_path, 'get_name.sql']),
+                                  cid=exid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = res['rows'][0]
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            sql = render_template("/".join([self.template_path, 'delete.sql']),
+                                  data=data,
+                                  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=_("Exclusion constraint dropped."),
+                data={
+                    'id': exid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns modified SQL for the selected
+        Exclusion constraint.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        data = {}
+        for k, v in request.args.items():
+            try:
+                data[k] = json.loads(v)
+            except ValueError:
+                data[k] = v
+
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            sql = self.get_sql(data, tid, exid)
+            sql = sql.strip('\n').strip(' ')
+
+            return make_json_response(
+                    data=sql,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def get_sql(self, data, tid, exid=None):
+        """
+        This function will generate sql from model data.
+
+        Args:
+          data: Contains the data of the selected Exclusion constraint.
+          tid: Table ID.
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        if exid is not None:
+            sql = render_template("/".join([self.template_path, 'properties.sql']),
+                                  tid=tid,
+                                  cid=exid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            old_data = res['rows'][0]
+            required_args = ['name']
+            for arg in required_args:
+                if arg not in data:
+                    data[arg] = old_data[arg]
+
+            sql = render_template("/".join([self.template_path, 'update.sql']),
+                                  data=data, o_data=old_data)
+        else:
+            required_args = ['columns']
+
+            for arg in required_args:
+                if arg not in data:
+                    return _('-- definition incomplete')
+                elif isinstance(data[arg], list) and len(data[arg]) < 1:
+                    return _('-- definition incomplete')
+
+            sql = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=data, conn=self.conn)
+            sql += "\n"
+            sql += render_template("/".join([self.template_path, 'alter.sql']),
+                                   data=data, conn=self.conn)
+
+        return sql
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function generates sql to show in the sql pane for the selected
+        Exclusion constraint.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        try:
+            SQL = render_template(
+                "/".join([self.template_path, 'properties.sql']),
+                tid=tid, conn=self.conn, cid=exid)
+            status, result = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=result)
+
+            data = result['rows'][0]
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            sql = render_template(
+                "/".join([self.template_path, 'get_constraint_cols.sql']),
+                cid=exid,
+                colcnt=data['indnatts'])
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            columns = []
+            for row in res['rows']:
+                if row['options'] & 1:
+                    order = False
+                    nulls_order = True if (row['options'] & 2) else False
+                else:
+                    order = True
+                    nulls_order = True if (row['options'] & 2) else False
+
+                columns.append({"column": row['coldef'].strip('"'),
+                                "oper_class": row['opcname'],
+                                "order": order,
+                                "nulls_order": nulls_order,
+                                "operator": row['oprname']
+                                })
+
+            data['columns'] = columns
+
+            if not data['amname'] or data['amname'] == '':
+                data['amname'] = 'btree'
+
+            SQL = render_template(
+                "/".join([self.template_path, 'create.sql']), data=data)
+            SQL += "\n"
+            SQL += render_template(
+                "/".join([self.template_path, 'alter.sql']),
+                data=data, conn=self.conn)
+
+            sql_header = "-- Constraint: {0}\n\n-- ".format(data['name'])
+
+            sql_header += render_template(
+                    "/".join([self.template_path, 'delete.sql']),
+                    data=data)
+            sql_header += "\n"
+
+            SQL = sql_header + SQL
+
+            return ajax_response(response=SQL)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+constraint = ConstraintRegistry(
+    'exclusion_constraint', ExclusionConstraintModule, ExclusionConstraintView
+    )
+ExclusionConstraintView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/img/exclusion_constraint.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/img/exclusion_constraint.png
new file mode 100644
index 0000000000000000000000000000000000000000..bd62eef6410d9315857d2e6d246770e8b65b8f47
GIT binary patch
literal 725
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47y|-)LR^8|wi}<ymTvVw_QrY1
zLC3?d^*UDhFWLA1|9`)wM`KREbvXQ5A-=(6%9fR9ZhU_6^4^pwS28lLWMw^=HS5>=
z_isLZP1<@%wQ`Qjk=K%*nNgG09JqAnY+~Zm($d$dsZT2^E~KQ~e)?Lsd6{J1L`ko#
z8QYH^XzhAZSorVPuP+-md|tBT-`~HFYif>6nkJPuNuqp?YIOD56Ib^8`rV6+d^K<0
zzkmPUtXQ!pGFqZ!hGg3swe*g4XK!wIaz5qe_HfFSrwbQu^AC`0S|K@Qk7Uk-*~hPM
zEhsq<82I=1?;BI6UhV1m_vg>S@bIm*jgk|0NG?2Of9R!T^@7Cpr%znEwc5~Vm%sly
zUEQ5NKC5l*c3rqFx8Q{2nrn7@9?A4<w%>5&&c`3`Z{FOOoV;9Hdxfs<!JM4Ox9?6m
zenWfBW$A4XY?hzYUUH^<`Bv54PXhWDq@H_cwegx-ao6F7A05CjWh@Eu3ubV5b|VeM
zN%D4g;b^-zwF=1LEbxdd2GSm2>~=ES4#-&V>Eak7aXC5R07H*Y2Gbdx45l?XZ+LiQ
zWMmY~)Ws(}c=qt=V{riyAu&Nw;pq&V9$ucOPn<e=Qd>hyv$&ZhB;@K9Q<JS*N=v?e
zImpD;=5|bN*|M}_&%~xBFluK@M_UI6S4XqEt8Zx7+`W4C?)K%=xA(7?_c&m$V4z{4
zVq&6Wqh!RTA|NX)Ek1w3j45*_&6>t1bmBw`i*--4qPDWM%7n?><t2V{=CjN{U1V)w
zV3-jtUHUai{xs0(swJ)wB`Jv|saDBFsX&Us$iUE0*8qr2Lkumf3@ojTjkFEStPBi}
hcbK)IXvob^$xN%ntzp~MJ}aOG22WQ%mvv4FO#q89Jv#sZ

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/js/exclusion_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/js/exclusion_constraint.js
new file mode 100644
index 0000000..6cfa7c3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/js/exclusion_constraint.js
@@ -0,0 +1,915 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+        'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, Alertify) {
+
+  var ExclusionConstraintColumnModel = pgBrowser.Node.Model.extend({
+    defaults: {
+      column: undefined,
+      oper_class: undefined,
+      order: undefined,
+      nulls_order: undefined,
+      operator:undefined,
+      col_type:undefined
+    },
+    toJSON: function () {
+      var d = pgBrowser.Node.Model.prototype.toJSON.apply(this, arguments);
+      delete d.col_type;
+      return d;
+    },
+    schema: [{
+        id: 'column', label:'{{ _('Column') }}', type:'text', editable: false,
+        cell:'string'
+      },{
+        id: 'oper_class', label:'{{ _('Operator class') }}', type:'text',
+        node: 'table', url: 'get_oper_class', first_empty: true,
+        editable: function(m) {
+          if (m instanceof Backbone.Collection) {
+            return true;
+          }
+          if ((_.has(m.collection, 'handler') &&
+                !_.isUndefined(m.collection.handler) &&
+                !_.isUndefined(m.collection.handler.get('oid')))) {
+            return false;
+          }
+
+          if (m.collection) {
+            var indexType = m.collection.handler.get('amname')
+            return (indexType == 'btree' || _.isUndefined(indexType) ||
+              _.isNull(indexType) || indexType == '');
+          } else {
+            return true;
+          }
+        },
+        select2: {
+          allowClear: true, width: 'style',
+          placeholder: '{{ _("Select the operator class") }}'
+        }, cell: Backgrid.Extension.Select2Cell.extend({
+          initialize: function () {
+            Backgrid.Extension.Select2Cell.prototype.initialize.apply(this, arguments);
+
+          var self = this,
+            url = self.column.get('url') || self.defaults.url,
+            m = self.model,
+            indextype = self.model.collection.handler.get('amname');
+
+            if (url && (indextype == 'btree' || _.isUndefined(indextype) ||
+                _.isNull(indextype) || indextype == '')) {
+              var node = this.column.get('schema_node'),
+                  eventHandler = m.top || m,
+                  node_info = this.column.get('node_info'),
+                  full_url = node.generate_url.apply(
+                    node, [
+                      null, url, this.column.get('node_data'),
+                      this.column.get('url_with_id') || false, node_info
+                    ]),
+                  data = [];
+
+              indextype = 'btree';
+
+              if (this.column.get('version_compatible')) {
+                eventHandler.trigger('pgadmin:view:fetching', m, self.column);
+                $.ajax({
+                  async: false,
+                  data : {indextype:indextype},
+                  url: full_url,
+                  success: function(res) {
+                    data = res.data;
+                  },
+                  error: function() {
+                    eventHandler.trigger('pgadmin:view:fetch:error', m, self.column);
+                  }
+                });
+                eventHandler.trigger('pgadmin:view:fetched', m, self.column);
+              }
+              /*
+               * Transform the data
+               */
+              transform = self.column.get('transform') || self.defaults.transform;
+              if (transform && _.isFunction(transform)) {
+                // We will transform the data later, when rendering.
+                // It will allow us to generate different data based on the
+                // dependencies.
+                self.column.set('options', transform.bind(self, data));
+              } else {
+                self.column.set('options', data);
+              }
+            } else {
+              self.column.set('options', []);
+            }
+          }
+        })
+      },{
+        id: 'order', label:'{{ _('DESC') }}', type: 'switch',
+        options: {
+          onText: 'ASC',
+          offText: 'DESC',
+        },editable: function(m) {
+          if (m instanceof Backbone.Collection) {
+            return true;
+          }
+          if ((_.has(m.collection, 'handler') &&
+                !_.isUndefined(m.collection.handler) &&
+                !_.isUndefined(m.collection.handler.get('oid')))) {
+            return false;
+          }
+          return true;
+        }
+      },{
+        id: 'nulls_order', label:'{{ _('NULLs order') }}', type:"switch",
+        options: {
+          onText: 'FIRST',
+          offText: 'LAST',
+        },editable: function(m) {
+          if (m instanceof Backbone.Collection) {
+            return true;
+          }
+          if ((_.has(m.collection, 'handler') &&
+                !_.isUndefined(m.collection.handler) &&
+                !_.isUndefined(m.collection.handler.get('oid')))) {
+            return false;
+          }
+          return true;
+        }
+      },{
+        id: 'operator', label:'{{ _('Operator') }}', type: 'text',
+        node: 'table', url: 'get_operator',
+        editable: function(m) {
+          if (m instanceof Backbone.Collection) {
+            return true;
+          }
+          if ((_.has(m.collection, 'handler') &&
+                !_.isUndefined(m.collection.handler) &&
+                !_.isUndefined(m.collection.handler.get('oid')))) {
+            return false;
+          }
+          return true;
+        },
+        select2: {
+          allowClear: false, width: 'style',
+        }, cell: Backgrid.Extension.Select2Cell.extend({
+        initialize: function () {
+          Backgrid.Extension.Select2Cell.prototype.initialize.apply(this, arguments);
+
+          var self = this,
+            url = self.column.get('url') || self.defaults.url,
+            m = self.model,
+            col_type = self.model.get('col_type');
+
+            self.column.set('options', []);
+
+            if (url && !_.isUndefined(col_type) && !_.isNull(col_type) && col_type != '') {
+              var node = this.column.get('schema_node'),
+                  eventHandler = m.top || m,
+                  node_info = this.column.get('node_info'),
+                  full_url = node.generate_url.apply(
+                    node, [
+                      null, url, this.column.get('node_data'),
+                      this.column.get('url_with_id') || false, node_info
+                    ]),
+                  data = [];
+
+              if (this.column.get('version_compatible')) {
+                eventHandler.trigger('pgadmin:view:fetching', m, self.column);
+                $.ajax({
+                  async: false,
+                  data : {col_type:col_type},
+                  url: full_url,
+                  success: function(res) {
+                    data = res.data;
+                  },
+                  error: function() {
+                    eventHandler.trigger('pgadmin:view:fetch:error', m, self.column);
+                  }
+                });
+                eventHandler.trigger('pgadmin:view:fetched', m, self.column);
+              }
+              /*
+               * Transform the data
+               */
+              transform = self.column.get('transform') || self.defaults.transform;
+              if (transform && _.isFunction(transform)) {
+                // We will transform the data later, when rendering.
+                // It will allow us to generate different data based on the
+                // dependencies.
+                self.column.set('options', transform.bind(self, data));
+              } else {
+                self.column.set('options', data);
+              }
+            }
+          }
+        })
+      }
+    ]
+  });
+
+  var ExclusionConstraintColumnControl =  Backform.ExclusionConstraintColumnControl =
+    Backform.UniqueColCollectionControl.extend({
+
+    initialize: function(opts) {
+      Backform.UniqueColCollectionControl.prototype.initialize.apply(
+        this, arguments
+          );
+
+      var self = this,
+        node = 'exclusion_constraint',
+        headerSchema = [{
+          id: 'column', label:'', type:'text',
+          node: 'column', control: Backform.NodeListByNameControl.extend({
+            initialize: function() {
+              // Here we will decide if we need to call URL
+              // Or fetch the data from parent columns collection
+                if(self.model.handler) {
+                  Backform.Select2Control.prototype.initialize.apply(this, arguments);
+                  // Do not listen for any event(s) for existing constraint.
+                  if (_.isUndefined(self.model.get('oid'))) {
+                    var tableCols = self.model.top.get('columns');
+                    this.listenTo(tableCols, 'remove' , this.removeColumn);
+                    this.listenTo(tableCols, 'change:name', this.resetColOptions);
+                    this.listenTo(tableCols, 'change:cltype', this.resetColOptions);
+                  }
+                  this.custom_options();
+                } else {
+                  Backform.NodeListByNameControl.prototype.initialize.apply(this, arguments);
+                }
+            },
+            removeColumn: function (m) {
+              var that = this;
+              setTimeout(function   () {
+                that.custom_options();
+                that.render.apply(that);
+              }, 50);
+            },
+            resetColOptions: function(m) {
+              var that = this;
+
+              if (m.previous('name') ==  self.headerData.get('column')) {
+                /*
+                 * Table column name has changed so update
+                 * column name in exclude constraint as well.
+                 */
+                self.headerData.set(
+                  {"column": m.get('name')});
+                  self.headerDataChanged();
+              }
+
+              setTimeout(function () {
+                that.custom_options();
+                that.render.apply(that);
+              }, 50);
+            },
+            custom_options: function() {
+              // We will add all the columns entered by user in table model
+              var columns = self.model.top.get('columns'),
+                  added_columns_from_tables = [],
+                  col_types = [];
+
+              if (columns.length > 0) {
+                _.each(columns.models, function(m) {
+                  var col = m.get('name');
+                  if(!_.isUndefined(col) && !_.isNull(col)) {
+                    added_columns_from_tables.push({
+                      label: col, value: col, image:'icon-column'
+                      });
+                    col_types.push({name:col, type:m.get('cltype')});
+                  }
+                });
+              }
+              // Set the values in to options so that user can select
+              this.field.set('options', added_columns_from_tables);
+              self.field.set('col_types', col_types);
+            },
+            remove: function () {
+              if(self.model.handler) {
+                  tableCols = self.model.top.get('columns');
+                  this.stopListening(tableCols, 'remove' , this.removeColumn);
+                  this.stopListening(tableCols, 'change:name' , this.resetColOptions);
+                  this.stopListening(tableCols, 'change:cltype' , this.resetColOptions);
+
+                Backform.Select2Control.prototype.remove.apply(this, arguments);
+
+              } else {
+                Backform.NodeListByNameControl.prototype.remove.apply(this, arguments);
+              }
+            },
+            template: _.template([
+              '<div class="<%=Backform.controlsClassName%> <%=extraClasses.join(\' \')%>">',
+              '  <select class="pgadmin-node-select form-control" name="<%=name%>" style="width:100%;" value="<%-value%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> >',
+              '    <% if (first_empty) { %>',
+              '    <option value="" <%="" === rawValue ? "selected" : "" %>><%- empty_value %></option>',
+              '    <% } %>',
+              '    <% for (var i=0; i < options.length; i++) { %>',
+              '    <% var option = options[i]; %>',
+              '    <option <% if (option.image) { %> data-image=<%= option.image %> <% } %> value=<%= formatter.fromRaw(option.value) %> <%=option.value === rawValue ? "selected=\'selected\'" : "" %>><%-option.label%></option>',
+              '    <% } %>',
+              '  </select>',
+              '</div>'].join("\n"))
+          }),
+          transform: function(rows) {
+            // This will only get called in case of NodeListByNameControl.
+
+            var that = this,
+                node = that.field.get('schema_node'),
+                res = [],
+                col_types = [],
+                filter = that.field.get('filter') || function() { return true; };
+
+            filter = filter.bind(that);
+
+            _.each(rows, function(r) {
+              if (filter(r)) {
+                var l = (_.isFunction(node['node_label']) ?
+                      (node['node_label']).apply(node, [r, that.model, that]) :
+                      r.label),
+                    image = (_.isFunction(node['node_image']) ?
+                      (node['node_image']).apply(
+                        node, [r, that.model, that]
+                        ) :
+                      (node['node_image'] || ('icon-' + node.type)));
+                res.push({
+                  'value': r.label,
+                  'image': image,
+                  'label': l
+                });
+                col_types.push({name:r.label, type:r.datatype});
+              }
+            });
+            self.field.set('col_types', col_types);
+            return res;
+          },
+          canAdd: function(m) {
+            return !((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          },
+          select2: {
+            allowClear: false, width: 'style',
+            placeholder: 'Select column'
+          }, first_empty: !self.model.isNew(),
+          disabled: function(m) {
+            return !_.isUndefined(self.model.get('oid'));
+          }
+        }],
+        headerDefaults = {column: null},
+
+        gridCols = ['column', 'oper_class', 'order', 'nulls_order', 'operator'];
+
+      self.headerData = new (Backbone.Model.extend({
+        defaults: headerDefaults,
+        schema: headerSchema
+      }))({});
+
+      var headerGroups = Backform.generateViewSchema(
+          self.field.get('node_info'), self.headerData, 'create',
+          node, self.field.get('node_data')
+          ),
+          fields = [];
+
+      _.each(headerGroups, function(o) {
+        fields = fields.concat(o.fields);
+      });
+
+      self.headerFields = new Backform.Fields(fields);
+      self.gridSchema = Backform.generateGridColumnsFromModel(
+          self.field.get('node_info'), self.field.get('model'), 'edit', gridCols, self.field.get('schema_node')
+          );
+
+      self.controls = [];
+      self.listenTo(self.headerData, "change", self.headerDataChanged);
+      self.listenTo(self.headerData, "select2", self.headerDataChanged);
+      self.listenTo(self.collection, "add", self.onAddorRemoveColumns);
+      self.listenTo(self.collection, "remove", self.onAddorRemoveColumns);
+    },
+
+    generateHeader: function(data) {
+      var header = [
+        '<div class="subnode-header-form">',
+        ' <div class="container-fluid">',
+        '  <div class="row">',
+        '   <div class="col-xs-4">',
+        '    <label class="control-label"><%-column_label%></label>',
+        '   </div>',
+        '   <div class="col-xs-4" header="column"></div>',
+        '   <div class="col-xs-4">',
+        '     <button class="btn-sm btn-default add" <%=canAdd ? "" : "disabled=\'disabled\'"%> ><%-add_label%></buttton>',
+        '   </div>',
+        '  </div>',
+        ' </div>',
+        '</div>',].join("\n")
+
+      _.extend(data, {
+        column_label: '{{ _('Column')}}',
+        add_label: '{{ _('ADD')}}'
+      });
+
+      var self = this,
+          headerTmpl = _.template(header),
+          $header = $(headerTmpl(data)),
+          controls = this.controls;
+
+      this.headerFields.each(function(field) {
+        var control = new (field.get("control"))({
+          field: field,
+          model: self.headerData
+        });
+
+        $header.find('div[header="' + field.get('name') + '"]').append(
+          control.render().$el
+        );
+
+        controls.push(control);
+      });
+
+      // We should not show add but in properties mode
+      if (data.mode == 'properties') {
+        $header.find("button.add").remove();
+      }
+
+      self.$header = $header;
+
+      return $header;
+    },
+
+    events: _.extend(
+                  {}, Backform.UniqueColCollectionControl.prototype.events,
+                {'click button.add': 'addColumns'}
+                ),
+
+    showGridControl: function(data) {
+
+      var self = this,
+          titleTmpl = _.template("<div class='subnode-header'></div>"),
+          $gridBody =
+            $("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode'></div>").append(
+              titleTmpl({label: data.label})
+            );
+
+      $gridBody.append(self.generateHeader(data));
+
+      var gridColumns = _.clone(this.gridSchema.columns);
+
+      // Insert Delete Cell into Grid
+      if (data.disabled == false && data.canDelete) {
+          gridColumns.unshift({
+            name: "pg-backform-delete", label: "",
+            cell: Backgrid.Extension.DeleteCell,
+            editable: false, cell_priority: -1
+          });
+      }
+
+      if (self.grid) {
+        self.grid.remove();
+        self.grid.null;
+      }
+      // Initialize a new Grid instance
+      var grid = self.grid = new Backgrid.Grid({
+        columns: gridColumns,
+        collection: self.collection,
+        className: "backgrid table-bordered"
+      });
+      self.$grid = grid.render().$el;
+
+      $gridBody.append(self.$grid);
+
+      setTimeout(function() {
+        self.headerData.set({
+          'column': self.$header.find(
+            'div[header="column"] select'
+            ).val()
+            }, {silent:true}
+          );
+      }, 10);
+
+      // Render node grid
+      return $gridBody;
+    },
+
+    headerDataChanged: function() {
+      var self = this, val,
+          data = this.headerData.toJSON(),
+          inSelected = false,
+          checkVars = ['column'];
+
+      if (!self.$header) {
+        return;
+      }
+
+      if (self.control_data.canAdd) {
+        self.collection.each(function(m) {
+          if (!inSelected) {
+            _.each(checkVars, function(v) {
+              if (!inSelected) {
+                val = m.get(v);
+                inSelected = ((
+                  (_.isUndefined(val) || _.isNull(val)) &&
+                  (_.isUndefined(data[v]) || _.isNull(data[v]))
+                  ) ||
+                  (val == data[v]));
+              }
+            });
+          }
+        });
+      }
+      else {
+        inSelected = true;
+      }
+
+      self.$header.find('button.add').prop('disabled', inSelected);
+    },
+
+    addColumns: function(ev) {
+      ev.preventDefault();
+      var self = this,
+          column = self.headerData.get('column');
+
+      if (!column || column == '') {
+        return false;
+      }
+
+      var coll = self.model.get(self.field.get('name')),
+          m = new (self.field.get('model'))(
+                self.headerData.toJSON(), {
+                  silent: true, top: self.model.top,
+                  collection: coll, handler: coll
+                }),
+          col_types =self.field.get('col_types') || [];
+
+      for(var i=0; i < col_types.length; i++) {
+        var col_type = col_types[i];
+        if (col_type['name'] ==  m.get('column')) {
+            m.set({'col_type':col_type['type']});
+          break;
+        }
+      }
+
+      coll.add(m);
+
+      var idx = coll.indexOf(m);
+
+      // idx may not be always > -1 because our UniqueColCollection may
+      // remove 'm' if duplicate value found.
+      if (idx > -1) {
+        self.$grid.find('.new').removeClass('new');
+
+        var newRow = self.grid.body.rows[idx].$el;
+
+        newRow.addClass("new");
+        $(newRow).pgMakeVisible('backform-tab');
+      } else {
+        delete m;
+      }
+
+      return false;
+    },
+
+    onAddorRemoveColumns: function() {
+      var self = this;
+
+      // Wait for collection to be updated before checking for the button to be
+      // enabled, or not.
+      setTimeout(function() {
+          self.collection.trigger('pgadmin:columns:updated', self.collection);
+        self.headerDataChanged();
+      }, 10);
+    },
+
+    remove: function() {
+      /*
+       * Stop listening the events registered by this control.
+       */
+      this.stopListening(this.headerData, "change", this.headerDataChanged);
+      this.listenTo(this.headerData, "select2", this.headerDataChanged);
+      this.listenTo(this.collection, "remove", this.onAddorRemoveColumns);
+
+      // Remove header controls.
+      _.each(this.controls, function(controls) {
+        controls.remove();
+      });
+
+      ExclusionConstraintColumnControl.__super__.remove.apply(this, arguments);
+
+      // Remove the header model
+      delete (this.headerData);
+
+    }
+  });
+
+  // Extend the browser's node class for exclusion constraint node
+  if (!pgBrowser.Nodes['exclusion_constraint']) {
+    pgAdmin.Browser.Nodes['exclusion_constraint'] = pgBrowser.Node.extend({
+      type: 'exclusion_constraint',
+      label: '{{ _('Exclusion constraint') }}',
+      collection_type: 'coll-constraints',
+      sqlAlterHelp: 'ddl-alter.html',
+      sqlCreateHelp: 'ddl-constraints.html',
+      hasSQL: true,
+      parent_type: 'table',
+      canDrop: true,
+      canDropCascade: true,
+      hasDepends: true,
+      Init: function() {
+        /* Avoid multiple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_exclusion_constraint_on_coll', node: 'coll-constraints', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Exclusion constraint...') }}',
+          icon: 'wcTabIcon icon-exclusion_constraint', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }]);
+      },
+      is_not_valid: function(node) {
+        return (node && !node.valid);
+      },
+      // Define the model for exclusion constraint node
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          comment: undefined,
+          spcname: "pg_default",
+          amname: "gist",
+          fillfactor: undefined,
+          condeferrable: undefined,
+          condeferred: undefined,
+          columns: []
+        },
+
+        // Define the schema for the exclusion constraint node
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', type: 'text',
+          mode: ['properties', 'create', 'edit'], editable: true,
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'comment', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          deps:['name'], disabled:function(m) {
+            var name = m.get('name');
+            if (!(name && name != '')) {
+              setTimeout(function(){
+                m.set('comment', null);
+              },10);
+              return true;
+            } else {
+              return false;
+            }
+          }
+        },{
+          id: 'spcname', label: '{{ _('Tablespace') }}',
+          type: 'text', group: '{{ _('Definition') }}',
+          control: 'node-list-by-name', node: 'tablespace',
+          select2:{allowClear:false},
+          filter: function(m) {
+            // Don't show pg_global tablespace in selection.
+            if (m.label == "pg_global") return false;
+            else return true;
+          }
+        },{
+          id: 'amname', label: '{{ _('Access method') }}',
+          type: 'text', group: '{{ _('Definition') }}',
+          url:"get_access_methods", node: 'table',
+          control: Backform.NodeAjaxOptionsControl.extend({
+            // When access method changes we need to clear columns collection
+            onChange: function() {
+              Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+              var self = this,
+              // current access method
+              current_am = self.model.get('amname'),
+              // previous access method
+              previous_am = self.model.previous('amname'),
+              column_collection = self.model.get('columns');
+
+              if (column_collection.length > 0 && current_am != previous_am) {
+                var msg = '{{ _('Changing access method will clear columns collection') }}';
+                Alertify.confirm(msg, function (e) {
+                    // User clicks Ok, lets clear collection
+                    column_collection.reset();
+                    setTimeout(function() {
+                      column_collection.trigger('pgadmin:columns:updated', column_collection);
+                    }, 10);
+
+                  }, function() {
+                    // User clicks Cancel set previous value again in combo box
+                    setTimeout(function(){
+                      self.model.set('amname', previous_am);
+                    }, 10);
+                });
+              }
+            }
+          }),
+          select2:{allowClear:true},
+          disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          }
+        },{
+          id: 'fillfactor', label: '{{ _('Fill factor') }}',
+          type: 'int', group: '{{ _('Definition') }}', allowNull: true
+        },{
+          id: 'condeferrable', label: '{{ _('Deferrable') }}',
+          type: 'switch', group: '{{ _('Definition') }}', deps: ['index'],
+          disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          }
+        },{
+          id: 'condeferred', label: '{{ _('Deferred') }}',
+          type: 'switch', group: '{{ _('Definition') }}',
+          deps: ['condeferrable'],
+          disabled: function(m) {
+            if((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew())) {
+              return true;
+            }
+
+            // Disable if condeferred is false or unselected.
+            if(m.get('condeferrable') == true) {
+              return false;
+            } else {
+              setTimeout(function(){
+                m.set('condeferred', false);
+              },10);
+              return true;
+            }
+          }
+        },{
+          id: 'constraint', label:'{{ _('Constraint') }}', cell: 'string',
+          type: 'multiline', mode: ['create', 'edit'], editable: false,
+          group: '{{ _('Definition') }}', disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          }
+        },{
+          id: 'columns', label: '{{ _('Columns') }}',
+          type: 'collection', group: '{{ _('Columns') }}', disabled: false,
+          deps:['amname'], canDelete: true, editable: false,
+          canAdd: function(m) {
+            // We can't update columns of existing exclusion constraint.
+            return !((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          },
+          control: ExclusionConstraintColumnControl,
+          model: ExclusionConstraintColumnModel,
+          disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          },
+          cell: Backgrid.StringCell.extend({
+            initialize: function() {
+              Backgrid.StringCell.prototype.initialize.apply(this, arguments);
+              var self = this;
+              // Do not listen for any event(s) for existing constraint.
+              if (_.isUndefined(self.model.get('oid'))) {
+                var tableCols = self.model.top.get('columns');
+
+                self.listenTo(tableCols, 'remove' , self.removeColumn);
+                self.listenTo(tableCols, 'change:name', self.resetColOptions);
+                self.listenTo(tableCols, 'change:cltype', self.removeColumnWithType);
+              }
+
+              this.model.get('columns').on('pgadmin:columns:updated', function() {
+                self.render.apply(self);
+              });
+            },
+            removeColumnWithType: function(m){
+              var self = this,
+                  cols = self.model.get('columns'),
+                  removedCols = cols.where(
+                    {col_type: m.previous('cltype')}
+                    );
+
+              cols.remove(removedCols);
+              setTimeout(function () {
+                self.render();
+              }, 10);
+
+              setTimeout(function () {
+                constraints = self.model.top.get("exclude_constraint");
+                var removed = [];
+                constraints.each(function(constraint) {
+                  if (constraint.get("columns").length == 0) {
+                     removed.push(constraint);
+                  }
+                });
+                constraints.remove(removed);
+              },100);
+            },
+            removeColumn: function(m){
+              var self = this,
+                  removedCols = self.model.get('columns').where(
+                    {column: m.get('name')}
+                    );
+
+              self.model.get('columns').remove(removedCols);
+              setTimeout(function () {
+                self.render();
+              }, 10);
+
+              setTimeout(function () {
+                constraints = self.model.top.get("exclude_constraint");
+                var removed = [];
+                constraints.each(function(constraint) {
+                  if (constraint.get("columns").length == 0) {
+                     removed.push(constraint);
+                  }
+                });
+                constraints.remove(removed);
+              },100);
+            },
+            resetColOptions : function(m) {
+                var self = this,
+                  updatedCols = self.model.get('columns').where(
+                  {"column": m.previous('name')}
+                  );
+
+                if (updatedCols.length > 0) {
+                  /*
+                   * Table column name has changed so update
+                   * column name in foreign key as well.
+                   */
+                  updatedCols[0].set(
+                  {"column": m.get('name')});
+                }
+
+                setTimeout(function () {
+                  self.render();
+                }, 10);
+            },
+            formatter: {
+              fromRaw: function (rawValue, model) {
+                return rawValue.pluck("column").toString();
+              },
+              toRaw: function (val, model) {
+                return val;
+              }
+            },
+            render: function() {
+              return Backgrid.StringCell.prototype.render.apply(this, arguments);
+            },
+            remove: function() {
+              var tableCols = this.model.top.get('columns'),
+                  cols = this.model.get('columns');
+              if (cols) {
+                cols.off('pgadmin:columns:updated');
+              }
+
+              this.stopListening(tableCols, 'remove' , self.removeColumn);
+              this.stopListening(tableCols, 'change:name' , self.resetColOptions);
+              this.stopListening(tableCols, 'change:cltype' , self.removeColumnWithType);
+
+              Backgrid.StringCell.prototype.remove.apply(this, arguments);
+            }
+          }),
+        }],
+        validate: function() {
+          this.errorModel.clear();
+          var columns = this.get('columns');
+          if ((_.isUndefined(columns) || _.isNull(columns) || columns.length < 1)) {
+            var msg = '{{ _('Please specify columns for Exclude constraint.') }}';
+            this.errorModel.set('columns', msg);
+            return msg;
+          }
+
+          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, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to create table
+            if (_.indexOf(['schema'], d._type) > -1)
+              return true;
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1) {
+            return false;
+          } else {
+            return true;
+          }
+      }
+    });
+  }
+
+  return pgBrowser.Nodes['exclusion_constraint'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/__init__.py
new file mode 100644
index 0000000..ce05031
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/__init__.py
@@ -0,0 +1,1072 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements Foreign key constraint Node"""
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext as _
+from pgadmin.utils.ajax import make_json_response, \
+    make_response as ajax_response, internal_server_error
+from pgadmin.browser.utils import PGChildNodeView
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+import pgadmin.browser.server_groups.servers.databases as database
+from functools import wraps
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.constraints.type \
+    import ConstraintRegistry, ConstraintTypeModule
+
+
+class ForeignKeyConstraintModule(ConstraintTypeModule):
+    """
+    class ForeignKeyConstraintModule(CollectionNodeModule)
+
+        A module class for Foreign key constraint node derived from ConstraintTypeModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the ForeignKeyConstraintModule and it's base module.
+
+    * get_nodes(gid, sid, did)
+      - Method is used to generate the browser collection node.
+
+    * node_inode()
+      - Method is overridden from its base class to make the node as leaf node.
+
+    * script_load()
+      - Load the module script for language, when any of the database node is
+        initialized.
+    """
+
+    NODE_TYPE = 'foreign_key'
+    COLLECTION_LABEL = _("Foreign Keys")
+
+    def __init__(self, *args, **kwargs):
+        """
+        Method is used to initialize the ForeignKeyConstraintModule and it's base module.
+
+        Args:
+          *args:
+          **kwargs:
+
+        Returns:
+
+        """
+        self.min_ver = None
+        self.max_ver = None
+        super(ForeignKeyConstraintModule, self).__init__(*args, **kwargs)
+
+    def get_nodes(self, gid, sid, did, scid, tid):
+        """
+        Generate the collection node
+        """
+        pass
+
+    @property
+    def node_inode(self):
+        """
+        Override this property to make the node a leaf node.
+
+        Returns: False as this is the leaf node
+        """
+        return False
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for foreign_key, when any of the table node is
+        initialized.
+
+        Returns: node type of the server module.
+        """
+        return database.DatabaseModule.NODE_TYPE
+
+    @property
+    def csssnippets(self):
+        """
+        Returns a snippet of css to include in the page
+        """
+        snippets = [
+                render_template(
+                    "browser/css/collection.css",
+                    node_type=self.node_type,
+                    _=_
+                    ),
+                render_template(
+                    "foreign_key/css/foreign_key.css",
+                    node_type=self.node_type,
+                    _=_
+                    )
+                ]
+
+        for submodule in self.submodules:
+            snippets.extend(submodule.csssnippets)
+
+        return snippets
+
+blueprint = ForeignKeyConstraintModule(__name__)
+
+
+class ForeignKeyConstraintView(PGChildNodeView):
+    """
+    class ForeignKeyConstraintView(PGChildNodeView)
+
+        A view class for Foreign key constraint node derived from PGChildNodeView. This class is
+        responsible for all the stuff related to view like creating, updating Foreign key constraint
+        node, showing properties, showing sql in sql pane.
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the ForeignKeyConstraintView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * list()
+      - This function returns foreign key constraint nodes within that
+        collection as http response.
+
+    * get_list()
+      - This function is used to list all the language nodes within that collection
+        and return list of foreign key constraint nodes.
+
+    * nodes()
+      - This function returns child node within that collection.
+        Here return all foreign key constraint node as http response.
+
+    * get_nodes()
+      - returns all foreign key constraint nodes' list.
+
+    * properties()
+      - This function will show the properties of the selected foreign key.
+
+    * update()
+      - This function will update the data for the selected foreign key.
+
+    * msql()
+      - This function is used to return modified SQL for the selected foreign key.
+
+    * get_sql()
+      - This function will generate sql from model data.
+
+    * sql():
+      - This function will generate sql to show it in sql pane for the selected foreign key.
+
+    * get_indices():
+        - This function returns indices for current table.
+
+    """
+
+    node_type = 'foreign_key'
+
+    parent_ids = [
+            {'type': 'int', 'id': 'gid'},
+            {'type': 'int', 'id': 'sid'},
+            {'type': 'int', 'id': 'did'},
+            {'type': 'int', 'id': 'scid'},
+            {'type': 'int', 'id': 'tid'}
+            ]
+    ids = [{'type': 'int', 'id': 'fkid'}
+            ]
+
+    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'}],
+        'indices': [{}, {'get': 'get_indices'}],
+        'validate': [{'get': 'validate_foreign_key'}],
+        'get_coveringindex': [{}, {'get': 'get_coveringindex'}]
+    })
+
+    def module_js(self):
+        """
+        This property defines (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        return make_response(
+                render_template(
+                    "foreign_key/js/foreign_key.js",
+                    _=_
+                    ),
+                200, {'Content-Type': 'application/x-javascript'}
+                )
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+                kwargs['sid']
+                )
+            self.conn = self.manager.connection(did=kwargs['did'])
+
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    _(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            self.template_path = 'foreign_key/sql'
+            # We need parent's name eg table name and schema name
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=kwargs['tid'])
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            for row in rset['rows']:
+                self.schema = row['schema']
+                self.table = row['table']
+            return f(*args, **kwargs)
+
+        return wrap
+
+    def end_transaction(self):
+        SQL = render_template(
+                "/".join([self.template_path, 'end.sql']))
+        # End transaction if any.
+        self.conn.execute_scalar(SQL)
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function is used to list all the foreign key
+        nodes within that collection.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        try:
+            sql = render_template("/".join([self.template_path, 'properties.sql']),
+                                  tid=tid, cid=fkid)
+
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            result = res['rows'][0]
+
+            sql = render_template("/".join([self.template_path,
+                                            'get_constraint_cols.sql']),
+                                  tid=tid,
+                                  keys=zip(result['confkey'], result['conkey']),
+                                  confrelid=result['confrelid'])
+
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            columns = []
+            cols = []
+            for row in res['rows']:
+                columns.append({"local_column": row['conattname'],
+                                "references": result['confrelid'],
+                                "referenced": row['confattname']})
+                cols.append(row['conattname'])
+
+            result['columns'] = columns
+
+            if fkid:
+                coveringindex = self.search_coveringindex(tid, cols)
+                result['coveringindex'] = coveringindex
+                if coveringindex:
+                    result['autoindex'] = True
+                    result['hasindex'] = True
+                else:
+                    result['autoindex'] = False
+                    result['hasindex'] = False
+
+            return ajax_response(
+                    response=result,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def list(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function returns all foreign keys
+        nodes within that collection as a http response.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        try:
+            res = self.get_node_list(gid, sid, did, scid, tid, fkid)
+            return ajax_response(
+                    response=res,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_node_list(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function returns all foreign keys
+        nodes within that collection as a list.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                                        tid=tid)
+        status, res = self.conn.execute_dict(SQL)
+
+        return res['rows']
+
+    @check_precondition
+    def nodes(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function returns all event trigger nodes as a
+        http response.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        try:
+            res = self.get_nodes(gid, sid, did, scid, tid, fkid)
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_nodes(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function returns all event trigger nodes as a list.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        res = []
+        SQL = render_template("/".join([self.template_path,
+                                        'nodes.sql']),
+                              tid=tid)
+        status, rset = self.conn.execute_2darray(SQL)
+
+        for row in rset['rows']:
+            if row["convalidated"]:
+                icon = "icon-foreign_key_no_validate"
+                valid = False
+            else:
+                icon = "icon-foreign_key"
+                valid = True
+            res.append(
+                    self.blueprint.generate_browser_node(
+                        row['oid'],
+                        tid,
+                        row['name'],
+                        icon=icon,
+                        valid=valid
+                    ))
+        return res
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function will create a foreign key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        required_args = ['columns']
+
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        for k, v in data.items():
+            try:
+                data[k] = json.loads(v)
+            except (ValueError, TypeError):
+                data[k] = v
+
+        for arg in required_args:
+            if arg not in data:
+                return make_json_response(
+                      status=400,
+                      success=0,
+                      errormsg=_(
+                          "Couldn't find required parameter (%s)." % str(arg)
+                      )
+                    )
+            elif isinstance(data[arg], list) and len(data[arg]) < 1:
+                return make_json_response(
+                      status=400,
+                      success=0,
+                      errormsg=_(
+                          "Couldn't find required parameter (%s)." % str(arg)
+                      )
+                    )
+
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=data['columns'][0]['references'])
+            status, res = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data['remote_schema'] = res['rows'][0]['schema']
+            data['remote_table'] = res['rows'][0]['table']
+
+            if 'name' not in data or data['name'] == "":
+                SQL = render_template(
+                    "/".join([self.template_path, 'begin.sql']))
+                # Start transaction.
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+            # The below SQL will execute CREATE DDL only
+            SQL = render_template(
+                "/".join([self.template_path, 'create.sql']),
+                data=data, conn=self.conn
+                )
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                self.end_transaction()
+                return internal_server_error(errormsg=res)
+
+            if 'name' not in data or data['name'] == "":
+                sql = render_template(
+                    "/".join([self.template_path,
+                              'get_oid_with_transaction.sql']),
+                    tid=tid)
+
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+                self.end_transaction()
+
+                data['name'] = res['rows'][0]['name']
+
+            else:
+                sql = render_template("/".join([self.template_path, 'get_oid.sql']), name=data['name'])
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+            if res['rows'][0]["convalidated"]:
+                icon = "icon-foreign_key_no_validate"
+                valid = False
+            else:
+                icon = "icon-foreign_key"
+                valid = True
+
+            sql = render_template("/".join([self.template_path, 'alter.sql']), data=data, conn=self.conn)
+            sql = sql.strip('\n').strip(' ')
+
+            if sql != '':
+                status, result = self.conn.execute_scalar(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=result)
+
+            if data['autoindex']:
+                sql = render_template(
+                    "/".join([self.template_path, 'create_index.sql']),
+                    data=data, conn=self.conn)
+                sql = sql.strip('\n').strip(' ')
+
+                if sql != '':
+                    status, idx_res = self.conn.execute_scalar(sql)
+                    if not status:
+                        self.end_transaction()
+                        return internal_server_error(errormsg=idx_res)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    res['rows'][0]['oid'],
+                    tid,
+                    data['name'],
+                    valid=valid,
+                    icon=icon
+                    )
+                )
+
+        except Exception as e:
+            self.end_transaction()
+            return make_json_response(
+                status=400,
+                success=0,
+                errormsg=e
+            )
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function will update the data for the selected
+        foreign key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        try:
+            data['schema'] = self.schema
+            data['table'] = self.table
+            sql = self.get_sql(data, tid, fkid)
+            sql = sql.strip('\n').strip(' ')
+            if sql != "":
+                status, res = self.conn.execute_scalar(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                sql = render_template("/".join([self.template_path, 'get_oid.sql']),
+                                      tid=tid,
+                                      name=data['name'])
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                if res['rows'][0]["convalidated"]:
+                    icon = "icon-foreign_key_no_validate"
+                    valid = False
+                else:
+                    icon = "icon-foreign_key"
+                    valid = True
+
+                return make_json_response(
+                    success=1,
+                    info="Foreign key updated",
+                    data={
+                        'id': res['rows'][0]['oid'],
+                        'tid': tid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did,
+                        'icon':icon,
+                        'valid':valid
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': fkid,
+                        'tid': tid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function will delete an existing foreign key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        # Below code will decide if it's simple drop or drop with cascade call
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+        try:
+            sql = render_template("/".join([self.template_path, 'get_name.sql']), fkid=fkid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = res['rows'][0]
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            sql = render_template("/".join([self.template_path, 'delete.sql']), data=data, 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=_("Foreign key dropped."),
+                data={
+                    'id': fkid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function returns modified SQL for the selected
+        foreign key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        data = {}
+        for k, v in request.args.items():
+            try:
+                data[k] = json.loads(v)
+            except ValueError:
+                data[k] = v
+
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            sql = self.get_sql(data, tid, fkid)
+            sql = sql.strip('\n').strip(' ')
+
+            return make_json_response(
+                    data=sql,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def get_sql(self, data, tid, fkid=None):
+        """
+        This function will generate sql from model data.
+
+        Args:
+          data: Contains the data of the selected foreign key constraint.
+          tid: Table ID.
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        if fkid is not None:
+            sql = render_template("/".join([self.template_path, 'properties.sql']), tid=tid, cid=fkid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            old_data = res['rows'][0]
+            required_args = ['name']
+            for arg in required_args:
+                if arg not in data:
+                    data[arg] = old_data[arg]
+
+            sql = render_template("/".join([self.template_path, 'update.sql']),
+                                  data=data, o_data=old_data)
+
+            if 'autoindex' in data and data['autoindex'] and\
+                    ('coveringindex' in data and
+                             data['coveringindex'] != ''):
+
+                col_sql = render_template("/".join([self.template_path,
+                                                'get_constraint_cols.sql']),
+                                      tid=tid,
+                                      keys=zip(old_data['confkey'], old_data['conkey']),
+                                      confrelid=old_data['confrelid'])
+
+                status, res = self.conn.execute_dict(col_sql)
+
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                columns = []
+                for row in res['rows']:
+                    columns.append({"local_column": row['conattname'],
+                                    "references": old_data['confrelid'],
+                                    "referenced": row['confattname']})
+
+                data['columns'] = columns
+
+                sql += render_template(
+                    "/".join([self.template_path, 'create_index.sql']),
+                    data=data, conn=self.conn)
+        else:
+            required_args = ['columns']
+
+            for arg in required_args:
+                if arg not in data:
+                    return _('-- definition incomplete')
+                elif isinstance(data[arg], list) and len(data[arg]) < 1:
+                    return _('-- definition incomplete')
+
+            if data['autoindex'] and ('coveringindex' not in data or
+                                      data['coveringindex'] == ''):
+                return _('-- definition incomplete')
+
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=data['columns'][0]['references'])
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            data['remote_schema'] = rset['rows'][0]['schema']
+            data['remote_table'] = rset['rows'][0]['table']
+
+            sql = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=data, conn=self.conn)
+            sql += "\n"
+            sql += render_template("/".join([self.template_path, 'alter.sql']),
+                                   data=data, conn=self.conn)
+
+            if data['autoindex']:
+                sql += render_template(
+                    "/".join([self.template_path, 'create_index.sql']),
+                    data=data, conn=self.conn)
+        return sql
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function generates sql to show in the sql pane for the selected
+        foreign key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          fkid: Foreign key constraint ID
+
+        Returns:
+
+        """
+        try:
+            SQL = render_template(
+                "/".join([self.template_path, 'properties.sql']),
+                tid=tid, conn=self.conn, cid=fkid)
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = res['rows'][0]
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            sql = render_template("/".join([self.template_path,
+                                            'get_constraint_cols.sql']),
+                                  tid=tid,
+                                  keys=zip(data['confkey'], data['conkey']),
+                                  confrelid=data['confrelid'])
+
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            columns = []
+            for row in res['rows']:
+                columns.append({"local_column": row['conattname'],
+                                "references": data['confrelid'],
+                                "referenced": row['confattname']})
+
+            data['columns'] = columns
+
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=data['columns'][0]['references'])
+            status, res = self.conn.execute_2darray(SQL)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data['remote_schema'] = res['rows'][0]['schema']
+            data['remote_table'] = res['rows'][0]['table']
+
+            SQL = render_template(
+                "/".join([self.template_path, 'create.sql']), data=data)
+            SQL += "\n"
+            SQL += render_template(
+                "/".join([self.template_path, 'alter.sql']),
+                data=data, conn=self.conn)
+
+            sql_header = "-- Constraint: {0}\n\n-- ".format(data['name'])
+
+            sql_header += render_template(
+                    "/".join([self.template_path, 'delete.sql']),
+                    data=data)
+            sql_header += "\n"
+
+            SQL = sql_header + SQL
+
+            return ajax_response(response=SQL)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function gets the dependents and returns an ajax response
+        for the event trigger node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            etid: Event trigger ID
+        """
+        dependents_result = self.get_dependents(self.conn, fkid)
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, tid, fkid=None):
+        """
+        This function gets the dependencies and returns an ajax response
+        for the event trigger node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            etid: Event trigger ID
+        """
+        dependencies_result = self.get_dependencies(self.conn, fkid)
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+    @check_precondition
+    def validate_foreign_key(self, gid, sid, did, scid, tid, fkid):
+        """
+
+        Args:
+          gid:
+          sid:
+          did:
+          scid:
+          tid:
+          fkid:
+
+        Returns:
+
+        """
+        data = {}
+        try:
+            data['schema'] = self.schema
+            data['table'] = self.table
+            sql = render_template("/".join([self.template_path, 'get_name.sql']), fkid=fkid)
+            status, res = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data['name'] = res
+            sql = render_template("/".join([self.template_path, 'validate.sql']), data=data)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=_("Foreign key updated."),
+                data={
+                    'id': fkid,
+                    'tid': tid,
+                    'scid': scid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def search_coveringindex(self, tid, cols):
+        """
+
+        Args:
+          tid: Table id
+          cols: column list
+
+        Returns:
+
+        """
+
+        cols = set(cols)
+        SQL = render_template("/".join([self.template_path,
+                                        'get_constraints.sql']),
+                              tid=tid)
+        status, constraints = self.conn.execute_dict(SQL)
+
+        if not status:
+            raise Exception(constraints)
+
+        for costrnt in constraints['rows']:
+
+            sql = render_template(
+                "/".join([self.template_path, 'get_cols.sql']),
+                cid=costrnt['oid'],
+                colcnt=costrnt['indnatts'])
+            status, rest = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=rest)
+
+            indexcols = set()
+            for r in rest['rows']:
+                indexcols.add(r['column'].strip('"'))
+
+            if len(cols - indexcols) == len(indexcols - cols) == 0:
+                return costrnt["idxname"]
+
+        return None
+
+    @check_precondition
+    def get_coveringindex(self, gid, sid, did, scid, tid=None):
+        """
+
+        Args:
+          gid:
+          sid:
+          did:
+          scid:
+          tid:
+
+        Returns:
+
+        """
+
+        data = request.args if request.args else None
+        index = None
+        try:
+            if data and 'cols' in data:
+                cols = set(json.loads(data['cols']))
+                index = self.search_coveringindex(tid, cols)
+
+            return make_json_response(
+                data=index,
+                status=200
+                )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+constraint = ConstraintRegistry(
+    'foreign_key', ForeignKeyConstraintModule, ForeignKeyConstraintView
+    )
+ForeignKeyConstraintView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/img/foreign_key.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/img/foreign_key.png
new file mode 100644
index 0000000000000000000000000000000000000000..b3605500439db72c2c43879abb61a24c956e1bf5
GIT binary patch
literal 299
zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAm`Z~Df*BafCZDwc@shmVT^JZv
z^(q?yd7K3vk;OpT1B~5HX4?T7@t!V@ArhC96BcmYxO{&<`^@{hp1!(zcCE*!Xt{ek
z>#~zoS#lB*UhMz1;M236-R60Bchvnn_1J5|fxo9_r|aE`sZD=6Ra!x`dfT1-l|Ns#
zc?qz5Y;4QhSCW{_ZI`Xd&M@z_Ipcx{mt%ldsg}4#l%yn<q*^5xr2;7iBLhPNT|*;X
z1H%v_11l3_D-%;~15+ylgL&`tR-tIf%}>cptAuDUumo!Wsy4AQFoS3~H9d3<Py>Uf
LtDnm{r-UW|7b#?7

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/img/foreign_key_no_validate.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/img/foreign_key_no_validate.png
new file mode 100644
index 0000000000000000000000000000000000000000..975561bcf7237d5e99e9d42ff705db67f1bb2fdb
GIT binary patch
literal 618
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47?}cmLR^8|J$v@__xDejFyZ2b
zi$8z-{QB`zYisM4En6<0I(I!O=_mukh49ENQx_g}_r9B%`S9VxT^qNb3Jm#HUVhl!
zdv;s@UR$S+;o(=WUVZT3K}|{3x3aQtMMYop^7dFed<+YF@9+Q4!}I?A`&DI?UvqQ6
zWMq6!O8O8Q`rZ#H?s56j<qIdyd`?XK7#;l~B;>uX-#d5r&6_u0x^(Hn`SXVl9ln*C
z`XLx7?)%QoZIzC3Utiz$4V$k4O$ZKt@8k2%)%A^?{c8)0-OjEbf`Z<Ad%tyYeQj?3
z%Fytow)Tg>z;|9=Z=GFcI(xm;(te?;`dmTbt&{T+H?Q>ONi$u%pUW#W=hVD!YnKOx
z0%J*#UoeBivm0qZ4rhT!WHFHT0Ash4*>*t2d`}n05Q)ocd!Gt51&FlhcQmc(yxVnG
z_QqY=_y4tuT#tzt)X$mu_uM%>#d^WRQWlF}KCf&v?%p7fwDq9h|E9y52U)it=ayGp
zV=v&MA>$~rv4TnDgeT|961JN$^VB39^+a5!oSK^T_Oj!Ts>L_67GHkr`19O1cITdB
zlVAFHub7p*F(z^E%~veD|1!GIIVYasa7c9fZ)Shnk3kvpt3v;?HQy7Q(A;;*1?W`O
z64!{5l*E!$tK_0oAjM#0U}&goV61Ck5n^a$WoT|?V5x0jW@TV-`Pqsj6b-rgDVb@N
WxHT-=EpQL0fx*+&&t;ucLK6V@6Aj$}

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/templates/foreign_key/css/foreign_key.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/templates/foreign_key/css/foreign_key.css
new file mode 100644
index 0000000..aeaff11
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/templates/foreign_key/css/foreign_key.css
@@ -0,0 +1,12 @@
+.icon-foreign_key {
+  background-image: url('{{ url_for('NODE-foreign_key.static', filename='img/foreign_key.png') }}') !important;
+  background-repeat: no-repeat;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
+
+.icon-foreign_key_no_validate {
+  background-image: url('{{ url_for('NODE-foreign_key.static', filename='img/foreign_key_no_validate.png') }}') !important;
+  border-radius: 10px
+}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/templates/foreign_key/js/foreign_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/templates/foreign_key/js/foreign_key.js
new file mode 100644
index 0000000..7da458a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/templates/foreign_key/js/foreign_key.js
@@ -0,0 +1,1072 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+        'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, Alertify) {
+
+  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>'
+          );
+    }
+  },
+      headerSelectControlTemplate =  _.template([
+                              '<div class="<%=Backform.controlsClassName%> <%=extraClasses.join(\' \')%>">',
+                              '  <select class="pgadmin-node-select form-control" name="<%=name%>" style="width:100%;" value="<%-value%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> >',
+                              '    <% if (first_empty) { %>',
+                              '    <option value="" <%="" === rawValue ? "selected" : "" %>><%- empty_value %></option>',
+                              '    <% } %>',
+                              '    <% for (var i=0; i < options.length; i++) { %>',
+                              '    <% var option = options[i]; %>',
+                              '    <option <% if (option.image) { %> data-image=<%= option.image %> <% } %> value=<%= formatter.fromRaw(option.value) %> <%=option.value === rawValue ? "selected=\'selected\'" : "" %>><%-option.label%></option>',
+                              '    <% } %>',
+                              '  </select>',
+                              '</div>'].join("\n"));
+
+  var ForeignKeyColumnModel = pgBrowser.Node.Model.extend({
+    defaults: {
+      local_column: undefined,
+      references: undefined,
+      referenced: undefined
+    },
+    schema: [{
+        id: 'local_column', label:'Local', type:'text', editable: false,
+        cellHeaderClasses: 'width_percent_50', cell:'string',
+        headerCell: Backgrid.Extension.CustomHeaderCell
+      },{
+        id: 'referenced', label:'Referenced', type: 'text', editable: false,
+        cell:'string', cellHeaderClasses: 'width_percent_50',
+        headerCell: Backgrid.Extension.CustomHeaderCell
+      }]
+  });
+
+  var ForeignKeyColumnControl =  Backform.ForeignKeyColumnControl =
+    Backform.UniqueColCollectionControl.extend({
+
+    initialize: function(opts) {
+      Backform.UniqueColCollectionControl.prototype.initialize.apply(
+        this, arguments
+          );
+
+      var self = this,
+        node = 'foreign_key',
+        headerSchema = [{
+          id: 'local_column', label:'', type:'text',
+          node: 'column', control: Backform.NodeListByNameControl.extend({
+            initialize: function() {
+              // Here we will decide if we need to call URL
+              // Or fetch the data from parent columns collection
+                if(self.model.handler) {
+                  Backform.Select2Control.prototype.initialize.apply(this, arguments);
+                  // Do not listen for any event(s) for existing constraint.
+                  if (_.isUndefined(self.model.get('oid'))) {
+                    var tableCols = self.model.top.get('columns');
+                    this.listenTo(tableCols, 'remove' , this.removeColumn);
+                    this.listenTo(tableCols, 'change:name', this.resetColOptions);
+                  }
+
+                  this.custom_options();
+                } else {
+                  Backform.NodeListByNameControl.prototype.initialize.apply(this, arguments);
+                }
+            },
+            removeColumn: function (m) {
+              var that = this;
+              setTimeout(function   () {
+                that.custom_options();
+                that.render.apply(that);
+              }, 50);
+            },
+            resetColOptions: function(m) {
+              var that = this;
+
+              if (m.previous('name') ==  self.headerData.get('local_column')) {
+                /*
+                 * Table column name has changed so update
+                 * column name in foreign key as well.
+                 */
+                self.headerData.set(
+                  {"local_column": m.get('name')});
+                  self.headerDataChanged();
+              }
+
+              setTimeout(function () {
+                that.custom_options();
+                that.render.apply(that);
+              }, 50);
+            },
+            custom_options: function() {
+              // We will add all the columns entered by user in table model
+              var columns = self.model.top.get('columns'),
+                  added_columns_from_tables = [];
+
+              if (columns.length > 0) {
+                _.each(columns.models, function(m) {
+                    var col = m.get('name');
+                    if(!_.isUndefined(col) && !_.isNull(col)) {
+                      added_columns_from_tables.push(
+                        {label: col, value: col, image:'icon-column'}
+                      );
+                    }
+                });
+              }
+              // Set the values in to options so that user can select
+              this.field.set('options', added_columns_from_tables);
+            },
+            template: headerSelectControlTemplate,
+            remove: function () {
+              if(self.model.handler) {
+                var tableCols = self.model.top.get('columns');
+                this.stopListening(tableCols, 'remove' , this.removeColumn);
+                this.stopListening(tableCols, 'change:name' , this.resetColOptions);
+
+                Backform.Select2Control.prototype.remove.apply(this, arguments);
+
+              } else {
+                Backform.NodeListByNameControl.prototype.remove.apply(this, arguments);
+              }
+            }
+          }),
+          select2: {
+            allowClear: false, width: 'style',
+            placeholder: 'Select column'
+          }, first_empty: !_.isUndefined(self.model.get('oid')),
+          version_compatible: self.field.get('version_compatible'),
+          disabled: function(m) {
+            return !_.isUndefined(self.model.get('oid'));
+          }
+        },{
+          id: 'references', label:'', type: 'text', cache_level: 'server',
+          select2: {
+            allowClear: false, width: 'style',
+            placeholder: 'Select foreign table',
+          }, first_empty: true,
+          control: Backform.NodeListByNameControl.extend({
+            formatter: Backform.ControlFormatter,
+            template: headerSelectControlTemplate
+            }),
+          url: 'all_tables', node: 'table',
+          version_compatible: self.field.get('version_compatible'),
+          disabled: function(m) {
+            return !_.isUndefined(self.model.get('oid'));
+          },
+          transform: function(rows) {
+            var res = [];
+            _.each(rows, function(r) {
+              res.push({
+                'value': r.value,
+                'image': "icon-table",
+                'label': r.label
+              });
+            });
+            return res;
+          }
+        },{
+          id: 'referenced', label:'', type: 'text', cache_level: 'server',
+          transform: function(rows) {
+            var res = [];
+            _.each(rows, function(r) {
+              res.push({
+                'value': r.name,
+                'image': 'icon-column',
+                'label': r.name
+              });
+            });
+            return res;
+          },
+          control: Backform.Select2Control.extend({
+            formatter: Backform.ControlFormatter,
+            template: headerSelectControlTemplate,
+            render: function() {
+              var self = this,
+              url = self.field.get('url') || self.defaults.url,
+              m = self.model,
+              tid = m.get('references');
+
+              // Clear any existing value before setting new options.
+              m.set(self.field.get('name'), null, {silent: true});
+
+              if (url && !_.isUndefined(tid) && !_.isNull(tid) && tid != '') {
+                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
+                      ]),
+                    data = [];
+
+                if (this.field.get('version_compatible')) {
+                  m.trigger('pgadmin:view:fetching', m, self.field);
+                  $.ajax({
+                    async: false,
+                    data : {tid:tid},
+                    url: full_url,
+                    success: function(res) {
+                      data = res.data;
+                    },
+                    error: function() {
+                      m.trigger('pgadmin:view:fetch:error', m, self.field);
+                    }
+                  });
+                  m.trigger('pgadmin:view:fetched', m, self.field);
+                }
+                /*
+                 * Transform the data
+                 */
+                transform = this.field.get('transform') || self.defaults.transform;
+                if (transform && _.isFunction(transform)) {
+                  // We will transform the data later, when rendering.
+                  // It will allow us to generate different data based on the
+                  // dependencies.
+                  self.field.set('options', transform.bind(self, data));
+                } else {
+                  self.field.set('options', data);
+                }
+              } else {
+                self.field.set('options', []);
+              }
+              Backform.Select2Control.prototype.render.apply(this, arguments);
+              return this;
+            }
+          }), url: 'get_columns', first_empty: true,
+          select2: {
+            width: "style",
+            placeholder: 'Select column',
+            templateResult: formatNode,
+            templateSelection: formatNode
+          },
+          deps:['references'],  node: 'table',
+          version_compatible: self.field.get('version_compatible'),
+          disabled: function(m) {
+            return !_.isUndefined(self.model.get('oid'));
+          }
+        }],
+        headerDefaults = {local_column: null,
+                          references: null,
+                          referenced:null},
+        gridCols = ['local_column', 'references', 'referenced'];
+
+      if ((!self.model.isNew() && _.isUndefined(self.model.handler)) ||
+          (_.has(self.model, 'handler') &&
+          !_.isUndefined(self.model.handler) &&
+          !_.isUndefined(self.model.get('oid')))) {
+        var column = self.collection.first();
+        if (column) {
+          headerDefaults["references"] = column.get("references");
+        }
+      }
+
+      self.headerData = new (Backbone.Model.extend({
+        defaults: headerDefaults,
+        schema: headerSchema
+      }))({});
+
+      var headerGroups = Backform.generateViewSchema(
+          self.field.get('node_info'), self.headerData, 'create',
+          node, self.field.get('node_data')
+          ),
+          fields = [];
+
+      _.each(headerGroups, function(o) {
+        fields = fields.concat(o.fields);
+      });
+
+      self.headerFields = new Backform.Fields(fields);
+      self.gridSchema = Backform.generateGridColumnsFromModel(
+          //null, ForeignKeyColumnModel, 'edit', gridCols
+          self.field.get('node_info'), self.field.get('model'), 'edit',
+          gridCols, self.field.get('schema_node')
+          );
+
+      self.controls = [];
+      self.listenTo(self.headerData, "change", self.headerDataChanged);
+      self.listenTo(self.headerData, "select2", self.headerDataChanged);
+      self.listenTo(self.collection, "add", self.onAddorRemoveColumns);
+      self.listenTo(self.collection, "remove", self.onAddorRemoveColumns);
+    },
+
+    generateHeader: function(data) {
+      var header = [
+        '<div class="subnode-header-form">',
+        ' <div class="container-fluid">',
+        '  <div class="row">',
+        '   <div class="col-md-4">',
+        '    <label class="control-label"><%-column_label%></label>',
+        '   </div>',
+        '   <div class="col-md-4" header="local_column"></div>',
+        '   <div class="col-md-4">',
+        '     <button class="btn-sm btn-default add" <%=canAdd ? "" : "disabled=\'disabled\'"%> ><%-add_label%></buttton>',
+        '   </div>',
+        '  </div>',
+        '  <div class="row">',
+        '   <div class="col-md-4">',
+        '    <label class="control-label"><%-references_label%></label>',
+        '   </div>',
+        '   <div class="col-md-4" header="references"></div>',
+        '  </div>',
+        '  <div class="row">',
+        '   <div class="col-md-4">',
+        '    <label class="control-label"><%-referenced_label%></label>',
+        '   </div>',
+        '   <div class="col-md-4" header="referenced"></div>',
+        '  </div>',
+        ' </div>',
+        '</div>',].join("\n")
+
+      _.extend(data, {
+        column_label: '{{ _('Local column')}}',
+        add_label: '{{ _('ADD')}}',
+        references_label: '{{ _('References')}}',
+        referenced_label: '{{ _('Referencing')}}'
+      });
+
+      var self = this,
+          headerTmpl = _.template(header),
+          $header = $(headerTmpl(data)),
+          controls = this.controls;
+
+      this.headerFields.each(function(field) {
+        var control = new (field.get("control"))({
+          field: field,
+          model: self.headerData
+        });
+
+        $header.find('div[header="' + field.get('name') + '"]').append(
+          control.render().$el
+        );
+
+        controls.push(control);
+      });
+
+      // We should not show add but in properties mode
+      if (data.mode == 'properties') {
+        $header.find("button.add").remove();
+      }
+
+      self.$header = $header;
+
+      return $header;
+    },
+
+    events: _.extend(
+                {}, Backform.UniqueColCollectionControl.prototype.events,
+                {'click button.add': 'addColumns'}
+                ),
+
+    showGridControl: function(data) {
+
+      var self = this,
+          titleTmpl = _.template([
+            "<div class='subnode-header'>",
+            "<label class='control-label'><%-label%></label>",
+            "</div>"].join("\n")),
+          $gridBody =
+            $("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode'></div>").append(
+              titleTmpl({label: data.label})
+            );
+
+      $gridBody.append(self.generateHeader(data));
+
+      var gridSchema = _.clone(this.gridSchema);
+
+      // Insert Delete Cell into Grid
+      if (data.disabled == false && data.canDelete) {
+          gridSchema.columns.unshift({
+            name: "pg-backform-delete", label: "",
+            cell: Backgrid.Extension.DeleteCell,
+            editable: false, cell_priority: -1
+          });
+      }
+
+      // Initialize a new Grid instance
+      var grid = self.grid = new Backgrid.Grid({
+        columns: gridSchema.columns,
+        collection: self.collection,
+        className: "backgrid table-bordered"
+      });
+      self.$grid = grid.render().$el;
+
+      $gridBody.append(self.$grid);
+
+      setTimeout(function() {
+        self.headerData.set({
+          'local_column':
+          self.$header.find(
+            'div[header="local_column"] select option:first'
+            ).val(),
+            'referenced':
+          self.$header.find(
+            'div[header="referenced"] select option:first'
+            ).val(),
+            'references':
+          self.$header.find(
+            'div[header="references"] select option:first'
+            ).val()
+            }, {silent:true}
+          );
+      }, 10);
+
+      // Render node grid
+      return $gridBody;
+    },
+
+    headerDataChanged: function() {
+      var self = this, val,
+          data = this.headerData.toJSON(),
+          inSelected = false,
+          checkVars = ['local_column', 'referenced'];
+
+      if (!self.$header) {
+        return;
+      }
+
+      if (self.control_data.canAdd) {
+        self.collection.each(function(m) {
+          if (!inSelected) {
+            _.each(checkVars, function(v) {
+              if (!inSelected) {
+                val = m.get(v);
+                inSelected = ((
+                  (_.isUndefined(val) || _.isNull(val)) &&
+                  (_.isUndefined(data[v]) || _.isNull(data[v]))
+                  ) ||
+                  (val == data[v]));
+              }
+            });
+          }
+        });
+      }
+      else {
+        inSelected = true;
+      }
+
+      self.$header.find('button.add').prop('disabled', inSelected);
+    },
+
+    addColumns: function(ev) {
+      ev.preventDefault();
+      var self = this,
+          local_column = self.headerData.get('local_column'),
+          referenced = self.headerData.get('referenced');
+
+      if (!local_column || local_column == '' ||
+          !referenced || referenced  =='') {
+        return false;
+      }
+
+      var m = new (self.field.get('model'))(
+                self.headerData.toJSON()),
+          coll = self.model.get(self.field.get('name'));
+
+      coll.add(m);
+
+      var idx = coll.indexOf(m);
+
+      // idx may not be always > -1 because our UniqueColCollection may
+      // remove 'm' if duplicate value found.
+      if (idx > -1) {
+        self.$grid.find('.new').removeClass('new');
+
+        var newRow = self.grid.body.rows[idx].$el;
+
+        newRow.addClass("new");
+        $(newRow).pgMakeVisible('backform-tab');
+      } else {
+        delete m;
+      }
+
+      return false;
+    },
+
+    onAddorRemoveColumns: function() {
+      var self = this;
+
+      // Wait for collection to be updated before checking for the button to be
+      // enabled, or not.
+      setTimeout(function() {
+        if (self.collection.length > 0) {
+          self.$header.find(
+            'div[header="references"] select'
+            ).prop('disabled', true);
+        } else {
+          self.$header.find(
+            'div[header="references"] select'
+            ).prop('disabled', false);
+        }
+
+        self.collection.trigger('pgadmin:columns:updated', self.collection);
+
+        self.headerDataChanged();
+
+        if ((!_.has(self.model, 'handler') || (_.has(self.model, 'handler') &&
+              _.isUndefined(self.model.handler))) ||
+            (_.has(self.model, 'handler') && !_.isUndefined(self.model.handler) &&
+            !_.isUndefined(self.model.handler.get('oid')))) {
+          self.getCoveringIndex();
+        }
+
+      }, 10);
+    },
+
+    getCoveringIndex: function() {
+
+      var self = this,
+          url = 'get_coveringindex',
+          m = self.model
+          cols = [],
+          coveringindex = null;
+
+          self.collection.each(function(m){
+            cols.push(m.get('local_column'));
+          })
+
+          if (cols.length > 0) {
+            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
+                ]);
+
+            if (this.field.get('version_compatible')) {
+              m.trigger('pgadmin:view:fetching', m, self.field);
+              $.ajax({
+                async: false,
+                data : {cols:JSON.stringify(cols)},
+                url: full_url,
+                success: function(res) {
+                  coveringindex = res.data;
+                },
+                error: function() {
+                  m.trigger('pgadmin:view:fetch:error', m, self.field);
+                }
+              });
+              m.trigger('pgadmin:view:fetched', m, self.field);
+            }
+          }
+
+          if (coveringindex) {
+            m.set('hasindex', true);
+            m.set('autoindex', false);
+            m.set('coveringindex', coveringindex);
+          } else {
+            m.set('coveringindex', null);
+            m.set('autoindex', true);
+            m.set('hasindex', false);
+          }
+    },
+
+    remove: function() {
+      /*
+       * Stop listening the events registered by this control.
+       */
+      this.stopListening(this.headerData, "change", this.headerDataChanged);
+      this.listenTo(this.headerData, "select2", this.headerDataChanged);
+      this.listenTo(this.collection, "remove", this.onRemoveVariable);
+      // Remove header controls.
+      _.each(this.controls, function(controls) {
+        controls.remove();
+      });
+
+      ForeignKeyColumnControl.__super__.remove.apply(this, arguments);
+
+      // Remove the header model
+      delete (this.headerData);
+
+    }
+  });
+
+  // Extend the browser's node class for foreign key node
+  if (!pgBrowser.Nodes['foreign_key']) {
+    pgAdmin.Browser.Nodes['foreign_key'] = pgBrowser.Node.extend({
+      type: 'foreign_key',
+      label: '{{ _('Foreign key') }}',
+      collection_type: 'coll-constraints',
+      sqlAlterHelp: 'ddl-alter.html',
+      sqlCreateHelp: 'ddl-constraints.html',
+      hasSQL: true,
+      hasDepends: false,
+      parent_type: 'table',
+      canDrop: true,
+      canDropCascade: true,
+      hasDepends: true,
+      Init: function() {
+        /* Avoid multiple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_foreign_key_on_coll', node: 'coll-constraints', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Foreign key...') }}',
+          icon: 'wcTabIcon icon-foreign_key', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'validate_foreign_key', node: 'foreign_key', module: this,
+          applies: ['object', 'context'], callback: 'validate_foreign_key',
+          category: 'validate', priority: 4, label: '{{ _('Validate foreign key') }}',
+          icon: 'fa fa-link', enable : 'is_not_valid'
+        }
+        ]);
+      },
+      is_not_valid: function(node) {
+        return (node && !node.valid);
+      },
+      callbacks: {
+        validate_foreign_key: function(args) {
+          var input = args || {};
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+          if (!d) {
+            return false;
+          }
+          var data = d;
+          $.ajax({
+            url: obj.generate_url(i, 'validate', d, true),
+            type:'GET',
+            success: function(res) {
+              if (res.success == 1) {
+                Alertify.success("{{ _('" + res.info + "') }}");
+                t.removeIcon(i);
+                data.valid = true;
+                data.icon = 'icon-foreign_key';
+                t.addIcon(i, {icon: data.icon});
+                setTimeout(function() {t.deselect(i);}, 10);
+                setTimeout(function() {t.select(i);}, 100);
+              }
+            },
+            error: function(xhr, status, error) {
+              try {
+                var err = $.parseJSON(xhr.responseText);
+                if (err.success == 0) {
+                  msg = S('{{ _(' + err.errormsg + ')}}').value();
+                  Alertify.error("{{ _('" + err.errormsg + "') }}");
+                }
+              } catch (e) {}
+              t.unload(i);
+            }
+            });
+
+          return false;
+        }
+      },
+      // Define the model for foreign key node
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          comment: undefined,
+          condeferrable: undefined,
+          condeferred: undefined,
+          confmatchtype: undefined,
+          convalidated: undefined,
+          columns: undefined,
+          confupdtype: "a",
+          confdeltype: "a",
+          autoindex: true,
+          coveringindex: undefined,
+          hasindex:undefined
+        },
+        toJSON: function () {
+          var d = pgAdmin.Browser.Node.Model.prototype.toJSON.apply(this, arguments);
+          delete d.hasindex;
+          return d;
+        },
+        // Define the schema for the foreign key node
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', type: 'text',
+          mode: ['properties', 'create', 'edit'], editable:true,
+          headerCell: Backgrid.Extension.CustomHeaderCell, cellHeaderClasses: 'width_percent_50'
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'comment', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          deps:['name'], disabled:function(m) {
+            var name = m.get('name');
+            if (!(name && name != '')) {
+              setTimeout(function(){
+                m.set('comment', null);
+              },10);
+              return true;
+            } else {
+              return false;
+            }
+          }
+        },{
+          id: 'condeferrable', label: '{{ _('Deferrable') }}',
+          type: 'switch', group: '{{ _('Definition') }}',
+          disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+            // We can't update condeferrable of existing foreign key.
+            return !m.isNew();
+          }
+        },{
+          id: 'condeferred', label: '{{ _('Deferred') }}',
+          type: 'switch', group: '{{ _('Definition') }}',
+          deps: ['condeferrable'],
+          disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            } else if(!m.isNew()) {
+              return true;
+            }
+            // Disable if condeferred is false or unselected.
+            if(m.get('condeferrable') == true) {
+              return false;
+            } else {
+              setTimeout(function(){
+                m.set('condeferred', false);
+              },10);
+              return true;
+            }
+          }
+        },{
+          id: 'confmatchtype', label: '{{ _('Match type') }}',
+          type: 'switch', group: '{{ _('Definition') }}',
+          options: {
+            onText: 'FULL',
+            offText: 'SIMPLE',
+          },disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+            // We can't update condeferred of existing foreign key.
+            return !m.isNew();
+          }
+        },{
+          id: 'convalidated', label: "{{ _("Validated?") }}",
+          type: 'switch', group: '{{ _('Definition') }}',
+          options: {
+            onText: 'Yes',
+            offText: 'No'
+          },disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed
+                return !(_.isUndefined(m.get('oid')) || m.get("convalidated"));
+            }
+            // We can't update condeferred of existing foreign key.
+            return !(m.isNew() || m.get("convalidated"));
+          }
+        },{
+          id: 'autoindex', label: '{{ _('Auto FK index') }}',
+          type: 'switch', group: '{{ _('Definition') }}',
+          deps: ['name', 'hasindex'],
+          options: {
+            onText: 'Yes',
+            offText: 'No',
+          },disabled: function(m) {
+            var index = m.get('coveringindex'),
+                autoindex = m.get('autoindex'),
+                setIndexName = function() {
+                  var name = m.get('name'),
+                  oldindex = 'fki_'+m.previous ('name');
+
+                  if (m.get('hasindex')) {
+                    return true;
+                  } else if (m.get('autoindex') && !_.isUndefined(name) && !_.isNull(name) &&
+                              name != '' && (_.isUndefined(index) || _.isNull(index) ||
+                              index == '' || index == oldindex)) {
+                    var newIndex = 'fki_' + name;
+                    m.set('coveringindex', newIndex);
+                    return false;
+                  } else {
+                    return false;
+                  }
+                };
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                if(_.isUndefined(m.get('oid')) && _.isUndefined(m.handler.get('oid'))) {
+                  setTimeout(function () {
+                    m.set('autoindex', false);
+                  }, 10);
+                  return true;
+                } else {
+                  return setIndexName();
+                }
+            } else {
+              if(!m.isNew() && autoindex && !_.isUndefined(index) &&
+                    !_.isNull(index) && index != '' && m.get('hasindex')) {
+                return true;
+              } else {
+                return setIndexName();
+              }
+            }
+          }
+        },{
+          id: 'coveringindex', label: '{{ _('Covering index') }}', type: 'text',
+          mode: ['properties', 'create', 'edit'], group: '{{ _('Definition') }}',
+          deps:['autoindex', 'hasindex'],
+          disabled: function(m) {
+            var index = m.get('coveringindex'),
+              setIndexName = function() {
+                if (m.get('hasindex')) {
+                  return true;
+                } else if (!m.get('autoindex')) {
+                  setTimeout(function () {
+                    m.set('coveringindex', null);
+                  });
+                  return true;
+                } else {
+                  setTimeout(function () {
+                    var name = m.get('name'),
+                      newIndex = 'fki_' + name;
+
+                      if (m.get('autoindex') && !_.isUndefined(name) && !_.isNull(name) &&
+                            name != '') {
+                        m.set('coveringindex', newIndex);
+                      }
+                  });
+
+                  return false;
+                }
+              };
+
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                if (_.isUndefined(m.get('oid')) && _.isUndefined(m.handler.get('oid'))) {
+                  return true;
+                } else {
+                  return setIndexName();
+                }
+
+            } else if (!m.isNew() && m.get('autoindex') && !_.isUndefined(index)
+                        && _.isNull(index) && index == '') {
+              return true;
+            }
+
+            return setIndexName();
+          }
+        },{
+          id: 'columns', label: '{{ _('Columns') }}',
+          type: 'collection', group: '{{ _('Columns') }}', disabled: false,
+          node: 'foreign_key', editable: false, headerCell: Backgrid.Extension.CustomHeaderCell,
+          cellHeaderClasses: 'width_percent_50',
+          cell: Backgrid.StringCell.extend({
+            initialize: function() {
+              Backgrid.StringCell.prototype.initialize.apply(this, arguments);
+              var self = this,
+                collection = this.model.get('columns');
+              // Do not listen for any event(s) for existing constraint.
+              if (_.isUndefined(self.model.get('oid'))) {
+                var tableCols = self.model.top.get('columns');
+                self.listenTo(tableCols, 'remove' , self.removeColumn);
+                self.listenTo(tableCols, 'change:name', self.resetColOptions);
+              }
+
+              self.model.get('columns').on('pgadmin:columns:updated', function() {
+                self.render.apply(self);
+              });
+              self.listenTo(collection, "add", self.render);
+              self.listenTo(collection, "remove", self.render);
+            },
+            removeColumn: function(m){
+              var self = this,
+                  removedCols = self.model.get('columns').where(
+                    {local_column: m.get('name')}
+                    );
+
+              self.model.get('columns').remove(removedCols);
+              setTimeout(function () {
+                self.render();
+              }, 10);
+
+              setTimeout(function () {
+                constraints = self.model.top.get("foreign_key");
+                var removed = [];
+                constraints.each(function(constraint) {
+                  if (constraint.get("columns").length == 0) {
+                     removed.push(constraint);
+                  }
+                });
+                constraints.remove(removed);
+              },100);
+            },
+            resetColOptions : function(m) {
+                var self = this,
+                  updatedCols = self.model.get('columns').where(
+                  {"local_column": m.previous('name')}
+                  );
+                if (updatedCols.length > 0) {
+                  /*
+                   * Table column name has changed so update
+                   * column name in foreign key as well.
+                   */
+                  updatedCols[0].set(
+                  {"local_column": m.get('name')});
+                }
+
+                setTimeout(function () {
+                  self.render();
+                }, 10);
+            },
+            formatter: {
+              fromRaw: function (rawValue, model) {
+                var cols = [],
+                    remote_cols = [];
+                if (rawValue.length > 0) {
+                  rawValue.each(function(col){
+                    cols.push(col.get('local_column'));
+                    remote_cols.push(col.get('referenced'))
+                  });
+                  return '('+cols.join(', ')+') -> ('+ remote_cols.join(', ')+')';
+                }
+                return "";
+              },
+              toRaw: function (val, model) {
+                return val;
+              }
+            },
+            render: function() {
+              return Backgrid.StringCell.prototype.render.apply(this, arguments);
+            },
+            remove: function() {
+              var tableCols = this.model.top.get('columns');
+
+              this.stopListening(tableCols, 'remove' , self.removeColumn);
+              this.stopListening(tableCols, 'change:name' , self.resetColOptions);
+
+              Backgrid.StringCell.prototype.remove.apply(this, arguments);
+            }
+          }),
+          canAdd: function(m) {
+            // We can't update columns of existing foreign key.
+            return m.isNew();
+          }, canDelete: true,
+          control: ForeignKeyColumnControl,
+          model: ForeignKeyColumnModel,
+          disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+            // We can't update columns of existing foreign key.
+            return !m.isNew();
+            }
+        },{
+          id: 'confupdtype', label:'{{ _('On update') }}',
+          type:"select2", group: '{{ _('Action') }}', mode: ['edit','create'],
+          select2:{width:"50%", allowClear: false},
+          options: [
+            {label: "NO ACTION", value: "a"},
+            {label: "RESTRICT", value: "r"},
+            {label: "CASCADE", value: "c"},
+            {label: "SET NULL", value: "n"},
+            {label: "SET DEFAULT", value: "d"}
+          ],disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+            // We can't update confupdtype of existing foreign key.
+            return !m.isNew();
+          }
+        },{
+          id: 'confdeltype', label:'{{ _('On delete') }}',
+          type:"select2", group: '{{ _('Action') }}', mode: ['edit','create'],
+          select2:{width:"50%", allowClear: false},
+          options: [
+            {label: "NO ACTION", value: "a"},
+            {label: "RESTRICT", value: "r"},
+            {label: "CASCADE", value: "c"},
+            {label: "SET NULL", value: "n"},
+            {label: "SET DEFAULT", value: "d"}
+          ],disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+            // We can't update confdeltype of existing foreign key.
+            return !m.isNew();
+          }
+        }
+        ],
+        validate: function() {
+          this.errorModel.clear();
+
+          var columns = this.get('columns');
+          if ((_.isUndefined(columns) || _.isNull(columns) || columns.length < 1)) {
+            var msg = '{{ _('Please specify columns for Foreign key.') }}';
+            this.errorModel.set('columns', msg);
+            return msg;
+          }
+
+          var coveringindex = this.get('coveringindex'),
+              autoindex = this.get('autoindex');
+          if (autoindex && (_.isUndefined(coveringindex) || _.isNull(coveringindex) ||
+            String(coveringindex).replace(/^\s+|\s+$/g, '') == '')) {
+            var msg = '{{ _('Please specify covering index name.') }}';
+            this.errorModel.set('coveringindex', msg);
+            return msg;
+          }
+
+          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, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to c reate table
+            if (_.indexOf(['schema'], d._type) > -1)
+              return true;
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1) {
+            return false;
+          } else {
+            return true;
+          }
+      }
+    });
+  }
+
+  return pgBrowser.Nodes['foreign_key'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py
new file mode 100644
index 0000000..471a71e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py
@@ -0,0 +1,878 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements Primary key constraint Node"""
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext as _
+from pgadmin.utils.ajax import make_json_response, \
+    make_response as ajax_response, internal_server_error
+from pgadmin.browser.utils import PGChildNodeView
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+import pgadmin.browser.server_groups.servers.databases as database
+from functools import wraps
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.constraints.type \
+    import ConstraintRegistry, ConstraintTypeModule
+
+
+class IndexConstraintModule(ConstraintTypeModule):
+    """
+    class IndexConstraintModule(CollectionNodeModule)
+
+        A module class for Primary key constraint node derived from ConstraintTypeModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the PrimaryKeyConstraintModule and it's base module.
+
+    * get_nodes(gid, sid, did)
+      - Method is used to generate the browser collection node.
+
+    * node_inode()
+      - Method is overridden from its base class to make the node as leaf node.
+
+    * script_load()
+      - Load the module script for language, when any of the database node is
+        initialized.
+    """
+
+    NODE_TYPE = 'Index constraint'
+    COLLECTION_LABEL = _('index_constraint')
+
+    def __init__(self, *args, **kwargs):
+        """
+        Method is used to initialize the PrimaryKeyConstraintModule and it's base module.
+
+        Args:
+          *args:
+          **kwargs:
+
+        Returns:
+
+        """
+        self.min_ver = None
+        self.max_ver = None
+        super(IndexConstraintModule, self).__init__(*args, **kwargs)
+
+    def get_nodes(self, gid, sid, did, scid, tid):
+        """
+        Generate the collection node
+        """
+        pass
+
+    @property
+    def node_inode(self):
+        """
+        Override this property to make the node a leaf node.
+
+        Returns: False as this is the leaf node
+        """
+        return False
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for primary_key, when any of the table node is
+        initialized.
+
+        Returns: node type of the server module.
+        """
+        return database.DatabaseModule.NODE_TYPE
+
+
+class PrimaryKeyConstraintModule(IndexConstraintModule):
+    """
+     class PrimaryKeyConstraintModule(IndexConstraintModule)
+
+        A module class for the catalog schema node derived from IndexConstraintModule.
+    """
+
+    NODE_TYPE = 'primary_key'
+    COLLECTION_LABEL = _("Primary key")
+
+
+primary_key_blueprint = PrimaryKeyConstraintModule(__name__)
+
+
+class UniqueConstraintModule(IndexConstraintModule):
+    """
+     class UniqueConstraintModule(IndexConstraintModule)
+
+        A module class for the catalog schema node derived from IndexConstraintModule.
+    """
+
+    NODE_TYPE = 'unique_constraint'
+    COLLECTION_LABEL = _("Unique constraint")
+
+
+unique_constraint_blueprint = UniqueConstraintModule(__name__)
+
+
+class IndexConstraintView(PGChildNodeView):
+    """
+    class PrimaryKeyConstraintView(PGChildNodeView)
+
+        A view class for Primary key constraint node derived from PGChildNodeView. This class is
+        responsible for all the stuff related to view like creating, updating Primary key constraint
+        node, showing properties, showing sql in sql pane.
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the PrimaryKeyConstraintView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * list()
+      - This function returns primary key constraint nodes within that
+        collection as http response.
+
+    * get_list()
+      - This function is used to list all the language nodes within that collection
+        and return list of primary key constraint nodes.
+
+    * nodes()
+      - This function returns child node within that collection.
+        Here return all primary key constraint node as http response.
+
+    * get_nodes()
+      - returns all primary key constraint nodes' list.
+
+    * properties()
+      - This function will show the properties of the selected primary key.
+
+    * update()
+      - This function will update the data for the selected primary key.
+
+    * msql()
+      - This function is used to return modified SQL for the selected primary key.
+
+    * get_sql()
+      - This function will generate sql from model data.
+
+    * sql():
+      - This function will generate sql to show it in sql pane for the selected primary key.
+
+    * get_indices():
+        - This function returns indices for current table.
+
+    """
+
+    node_type = 'index_constraint'
+
+    node_label = _('Index constraint')
+
+    parent_ids = [
+            {'type': 'int', 'id': 'gid'},
+            {'type': 'int', 'id': 'sid'},
+            {'type': 'int', 'id': 'did'},
+            {'type': 'int', 'id': 'scid'},
+            {'type': 'int', 'id': 'tid'}
+            ]
+    ids = [{'type': 'int', 'id': 'cid'}
+            ]
+
+    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'}]
+    })
+
+    def module_js(self):
+        """
+        This property defines (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        return make_response(
+                render_template(
+                    "index_constraint/js/index_constraint.js",
+                    _=_,
+                    node_type=self.node_type,
+                    node_label=self.node_label
+                    ),
+                200, {'Content-Type': 'application/x-javascript'}
+                )
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+                kwargs['sid']
+                )
+            self.conn = self.manager.connection(did=kwargs['did'])
+
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    _(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            self.template_path = 'index_constraint/sql'
+            # We need parent's name eg table name and schema name
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=kwargs['tid'])
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            for row in rset['rows']:
+                self.schema = row['schema']
+                self.table = row['table']
+            return f(*args, **kwargs)
+
+        return wrap
+
+    def end_transaction(self):
+        SQL = render_template(
+                "/".join([self.template_path, 'end.sql']))
+        # End transaction if any.
+        self.conn.execute_scalar(SQL)
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function is used to list all the primary key
+        nodes within that collection.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        sql = render_template("/".join([self.template_path, 'properties.sql']),
+                              tid=tid,
+                              cid=cid,
+                              constraint_type= self.constraint_type)
+        status, res = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        result = res['rows'][0]
+
+        sql = render_template(
+            "/".join([self.template_path, 'get_constraint_cols.sql']),
+            cid=cid,
+            colcnt=result['indnatts'])
+        status, res = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        columns = []
+        for row in res['rows']:
+            columns.append({"column":   row['column'].strip('"')})
+
+        result['columns'] = columns
+
+        return ajax_response(
+                response=result,
+                status=200
+                )
+
+    @check_precondition
+    def list(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function returns all primary keys
+        nodes within that collection as a http response.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        try:
+            res = self.get_node_list(gid, sid, did, scid, tid, cid)
+            return ajax_response(
+                    response=res,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_node_list(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function returns all primary keys
+        nodes within that collection as a list.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        SQL = render_template("/".join([self.template_path, 'properties.sql']),
+                              tid=tid,
+                              constraint_type= self.constraint_type)
+        status, res = self.conn.execute_dict(SQL)
+
+        return res['rows']
+
+    @check_precondition
+    def nodes(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function returns all event trigger nodes as a
+        http response.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        try:
+            res = self.get_nodes(gid, sid, did, scid, tid, cid)
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_nodes(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function returns all event trigger nodes as a list.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        res = []
+        SQL = render_template("/".join([self.template_path, 'nodes.sql']),
+                              tid=tid,
+                              constraint_type=self.constraint_type)
+        status, rset = self.conn.execute_2darray(SQL)
+
+        for row in rset['rows']:
+            res.append(
+                    self.blueprint.generate_browser_node(
+                        row['oid'],
+                        tid,
+                        row['name'],
+                        icon="icon-%s" % self.node_type
+                    ))
+        return res
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function will create a primary key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        required_args = [
+            [u'columns', u'index']    # Either of one should be there.
+        ]
+
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        for k, v in data.items():
+            try:
+                data[k] = json.loads(v)
+            except (ValueError, TypeError):
+                data[k] = v
+
+        for arg in required_args:
+            if isinstance(arg, list):
+                for param in arg:
+                    if (param in data and
+                            (not isinstance(data[param], list) or
+                                (isinstance(data[param], list) and
+                                    len(data[param]) > 0))):
+                        break
+                else:
+                    return make_json_response(
+                      status=400,
+                      success=0,
+                      errormsg=_(
+                          "Couldn't find at least one required parameter (%s)." % str(param)
+                      )
+                    )
+
+            elif arg not in data:
+                return make_json_response(
+                    status=400,
+                    success=0,
+                    errormsg=_(
+                        "Couldn't find the required parameter (%s)." % arg
+                    )
+                )
+
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            if 'name' not in data or data['name'] == "":
+                SQL = render_template(
+                    "/".join([self.template_path, 'begin.sql']))
+                # Start transaction.
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+            # The below SQL will execute CREATE DDL only
+            SQL = render_template(
+                "/".join([self.template_path, 'create.sql']),
+                data=data, conn=self.conn,
+                constraint_name=self.constraint_name
+                )
+
+            status, msg = self.conn.execute_scalar(SQL)
+            if not status:
+                self.end_transaction()
+                return internal_server_error(errormsg=msg)
+
+            if 'name' not in data or data['name'] == "":
+                sql = render_template(
+                    "/".join([self.template_path,
+                              'get_oid_with_transaction.sql'],
+                             ),
+                    constraint_type=self.constraint_type,
+                    tid=tid)
+
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+                self.end_transaction()
+
+                data['name'] = res['rows'][0]['name']
+
+            else:
+                sql = render_template("/".join([self.template_path, 'get_oid.sql']),
+                                      tid=tid,
+                                      constraint_type=self.constraint_type,
+                                      name=data['name'])
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+            sql = render_template("/".join([self.template_path, 'alter.sql']),
+                                  data=data,
+                                  conn=self.conn)
+            sql = sql.strip('\n').strip(' ')
+
+            if sql != '':
+                status, result = self.conn.execute_scalar(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=result)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    res['rows'][0]['oid'],
+                    tid,
+                    data['name'],
+                    icon="icon-%s" % self.node_type
+                    )
+                )
+
+        except Exception as e:
+            self.end_transaction()
+            return make_json_response(
+                status=400,
+                success=0,
+                errormsg=e
+            )
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function will update the data for the selected
+        primary key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        try:
+            data['schema'] = self.schema
+            data['table'] = self.table
+            sql = self.get_sql(data, tid, cid)
+            sql = sql.strip('\n').strip(' ')
+            if sql != "":
+                status, res = self.conn.execute_scalar(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                sql = render_template("/".join([self.template_path, 'get_oid.sql']),
+                                      tid=tid,
+                                      constraint_type=self.constraint_type,
+                                      name=data['name'])
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                return make_json_response(
+                    success=1,
+                    info="Constraint updated",
+                    data={
+                        'id': cid,
+                        'tid': tid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': cid,
+                        'tid': tid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function will delete an existing primary key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        # Below code will decide if it's simple drop or drop with cascade call
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+        try:
+            sql = render_template("/".join([self.template_path, 'get_name.sql']),
+                                  tid=tid,
+                                  constraint_type = self.constraint_type,
+                                  cid=cid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = res['rows'][0]
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            sql = render_template("/".join([self.template_path, 'delete.sql']),
+                                  data=data,
+                                  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=_("{0} dropped.".format(self.node_label)),
+                data={
+                    'id': cid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function returns modified SQL for the selected
+        primary key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        data = {}
+        for k, v in request.args.items():
+            try:
+                data[k] = json.loads(v)
+            except ValueError:
+                data[k] = v
+
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            sql = self.get_sql(data, tid, cid)
+            sql = sql.strip('\n').strip(' ')
+
+            return make_json_response(
+                    data=sql,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def get_sql(self, data, tid, cid=None):
+        """
+        This function will generate sql from model data.
+
+        Args:
+          data: Contains the data of the selected primary key constraint.
+          tid: Table ID.
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        if cid is not None:
+            sql = render_template("/".join([self.template_path, 'properties.sql']),
+                                  tid=tid,
+                                  cid=cid,
+                                  constraint_type= self.constraint_type)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            old_data = res['rows'][0]
+            required_args = [u'name']
+            for arg in required_args:
+                if arg not in data:
+                    data[arg] = old_data[arg]
+
+            sql = render_template("/".join([self.template_path, 'update.sql']),
+                                  data=data,
+                                  o_data=old_data)
+        else:
+            required_args = [
+                [u'columns', u'index']    # Either of one should be there.
+            ]
+
+            for arg in required_args:
+                if isinstance(arg, list):
+                    for param in arg:
+                        if (param in data and
+                                ((isinstance(data[param], str) and
+                                    data[param] != "") or
+                                    (isinstance(data[param], list) and
+                                        len(data[param]) > 0))):
+                            break
+                    else:
+                        return _('-- definition incomplete')
+
+                elif arg not in data:
+                    return _('-- definition incomplete')
+
+            sql = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=data,
+                                  conn=self.conn,
+                                  constraint_name=self.constraint_name)
+            sql += "\n"
+            sql += render_template("/".join([self.template_path, 'alter.sql']),
+                                   data=data,
+                                   conn=self.conn)
+
+        return sql
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, tid, cid=None):
+        """
+        This function generates sql to show in the sql pane for the selected
+        primary key.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          cid: Primary key constraint ID
+
+        Returns:
+
+        """
+        try:
+            SQL = render_template(
+                "/".join([self.template_path, 'properties.sql']),
+                tid=tid,
+                conn=self.conn,
+                cid=cid,
+                constraint_type=self.constraint_type)
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = res['rows'][0]
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            sql = render_template(
+                "/".join([self.template_path, 'get_constraint_cols.sql']),
+                cid=cid, colcnt=data['indnatts'])
+
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            columns = []
+            for row in res['rows']:
+                columns.append({"column":   row['column'].strip('"')})
+
+            data['columns'] = columns
+
+            SQL = render_template(
+                "/".join([self.template_path, 'create.sql']),
+                data=data,
+                constraint_name=self.constraint_name)
+            SQL += "\n"
+            SQL += render_template(
+                "/".join([self.template_path, 'alter.sql']),
+                data=data, conn=self.conn)
+
+            sql_header = "-- Constraint: {0}\n\n-- ".format(data['name'])
+
+            sql_header += render_template(
+                    "/".join([self.template_path, 'delete.sql']),
+                    data=data)
+            sql_header += "\n"
+
+            SQL = sql_header + SQL
+
+            return ajax_response(response=SQL)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+class PrimaryKeyConstraintView(IndexConstraintView):
+    node_type = 'primary_key'
+
+    node_label = _('Primary key')
+
+    constraint_name = "PRIMARY KEY"
+
+    constraint_type = "p"
+
+
+class UniqueConstraintView(IndexConstraintView):
+    node_type = 'unique_constraint'
+
+    node_label = _('Unique constraint')
+
+    constraint_name = "UNIQUE"
+
+    constraint_type = "u"
+
+
+primary_key_constraint = ConstraintRegistry(
+    'primary_key', PrimaryKeyConstraintModule, PrimaryKeyConstraintView
+    )
+
+unique_constraint = ConstraintRegistry(
+    'unique_constraint', UniqueConstraintModule, UniqueConstraintView
+    )
+
+PrimaryKeyConstraintView.register_node_view(primary_key_blueprint)
+UniqueConstraintView.register_node_view(unique_constraint_blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/img/primary_key.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/img/primary_key.png
new file mode 100644
index 0000000000000000000000000000000000000000..b57f59778554c3b0ee1b872a4174cbe631ee64e2
GIT binary patch
literal 443
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf<N%)#S0Mf1eD~=McE?xh-aAwO
z?)k2F&$r(_Q+<4;&bQAOzI-_L>D`Vm@3!4NRdR5F#O>qdUq7Aw{C>xWm$N>;n)Uh3
z;xBI(e|bIY^Q(!UUNnAw(e(Yp+IP=)e*Jjj^V^Ldo=^DrV#4P)3vM4z`1*Fitz!|N
zUo?Mt)qC$u?bnY-Kfl}j@%4gFujk!8nS1w4)!Qd4-ac7$_f*mGm0D_V?`Q*^#aI&L
z7tG-B>_!@p!&%@FSq!8-z}W3%wjGcW@9E+gB5^r6VF6b{N>Y-`Gvm!0Hf=0cIJvo?
zsPOZLPmT=s%*?{88RjpTu%Tncj2$gYMC1b;qT2k!{Nfw~JtJL3mox?X#ySUk&+eD!
zaI9ft<CBfGtz|aJUirArf`Q@2TGkzB-UgQgtx_#<jVMV;EJ?LWE=mPb3`Pcq2D*ku
zx(0?JMg~?U##Sb#+6Jap1_txq>8(Q1kei>9nN|tWU|<Q>090*aWnc!;aB6z!8lVOS
MPgg&ebxsLQ07e(akpKVy

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/img/unique_constraint.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/img/unique_constraint.png
new file mode 100644
index 0000000000000000000000000000000000000000..e82857235ed1d5bb351b981c447ec3370d495fa9
GIT binary patch
literal 422
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}V}MVHE0AtpdP65|Z{ONGc?&PK
z9lkT?%wyl?6GdCEg|9qcv+MTqD^L55-OJi^HFW8P+MRbYmt2Wha=CWfUF*U_tvl~U
zF1%W|<$g;4`J&}FqUK+(-}In$+k@yiHybuQDqDIhZuaelb&vDr-HMxer)=5dRj1!9
zI{IqDj_1wmp7v~dv0(q3HOJoP31mnB9l=-<<QL4~@a#q!ki%Kv5m^kRJ;2!QWVRiU
z5##CN7$R}Gx6hRCKmd<(eQa7<+K&H>+NGIBA*;;oEjPCQ(Bz!3V!CK3^T!Lkmz5Z{
zJ+895(X7z(qO)VNNwR|esV?<b%bs?wlaJ6nmU+y3!<yT5bHqK~Dx3X_HeV^s{Hn_4
zgbmOh)e_f;l9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!vrU}|MxFz=n-DijU5
n`6-!cl@JXEmS7D))h1R3W)KahriZQpYGCkm^>bP0l+XkKGQ6yC

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/templates/index_constraint/js/index_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/templates/index_constraint/js/index_constraint.js
new file mode 100644
index 0000000..b19d90f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/templates/index_constraint/js/index_constraint.js
@@ -0,0 +1,536 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+        'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+  // Extend the browser's node class for index constraint node
+  if (!pgBrowser.Nodes['{{node_type}}']) {
+    pgAdmin.Browser.Nodes['{{node_type}}'] = pgBrowser.Node.extend({
+      type: '{{node_type}}',
+      label: '{{ node_label }}',
+      collection_type: 'coll-constraints',
+      sqlAlterHelp: 'ddl-alter.html',
+      sqlCreateHelp: 'ddl-constraints.html',
+      hasSQL: true,
+      hasDepends: false,
+      parent_type: 'table',
+      canDrop: true,
+      canDropCascade: true,
+      Init: function() {
+        /* Avoid multiple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_{{node_type}}_on_coll', node: 'coll-constraints', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ node_label }}',
+          icon: 'wcTabIcon icon-{{node_type}}', data: {action: 'create', check: true},
+          enable: 'canCreate'
+
+        }
+        ]);
+      },
+      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, parents = [];
+        // To iterate over tree to check parent node
+        while (i) {
+          // If it is schema then allow user to c reate table
+          if (_.indexOf(['schema'], d._type) > -1) {
+            {% if node_type == 'primary_key' %}
+            // There should be only one primary key per table.
+            var children = t.children(arguments[1], false),
+              primary_key_found = false;
+
+            _.each(children, function(child){
+              data = pgBrowser.tree.itemData($(child));
+              if (!primary_key_found && data._type == "primary_key") {
+                primary_key_found = true;
+              }
+            });
+            return !primary_key_found;
+            {% else %}
+            return true;
+            {% endif %}
+          }
+          parents.push(d._type);
+          i = t.hasParent(i) ? t.parent(i) : null;
+          d = i ? t.itemData(i) : null;
+        }
+        // If node is under catalog then do not allow 'create' menu
+        if (_.indexOf(parents, 'catalog') > -1) {
+          return false;
+        } else {
+            return true;
+        }
+      },
+
+      // Define the model for index constraint node
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          comment: undefined,
+          spcname: "pg_default",
+          index: undefined,
+          fillfactor: undefined,
+          condeferrable: undefined,
+          condeferred: undefined,
+          columns: []
+        },
+
+        // Define the schema for the index constraint node
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', type: 'text',
+          mode: ['properties', 'create', 'edit'], editable:true,
+          cellHeaderClasses:'width_percent_40',
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'text' , mode: ['properties'], editable: false,
+          cellHeaderClasses:'width_percent_20',
+        },{
+          id: 'comment', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          deps:['name'], disabled:function(m) {
+            var name = m.get('name');
+            if (!(name && name != '')) {
+              setTimeout(function(){
+                m.set('comment', null);
+              },10);
+              return true;
+            } else {
+              return false;
+            }
+          }
+        },{
+          id: 'columns', label: '{{ _('Columns') }}',
+          type: 'collection', group: '{{ _('Definition') }}',
+          editable: false,
+          cell: Backgrid.StringCell.extend({
+            initialize: function() {
+              Backgrid.StringCell.prototype.initialize.apply(this, arguments);
+
+              var self = this,
+                collection = this.model.get('columns');
+
+               // Do not listen for any event(s) for existing constraint.
+              if (_.isUndefined(self.model.get('oid'))) {
+                var tableCols = self.model.top.get('columns');
+                  self.listenTo(tableCols, 'remove' , self.removeColumn);
+                  self.listenTo(tableCols, 'change:name', self.resetColOptions);
+              }
+
+              collection.on('pgadmin:multicolumn:updated', function() {
+                self.render.apply(self);
+              });
+              self.listenTo(collection, "add", self.render);
+              self.listenTo(collection, "remove", self.render);
+            },
+            removeColumn: function(m) {
+              var self = this,
+                  removedCols = self.model.get('columns').where(
+                    {column: m.get('name')}
+                    );
+
+              self.model.get('columns').remove(removedCols);
+              setTimeout(function () {
+                self.render();
+              }, 10);
+
+              {% if node_type == 'primary_key' %}
+                var key = 'primary_key'
+              {% else %}
+                var key = 'unique_constraint'
+              {% endif %}
+
+              setTimeout(function () {
+                constraints = self.model.top.get(key);
+                var removed = [];
+                constraints.each(function(constraint) {
+                  if (constraint.get("columns").length == 0) {
+                     removed.push(constraint);
+                  }
+                });
+                constraints.remove(removed);
+              },100);
+
+            },
+            resetColOptions : function(m) {
+              var self = this,
+                updatedCols = self.model.get('columns').where(
+                {column: m.previous('name')}
+                );
+              if (updatedCols.length > 0) {
+                /*
+                 * Table column name has changed so update
+                 * column name in primary key as well.
+                 */
+                updatedCols[0].set(
+                {"column": m.get('name')},
+                {silent: true});
+              }
+
+              setTimeout(function () {
+                self.render();
+              }, 10);
+            },
+            formatter: {
+              fromRaw: function (rawValue, model) {
+                return rawValue.pluck("column").toString();
+              },
+              toRaw: function (val, model) {
+                return val;
+              }
+            },
+            render: function() {
+              return Backgrid.StringCell.prototype.render.apply(this, arguments);
+            },
+            remove: function() {
+              var tableCols = this.model.top.get('columns'),
+              primary_key_col = this.model.get('columns');
+
+              if (primary_key_col) {
+                primary_key_col.off('pgadmin:multicolumn:updated');
+              }
+
+              this.stopListening(tableCols, 'remove' , self.removeColumn);
+              this.stopListening(tableCols, 'change:name' , self.resetColOptions);
+
+              Backgrid.StringCell.prototype.remove.apply(this, arguments);
+            }
+          }),
+          canDelete: true, canAdd: true,
+          control: Backform.MultiSelectAjaxControl.extend({
+            formatter: {
+              fromRaw: function (rawData, model) {
+                var res = _.isObject(rawData) ?
+                      rawData : JSON.parse(rawData);
+
+                return _.pluck(res, 'column');
+              },
+              toRaw: function (formattedData, model) {
+                return formattedData;
+              }
+            },
+            defaults: _.extend(
+              {},
+              Backform.NodeListByNameControl.prototype.defaults,
+              {
+                select2: {
+                  multiple: true,
+                  allowClear: true,
+                  width: 'style',
+                  placeholder: '{{ _('Select the column(s)') }}',
+                }
+              }
+            ),
+            initialize: function() {
+            // Here we will decide if we need to call URL
+            // Or fetch the data from parent columns collection
+              var self = this;
+              if(this.model.handler) {
+                Backform.Select2Control.prototype.initialize.apply(this, arguments);
+                // Do not listen for any event(s) for existing constraint.
+                if (_.isUndefined(self.model.get('oid'))) {
+                  var tableCols = self.model.top.get('columns');
+                  self.listenTo(tableCols, 'remove' , self.resetColOptions);
+                  self.listenTo(tableCols, 'change:name', self.resetColOptions);
+                }
+
+                self.custom_options();
+              } else {
+                Backform.MultiSelectAjaxControl.prototype.initialize.apply(this, arguments);
+              }
+              self.model.get('columns').on('pgadmin:multicolumn:updated', function() {
+                  self.render.apply(self);
+                });
+            },
+            resetColOptions: function(m) {
+              var self = this;
+
+              setTimeout(function () {
+                self.custom_options();
+                self.render.apply(self);
+              }, 50);
+
+            },
+            custom_options: function() {
+              // We will add all the columns entered by user in table model
+              var columns = this.model.top.get('columns'),
+                added_columns_from_tables = [];
+
+              if (columns.length > 0) {
+                _.each(columns.models, function(m) {
+                    var col = m.get('name');
+                    if(!_.isUndefined(col) && !_.isNull(col)) {
+                      added_columns_from_tables.push(
+                        {label: col, value: col, image:'icon-column'}
+                      );
+                    }
+                });
+              }
+              // Set the values in to options so that user can select
+              this.field.set('options', added_columns_from_tables);
+            },
+            onChange: function(e) {
+              var self = this,
+                  model = this.model,
+                  $el = $(e.target),
+                  attrArr = this.field.get("name").split('.'),
+                  name = attrArr.shift(),
+                  path = attrArr.join('.'),
+                  vals = this.getValueFromDOM(),
+                  collection = model.get(name),
+                  removed = [];
+
+              this.stopListening(this.model, "change:" + name, this.render);
+
+              /*
+               * Iterate through all the values, and find out how many are already
+               * present in the collection.
+               */
+              collection.each(function(m) {
+                  var column = m.get('column'),
+                      idx = _.indexOf(vals, column);
+
+                  if (idx > -1) {
+                    vals.splice(idx, 1);
+                  } else {
+                    removed.push(column);
+                  }
+               });
+
+              /*
+               * Adding new values
+               */
+
+              _.each(vals, function(v) {
+                var m = new (self.field.get('model'))(
+                      {column: v}, { silent: true,
+                      top: self.model.top,
+                      collection: collection,
+                      handler: collection
+                    });
+
+                collection.add(m);
+              });
+
+              /*
+               * Removing unwanted!
+               */
+              _.each(removed, function(v) {
+                collection.remove(collection.where({column: v}));
+              });
+
+              this.listenTo(this.model, "change:" + name, this.render);
+            },
+            remove: function() {
+              if(this.model.handler) {
+                var self = this,
+                tableCols = self.model.top.get('columns');
+                self.stopListening(tableCols, 'remove' , self.resetColOptions);
+                self.stopListening(tableCols, 'change:name' , self.resetColOptions);
+                self.model.get('columns').off('pgadmin:multicolumn:updated');
+
+                Backform.Select2Control.prototype.remove.apply(this, arguments);
+
+              } else {
+                Backform.MultiSelectAjaxControl.prototype.remove.apply(this, arguments);
+              }
+            }
+          }),
+          deps: ['index'], node: 'column',
+          model: pgBrowser.Node.Model.extend({
+            defaults: {
+              column: undefined
+            },
+            validate: function() {
+              return null;
+           }
+          }),
+          transform : function(data){
+            var res = [];
+            if (data && _.isArray(data)) {
+              _.each(data, function(d) {
+                res.push({label: d.label, value: d.label, image:'icon-column'});
+              })
+            }
+            return res;
+          },
+          select2:{allowClear:false},
+          disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'top') && !_.isUndefined(m.top)
+                                    && !m.top.isNew()) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should be allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+
+            // We can't update columns of existing index constraint.
+            if (!m.isNew()) {
+                return true;
+            }
+            // Disable if index is selected.
+            var index = m.get('index');
+              if(_.isUndefined(index) || index == '') {
+                return false;
+              } else {
+                var col = m.get('columns');
+                col.reset();
+                return true;
+              }
+            }
+          },{
+          id: 'spcname', label: '{{ _('Tablespace') }}',
+          type: 'text', group: '{{ _('Definition') }}',
+          control: 'node-list-by-name', node: 'tablespace',
+          deps: ['index'],
+          select2:{allowClear:false},
+          filter: function(m) {
+            // Don't show pg_global tablespace in selection.
+            if (m.label == "pg_global") return false;
+            else return true;
+          },
+          disabled: function(m) {
+            // Disable if index is selected.
+            m = m.top || m;
+            var index = m.get('index');
+              if(_.isUndefined(index) || index == '') {
+                return false;
+              } else {
+                setTimeout(function(){
+                  m.set('spcname', '');
+                },10);
+                return true;
+              }
+          }
+        },{
+          id: 'index', label: '{{ _('Index') }}',
+          type: 'text', group: '{{ _('Definition') }}',
+          control: Backform.NodeListByNameControl.extend({
+          initialize:function() {
+            if (_.isUndefined(this.model.top)) {
+              Backform.NodeListByNameControl.prototype.initialize.apply(this,arguments);
+            } else {
+              Backform.Control.prototype.initialize.apply(this,arguments);
+            }
+          }
+          }),
+          select2:{allowClear:true}, node: 'index',
+          disabled: function(m) {
+            // If we are in table edit mode then disable it
+            if (_.has(m, 'top') && !_.isUndefined(m.top)
+                                    && !m.top.isNew()) {
+                return true;
+            }
+
+            // We can't update index of existing index constraint.
+            return !m.isNew();
+          },
+          // We will not show this field in Create Table mode
+          visible: function(m) {
+            return !_.isUndefined(m.top.node_info['table']);
+          }
+        },{
+          id: 'fillfactor', label: '{{ _('Fill factor') }}', deps: ['index'],
+          type: 'int', group: '{{ _('Definition') }}', allowNull: true,
+          disabled: function(m) {
+            // Disable if index is selected.
+            var index = m.get('index');
+              if(_.isUndefined(index) || index == '') {
+                return false;
+              } else {
+                setTimeout(function(){
+                  m.set('fillfactor', null);
+                },10);
+                return true;
+              }
+            }
+        },{
+          id: 'condeferrable', label: '{{ _('Deferrable') }}',
+          type: 'switch', group: '{{ _('Definition') }}', deps: ['index'],
+          disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'top') && !_.isUndefined(m.top)
+                                    && !m.top.isNew()) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+
+            // We can't update condeferrable of existing index constraint.
+            if (!m.isNew()) {
+                return true;
+            }
+            // Disable if index is selected.
+            var index = m.get('index');
+            if(_.isUndefined(index) || index == '') {
+              return false;
+            } else {
+              setTimeout(function(){
+                m.set('condeferrable', false);
+              },10);
+              return true;
+            }
+          }
+        },{
+          id: 'condeferred', label: '{{ _('Deferred') }}',
+          type: 'switch', group: '{{ _('Definition') }}',
+          deps: ['condeferrable'],
+          disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'top') && !_.isUndefined(m.top)
+                                    && !m.top.isNew()) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+
+            // We can't update condeferred of existing index constraint.
+            if (!m.isNew()) {
+              return true;
+            }
+            // Disable if condeferred is false or unselected.
+            if(m.get('condeferrable') == true) {
+              return false;
+            } else {
+              setTimeout(function(){
+                m.set('condeferred', false);
+              },10);
+              return true;
+            }
+          }
+        }
+        ],
+        validate: function() {
+          this.errorModel.clear();
+          // Clear parent's error as well
+          if (_.has(this, 'top')) {
+            this.top.errorModel.clear();
+          }
+
+          var columns = this.get('columns'),
+            index = this.get('index');
+
+            if ((_.isUndefined(index) || String(index).replace(/^\s+|\s+$/g, '') == '') &&
+                (_.isUndefined(columns) || _.isNull(columns) || columns.length < 1)) {
+              var msg = '{{ _('Please specify columns for ') }}' + '{{ node_label }}';
+              this.errorModel.set('columns', msg);
+              return msg;
+            }
+
+          return null;
+        }
+      })
+  });
+  }
+
+  return pgBrowser.Nodes['{{node_type}}'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/img/coll-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/img/coll-constraints.png
new file mode 100644
index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751
GIT binary patch
literal 314
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas
zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo
z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk
zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p
zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g|
XW)KahriZQpYGCkm^>bP0l+XkKyyRU}

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/templates/constraints/js/constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/templates/constraints/js/constraints.js
new file mode 100644
index 0000000..c8144fd
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/templates/constraints/js/constraints.js
@@ -0,0 +1,54 @@
+define(
+  [
+  'jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser',
+  'pgadmin.browser.collection'{% for c in constraints %}, 'pgadmin.node.{{ c|safe }}'{%endfor%}
+  ],
+function($, _, S, pgAdmin, pgBrowser) {
+
+  if (!pgBrowser.Nodes['coll-constraints']) {
+    var databases = pgAdmin.Browser.Nodes['coll-constraints'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'constraints',
+        label: '{{ _('Constraints') }}',
+        type: 'coll-constraints',
+        columns: ['name', 'comment']
+      });
+  };
+
+  if (!pgBrowser.Nodes['constraints']) {
+    pgAdmin.Browser.Nodes['constraints'] = pgBrowser.Node.extend({
+      type: 'constraints',
+      label: '{{ _('Constraints') }}',
+      collection_type: 'coll-constraints',
+      parent_type: ['table'],
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([]);
+      },
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          comment: undefined
+       },
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', type: 'text',
+          mode: ['properties', 'create', 'edit']
+        },{
+          id: 'oid', label:'{{ _('Oid') }}', cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'comment', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit']
+        }]
+      })
+  });
+  }
+
+  return pgBrowser.Nodes['constraints'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/type.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/type.py
new file mode 100644
index 0000000..d75bf2b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/type.py
@@ -0,0 +1,42 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+from pgadmin.browser.collection import CollectionNodeModule
+from flask import Blueprint
+
+
+class ConstraintRegistry(object):
+    """
+    ConstraintTypeRegistry
+
+    It is more of a registry for difference type of constraints for the tables.
+    Its job is to initialize to different type of constraint blueprint and
+    register it with its respective NodeView.
+    """
+    registry = dict()
+
+    def __init__(self, name, con_blueprint, con_nodeview):
+        if name not in ConstraintRegistry.registry:
+
+            blueprint = con_blueprint(name)
+
+            # TODO:: register the view with the blueprint
+            con_nodeview.register_node_view(blueprint)
+
+            ConstraintRegistry.registry[name] = {
+                'blueprint': blueprint,
+                'nodeview': con_nodeview
+            }
+
+
+class ConstraintTypeModule(CollectionNodeModule):
+    register = Blueprint.register
+
+    def __init__(self, *args, **kwargs):
+        super(ConstraintTypeModule, self).__init__(*args, **kwargs)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py
new file mode 100644
index 0000000..4573a97
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py
@@ -0,0 +1,874 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Index Node """
+
+from flask import render_template, make_response, request, jsonify
+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 as database
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+    parse_priv_to_db
+from functools import wraps
+import json
+
+
+class IndexesModule(CollectionNodeModule):
+    """
+     class IndexesModule(CollectionNodeModule)
+
+        A module class for Index node derived from CollectionNodeModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the Index and it's base module.
+
+    * get_nodes(gid, sid, did, scid, tid)
+      - Method is used to generate the browser collection node.
+
+    * node_inode()
+      - Method is overridden from its base class to make the node as leaf node.
+
+    * script_load()
+      - Load the module script for schema, when any of the server node is
+        initialized.
+    """
+
+    NODE_TYPE = 'index'
+    COLLECTION_LABEL = gettext("Indexes")
+
+    def __init__(self, *args, **kwargs):
+        """
+        Method is used to initialize the IndexModule and it's base module.
+
+        Args:
+            *args:
+            **kwargs:
+        """
+        self.min_ver = None
+        self.max_ver = None
+        super(IndexesModule, self).__init__(*args, **kwargs)
+
+    def BackendSupported(self, manager, **kwargs):
+        """
+        Load this module if vid is view, we will not load it under
+        material view
+        """
+        if super(IndexesModule, self).BackendSupported(manager, **kwargs):
+            conn = manager.connection(did=kwargs['did'])
+            # If DB is not connected then return error to browser
+            if not conn.connected():
+                return precondition_required(
+                    gettext(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            if 'vid' not in kwargs:
+                return True
+
+            template_path = 'index/sql/9.1_plus'
+            SQL = render_template("/".join(
+                  [template_path, 'backend_support.sql']), vid=kwargs['vid'])
+            status, res = conn.execute_scalar(SQL)
+
+            # check if any errors
+            if not status:
+                return internal_server_error(errormsg=res)
+            # Check vid is view not material view
+            # then true, othewise false
+            return res
+
+    def get_nodes(self, gid, sid, did, scid, **kwargs):
+        """
+        Generate the collection node
+        """
+        assert('tid' in kwargs or 'vid' in kwargs)
+        yield self.generate_browser_collection_node(
+            kwargs['tid'] if 'tid' in kwargs else kwargs['vid']
+        )
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for server, when any of the server-group node is
+        initialized.
+        """
+        return database.DatabaseModule.NODE_TYPE
+
+    @property
+    def node_inode(self):
+        """
+        Load the module node as a leaf node
+        """
+        return False
+
+blueprint = IndexesModule(__name__)
+
+
+class IndexesView(PGChildNodeView):
+    """
+    This class is responsible for generating routes for Index node
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the IndexView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * list()
+      - This function is used to list all the Index nodes within that
+      collection.
+
+    * nodes()
+      - This function will used to create all the child node within that
+        collection, Here it will create all the Index node.
+
+    * properties(gid, sid, did, scid, tid, idx)
+      - This function will show the properties of the selected Index node
+
+    * create(gid, sid, did, scid, tid)
+      - This function will create the new Index object
+
+    * update(gid, sid, did, scid, tid, idx)
+      - This function will update the data for the selected Index node
+
+    * delete(self, gid, sid, scid, tid, idx):
+      - This function will drop the Index object
+
+    * msql(gid, sid, did, scid, tid, idx)
+      - This function is used to return modified SQL for the selected
+        Index node
+
+    * get_sql(data, scid, tid)
+      - This function will generate sql from model data
+
+    * sql(gid, sid, did, scid):
+      - This function will generate sql to show it in sql pane for the
+        selected Index node.
+
+    * dependency(gid, sid, did, scid):
+      - This function will generate dependency list show it in dependency
+        pane for the selected Index node.
+
+    * dependent(gid, sid, did, scid):
+      - This function will generate dependent list to show it in dependent
+        pane for the selected Index node.
+    """
+
+    node_type = blueprint.node_type
+
+    parent_ids = [
+            {'type': 'int', 'id': 'gid'},
+            {'type': 'int', 'id': 'sid'},
+            {'type': 'int', 'id': 'did'},
+            {'type': 'int', 'id': 'scid'},
+            {'type': 'int', 'id': 'tid'}
+            ]
+    ids = [
+            {'type': 'int', 'id': 'idx'}
+            ]
+
+    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_collations': [{'get': 'get_collations'},
+                           {'get': 'get_collations'}],
+        'get_access_methods': [{'get': 'get_access_methods'},
+                               {'get': 'get_access_methods'}],
+        'get_op_class': [{'get': 'get_op_class'},
+                               {'get': 'get_op_class'}]
+    })
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+                kwargs['sid']
+            )
+            self.conn = self.manager.connection(did=kwargs['did'])
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    gettext(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            # We need datlastsysoid to check if current index is system index
+            self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+            # we will set template path for sql scripts
+            self.template_path = 'index/sql/9.1_plus'
+
+            # We need parent's name eg table name and schema name
+            # when we create new index in update we can fetch it using
+            # property sql
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=kwargs['tid'])
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            for row in rset['rows']:
+                self.schema = row['schema']
+                self.table = row['table']
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+    @check_precondition
+    def get_collations(self, gid, sid, did, scid, tid, idx=None):
+        """
+        This function will return list of collation available
+        via AJAX response
+        """
+        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['collation'],
+                             'value': row['collation']}
+                        )
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_access_methods(self, gid, sid, did, scid, tid, idx=None):
+        """
+        This function will return list of access methods available
+        via AJAX response
+        """
+        res = [{'label': '', 'value': ''}]
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_am.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['amname'],
+                             'value': row['amname']}
+                        )
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_op_class(self, gid, sid, did, scid, tid, idx=None):
+        """
+        This function will return list of op_class method
+        for each access methods available via AJAX response
+        """
+        res = dict()
+        try:
+            # Fetching all the access methods
+            SQL = render_template("/".join([self.template_path,
+                                            'get_am.sql']))
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            for row in rset['rows']:
+                # Fetching all the op_classes for each access method
+                SQL = render_template("/".join([self.template_path,
+                                                'get_op_class.sql']),
+                                      oid=row['oid'])
+                status, result = self.conn.execute_2darray(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                op_class_list = [{'label': '', 'value': ''}]
+
+                for r in result['rows']:
+                    op_class_list.append({'label': r['opcname'],
+                                          'value': r['opcname']})
+
+                # Append op_class list in main result as collection
+                res[row['amname']] = op_class_list
+
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+    @check_precondition
+    def list(self, gid, sid, did, scid, tid):
+        """
+        This function is used to list all the schema nodes within that collection.
+
+        Args:
+            gid: Server group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+
+        Returns:
+            JSON of available schema nodes
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'nodes.sql']), tid=tid)
+        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, tid):
+        """
+        This function will used to create all the child node within that collection.
+        Here it will create all the schema node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+
+        Returns:
+            JSON of available schema child nodes
+        """
+        res = []
+        SQL = render_template("/".join([self.template_path,
+                                        'nodes.sql']), tid=tid)
+        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'],
+                        tid,
+                        row['name'],
+                        icon="icon-index"
+                    ))
+
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    def _column_details(self, idx, data):
+        """
+        This functional will fetch list of column details for index
+
+        Args:
+            idx: Index OID
+            data: Properties data
+
+        Returns:
+            Updated properties data with column details
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'column_details.sql']), idx=idx)
+        status, rset = self.conn.execute_2darray(SQL)
+        if not status:
+            return internal_server_error(errormsg=rset)
+        # 'attdef' comes with quotes from query so we need to strip them
+        # 'options' we need true/false to render switch ASC(false)/DESC(true)
+        columns = []
+        cols = []
+        cnt = 1
+        for row in rset['rows']:
+            # We need all data as collection for ColumnsModel
+            cols_data = {
+                'colname': row['attdef'].strip('"'),
+                'collspcname': row['collnspname'],
+                'op_class': row['opcname'],
+            }
+            if row['options'][0] == 'DESC':
+                cols_data['sort_order'] = True
+            columns.append(cols_data)
+
+            # We need same data as string to display in properties window
+            # If multiple column then separate it by colon
+            cols_str = row['attdef']
+            if row['collnspname']:
+                cols_str += ' COLLATE ' + row['collnspname']
+            if row['opcname']:
+                cols_str += ' ' + row['opcname']
+            if row['options'][0] == 'DESC':
+                cols_str += ' DESC'
+            cols.append(cols_str)
+
+        # Push as collection
+        data['columns'] = columns
+        # Push as string
+        data['cols'] = ', '.join(cols)
+
+        return data
+
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid, idx):
+        """
+        This function will show the properties of the selected schema node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did:  Database ID
+            scid: Schema ID
+            scid: Schema ID
+            tid: Table ID
+            idx: Index ID
+
+        Returns:
+            JSON of selected schema node
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              tid=tid, idx=idx,
+                              datlastsysoid=self.datlastsysoid)
+
+        status, res = self.conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        # Making copy of output for future use
+        data = dict(res['rows'][0])
+
+        # Add column details for current index
+        data = self._column_details(idx, data)
+
+        return ajax_response(
+                response=data,
+                status=200
+                )
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, tid):
+        """
+        This function will creates new the schema object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+        """
+        data = request.form if request.form else json.loads(
+            request.data.decode()
+        )
+
+        for k, v in data.items():
+            try:
+                data[k] = json.loads(v)
+            except (ValueError, TypeError):
+                data[k] = v
+
+        required_args = {
+            'name': 'Name',
+            'columns': 'Columns'
+        }
+
+        for arg in required_args:
+            err_msg = None
+            if arg == 'columns' and len(data['columns']) < 1:
+                err_msg = "You must provide one or more column to create index"
+
+            if arg not in data:
+                err_msg = "Couldn't find the required parameter (%s)." % \
+                          required_args[arg]
+                # Check if we have at least one column
+            if err_msg is not None:
+                return make_json_response(
+                    status=410,
+                    success=0,
+                    errormsg=gettext(err_msg)
+                )
+
+        # Adding parent into data dict, will be using it while creating sql
+        data['schema'] = self.schema
+        data['table'] = self.table
+
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'create.sql']),
+                                  data=data, conn=self.conn)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # If user chooses concurrent index then we can not run it along
+            # with other alter statments so we will separate alter index part
+            SQL = render_template("/".join([self.template_path,
+                                            'alter.sql']),
+                                  data=data, conn=self.conn)
+            SQL = SQL.strip('\n').strip(' ')
+            if SQL != '':
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+            # we need oid to to add object in tree at browser
+            SQL = render_template("/".join([self.template_path,
+                                            'get_oid.sql']),
+                                  tid=tid, data=data)
+            status, idx = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=tid)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    idx,
+                    scid,
+                    data['name'],
+                    icon="icon-index"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid, idx):
+        """
+        This function will updates existing the schema object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           idx: Index ID
+        """
+        # Below will decide if it's simple drop or drop with cascade call
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+
+        try:
+            # We will first fetch the index name for current request
+            # so that we create template for dropping index
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  tid=tid, idx=idx,
+                                  datlastsysoid=self.datlastsysoid)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = dict(res['rows'][0])
+
+            SQL = render_template("/".join([self.template_path,
+                                            'delete.sql']),
+                                  data=data, conn=self.conn, 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("Index is dropped"),
+                data={
+                    'id': idx,
+                    'tid': tid
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid, idx):
+        """
+        This function will updates existing the schema object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           idx: Index ID
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            SQL = self.get_sql(scid, tid, idx, data)
+            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="Index updated",
+                    data={
+                        'id': idx,
+                        'tid': tid,
+                        'scid': scid
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': idx,
+                        'tid': tid,
+                        'scid': scid
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid, idx=None):
+        """
+        This function will generates modified sql for schema object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           idx: Index ID (When working with existing index)
+        """
+        data = dict()
+        for k, v in request.args.items():
+            try:
+                data[k] = json.loads(v)
+            except ValueError:
+                data[k] = v
+
+        # Adding parent into data dict, will be using it while creating sql
+        data['schema'] = self.schema
+        data['table'] = self.table
+
+        try:
+            SQL = self.get_sql(scid, tid, idx, data)
+
+            if SQL and SQL.strip('\n') and SQL.strip(' '):
+                return make_json_response(
+                        data=SQL,
+                        status=200
+                        )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def get_sql(self, scid, tid, idx, data):
+        """
+        This function will genrate sql from model data
+        """
+        if idx is not None:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  tid=tid, idx=idx,
+                                  datlastsysoid=self.datlastsysoid)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            old_data = dict(res['rows'][0])
+
+            # If name is not present in data then
+            # we will fetch it from old data, we also need schema & table name
+            if 'name' not in data:
+                data['name'] = old_data['name']
+
+            SQL = render_template(
+                "/".join([self.template_path, 'update.sql']),
+                data=data, o_data=old_data, conn=self.conn
+                )
+        else:
+            required_args = {
+                'name': 'Name',
+                'columns': 'Columns'
+            }
+            for arg in required_args:
+                err = False
+                if arg == 'columns' and len(data['columns']) < 1:
+                    err = True
+
+                if arg not in data:
+                    err = True
+                    # Check if we have at least one column
+                if err:
+                    return gettext('-- incomplete definition')
+
+            # If the request for new object which do not have did
+            SQL = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=data, conn=self.conn)
+            SQL += "\n"
+            SQL += render_template("/".join([self.template_path, 'alter.sql']),
+                                   data=data, conn=self.conn)
+
+        return SQL
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, tid, idx):
+        """
+        This function will generates reverse engineered sql for schema object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           idx: Index ID
+        """
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  tid=tid, idx=idx,
+                                  datlastsysoid=self.datlastsysoid)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = dict(res['rows'][0])
+            # Adding parent into data dict, will be using it while creating sql
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            # Add column details for current index
+            data = self._column_details(idx, data)
+
+            SQL = self.get_sql(scid, tid, None, data)
+
+            sql_header = "-- Index: {0}\n\n-- ".format(data['name'])
+            sql_header += render_template("/".join([self.template_path,
+                                                    'delete.sql']),
+                                          data=data, conn=self.conn)
+
+            SQL = sql_header + '\n\n' + SQL
+
+            return ajax_response(response=SQL)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, tid, idx):
+        """
+        This function get the dependents and return ajax response
+        for the schema node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+            idx: Index ID
+        """
+        dependents_result = self.get_dependents(
+            self.conn, idx
+        )
+
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, tid, idx):
+        """
+        This function get the dependencies and return ajax response
+        for the schema node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+            idx: Index ID
+
+        """
+        dependencies_result = self.get_dependencies(
+            self.conn, idx
+        )
+
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+IndexesView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/img/coll-index.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/img/coll-index.png
new file mode 100644
index 0000000000000000000000000000000000000000..bb1513c8287e2ee5b39c36836fc01636ed35cf5f
GIT binary patch
literal 468
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMfoB*E?S0LTF`o3ZPMa`Pi^5sX2
z>o4lmo>i<kCRV(kJ8wtFmZzrmmrp<ccKpfLLk~aix$|l3^$+VVy<2|v&4LrJidH^6
z@$Bo-$6pTI{~QpL<QJ9X6Op*>#s{~Mc;}!vhk&@*M_wFx^kv_@PdjdY*m&jL%JXj)
zpL#Xt=!@wGpZi26dW9#rhbFAP@YX3X&fY(E*X@s+uf1P$;qB7Xujd_mIpfgtu2Mf2
zp!*n0g8YIR9G=}s19CVEJR*yMv<Dcwoy@iaGV(oL977~7Cnp?W=<zwDqrtQ$=Z(x9
zj~cx^i3d#_1)o2B`1FyRgNw8HFpEpT)q?ItD<6tE2Ork$TgH~b9njmdWXhJFHFIoS
zgadk;7ERjJwQ82t5w!(UyuP}*vazK*qK|z2!pqa!lUTDOd#6MPBg0g2&X1Sx0WD@=
zP%UwdC`m~yNwrEYN(E93Mh1okx`sx&28JO<2397<Rwky}2Bua92J_zOtwPa|o1c=I
iRteEyU<uX$RBd8qU<T1}YI^7zkQJV;elF{r5}E-1(!&P;

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/img/index.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/img/index.png
new file mode 100644
index 0000000000000000000000000000000000000000..a239c125109ca8d73b6afa840b5740a8bb06934e
GIT binary patch
literal 562
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf+W?;sS0LTF`o3ZPMa`Pi^5sX2
z>o4lmo>i<kCRV(kJ8y?+{pF4=PftJpcH-&RqmREFeDHbi-A_Aie%yTR{klu<R-Sva
z<kahcm?Zz`B)_OcpNPbrw?2A=CAfvey9CEM1;#Bn{_^Crug9KzJ@oL)zI&gxTz|j*
z^1D^%-z+`-dclcTbB@07i%Rl|O!SIK*l_usOK`kXP@F?RoSlE{j6=^4Kl-x&{^wn{
zKW@A6VZ)Vot1rA=cINfMldt9;eL3^c^Qi}(c}FC8h9$U%Cb)*gFF*UnF(A&~KX&Hf
z=X>sa+J5uHrmOGQUVOXa?3=}>Ud=o9a@OG&(+)g;b8MqB(8G)+L4Lsu4$p3+0Xdun
z9+AaB+5?Q;PG;Ky88x0Rjv*44lM@t}42%pnFW}g)X=8DL5_f?jgZYf}hYz1VuCJh>
zqN8+*RZu{9`h+QyrcInWxt&8pLrc?p<%(4+vooAnM7C}zT~hk>3olQv0@E?IWoggE
zriEQI+a|y$Y+cRnuAVL)&N097^x3<IFQ2}B{5pS<HnaIE?Gp?Pv%m0#sEbTm4|IoW
ziEBhjN@7W>RdP`(kYX@0Ff`CLG}1LN3^6jWGBLI?G1WFOwK6c6_fBsWiiX_$l+3hB
ihz0{oum+%N6DtEVh=x<sL)QQ`FnGH9xvX<aXaWG5fB2*T

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/templates/index/js/index.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/templates/index/js/index.js
new file mode 100644
index 0000000..b49492a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/templates/index/js/index.js
@@ -0,0 +1,409 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser',
+        'backform', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
+
+  if (!pgBrowser.Nodes['coll-index']) {
+    var databases = pgAdmin.Browser.Nodes['coll-index'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'index',
+        label: '{{ _('Indexes') }}',
+        type: 'coll-index',
+        sqlAlterHelp: 'sql-alterindex.html',
+        sqlCreateHelp: 'sql-createindex.html',
+        columns: ['name', 'description']
+      });
+  };
+
+    // Model to create column collection control
+    var ColumnModel = pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          colname: undefined,
+          collspcname: undefined,
+          op_class: undefined,
+          sort_order: false,
+          nulls: false
+        },
+        schema: [
+          {
+            id: 'colname', label:'{{ _('Column') }}', cell: 'string',
+            type: 'text', disabled: 'inSchema', editable: false,
+            control: 'node-list-by-name', node: 'column'
+          },{
+            id: 'collspcname', label:'{{ _('Collation') }}', cell: 'string',
+            type: 'text', disabled: 'inSchema', editable: false,
+            control: 'node-ajax-options', url: 'get_collations', node: 'index'
+          },{
+            id: 'op_class', label:'{{ _('Operator class') }}', cell: 'string',
+            type: 'text', disabled: 'checkAccessMethod', editable: false,
+            control: 'node-ajax-options', url: 'get_op_class', node: 'index',
+            deps: ['amname'], transform: function(data) {
+             /* We need to extract data from collection according
+              * to access method selected by user if not selected
+              * send btree related op_class options
+              */
+             var amname = this.model.handler.get('amname'),
+                 options = data['btree'];
+
+             if(_.isUndefined(amname))
+               return options;
+
+             _.each(data, function(v, k) {
+                if(amname === k) {
+                  options = v;
+                }
+              });
+             return options;
+            }
+          },{
+            id: 'sort_order', label:'{{ _('Sort order') }}', cell: 'switch',
+            type: 'switch', disabled: 'checkAccessMethod', editable: false,
+            deps: ['amname'],
+            options: {
+             'onText': 'DESC', 'offText': 'ASC',
+             'onColor': 'success', 'offColor': 'default',
+             'size': 'small'
+            }
+          },{
+            id: 'nulls', label:'{{ _('NULLs') }}', cell: 'switch',
+            type: 'switch', disabled: 'checkAccessMethod', editable: false,
+            deps: ['amname', 'sort_order'],
+            options: {
+             'onText': 'FIRST', 'offText': 'LAST',
+             'onColor': 'success', 'offColor': 'default',
+             'size': 'small'
+            }
+          }
+        ],
+        validate: function() {
+          this.errorModel.clear();
+
+          if (_.isUndefined(this.get('colname'))
+              || String(this.get('colname')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Column Name can not be empty.') }}';
+            this.errorModel.set('colname', msg);
+            return msg;
+          }
+        },
+        // We will check if we are under schema node
+        inSchema: function() {
+          if(this.node_info &&  'catalog' in this.node_info) {
+            return true;
+          }
+          return false;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchemaWithModelCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info) {
+            // We will disable control if it's in 'edit' mode
+            if (m.isNew()) {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          return true;
+        },
+        // We will check if we are under schema node and added condition
+        checkAccessMethod: function(m) {
+        //Access method is empty or btree then do not disable field
+          var parent_model = m.handler;
+          if(!m.inSchema.apply(this, [m]) &&
+              (_.isUndefined(parent_model.get('amname')) ||
+               _.isNull(parent_model.get('amname')) ||
+               String(parent_model.get('amname')).replace(/^\s+|\s+$/g, '') == '' ||
+               parent_model.get('amname') === 'btree')) {
+            // We need to set nulls to true if sort_order is set to desc
+            // nulls first is default for desc
+            if(m.get('sort_order') == true) {
+               setTimeout(function() { m.set('nulls', true) }, 10);
+            } else {
+               setTimeout(function() { m.set('nulls', false) }, 10);
+            }
+            return false;
+          }
+          return true;
+        },
+    });
+
+  if (!pgBrowser.Nodes['index']) {
+    pgAdmin.Browser.Nodes['index'] = pgAdmin.Browser.Node.extend({
+      parent_type: ['table', 'view', 'mview'],
+      collection_type: ['coll-table', 'coll-view'],
+      type: 'index',
+      label: '{{ _('Index') }}',
+      hasSQL:  true,
+      hasDepends: true,
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_index_on_coll', node: 'coll-index', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Index...') }}',
+          icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_index', node: 'index', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Index...') }}',
+          icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_index_onTable', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Index...') }}',
+          icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_index_onMatView', node: 'mview', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 5, label: '{{ _('Index...') }}',
+          icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }
+        ]);
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          nspname: undefined,
+          tabname: undefined,
+          spcname: 'pg_default',
+          amname: 'btree'
+        },
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', cell: 'string',
+          type: 'text', disabled: 'inSchema'
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'int', disabled: true, mode: ['edit', 'properties']
+        },{
+          id: 'spcname', label:'{{ _('Tablespace') }}', cell: 'string',
+          control: 'node-list-by-name', node: 'tablespace',
+          select2: {'allowClear': true},
+          type: 'text', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema', filter: function(d) {
+            // If tablespace name is not "pg_global" then we need to exclude them
+            if(d && d.label.match(/pg_global/))
+            {
+              return false;
+            }
+            return true;
+          }
+        },{
+          id: 'amname', label:'{{ _('Access Method') }}', cell: 'string',
+          type: 'text', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchemaWithModelCheck', url: 'get_access_methods',
+          group: '{{ _('Definition') }}', select2: {'allowClear': true},
+          control: Backform.NodeAjaxOptionsControl.extend({
+            // When access method changes we need to clear columns collection
+            onChange: function() {
+              Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+              var self = this,
+              // current access method
+              current_am = self.model.get('amname'),
+              // previous access method
+              previous_am = self.model.previous('amname');
+              if (current_am != previous_am && self.model.get('columns').length !== 0) {
+                var msg = '{{ _('Changing access method will clear columns collection') }}';
+                alertify.confirm(msg, function (e) {
+                    // User clicks Ok, lets clear collection
+                    var column_collection = self.model.get('columns');
+                    column_collection.reset();
+                  }, function() {
+                    // User clicks Cancel set previous value again in combo box
+                    setTimeout(function(){
+                      self.model.set('amname', previous_am);
+                    }, 10);
+                });
+              }
+            }
+          })
+        },{
+          id: 'cols', label:'{{ _('Columns') }}', cell: 'string',
+          type: 'text', disabled: 'inSchema', mode: ['properties']
+        },{
+          id: 'fillfactor', label:'{{ _('Fill factor') }}', cell: 'string',
+          type: 'int', disabled: 'inSchema', mode: ['create', 'edit', 'properties'],
+          min: 10, max:100, group: '{{ _('Definition') }}'
+        },{
+          id: 'indisunique', label:'{{ _('Unique?') }}', cell: 'string',
+          type: 'switch', disabled: 'inSchemaWithModelCheck',
+          group: '{{ _('Definition') }}'
+        },{
+          id: 'indisclustered', label:'{{ _('Clustered?') }}', cell: 'string',
+          type: 'switch', disabled: 'inSchema',
+          group: '{{ _('Definition') }}'
+        },{
+          id: 'indisvalid', label:'{{ _('Valid?') }}', cell: 'string',
+          type: 'switch', disabled: true, mode: ['properties'],
+
+        },{
+          id: 'indisprimary', label:'{{ _('Primary?') }}', cell: 'string',
+          type: 'switch', disabled: true, mode: ['properties'],
+
+        },{
+          id: 'is_sys_idx', label:'{{ _('System index?') }}', cell: 'string',
+          type: 'switch', disabled: true, mode: ['properties'],
+
+        },{
+          id: 'isconcurrent', label:'{{ _('Concurrent build?') }}', cell: 'string',
+          type: 'switch', disabled: 'inSchemaWithModelCheck',
+          mode: ['create', 'edit'], group: '{{ _('Definition') }}'
+        },{
+          id: 'indconstraint', label:'{{ _('Constraint') }}', cell: 'string',
+          type: 'text', disabled: 'inSchemaWithModelCheck', mode: ['create', 'edit'],
+          control: 'sql-field', visible: true, group: '{{ _('Definition') }}'
+        },{
+          id: 'columns', label: 'Columns', type: 'collection',
+          group: '{{ _('Definition') }}', model: ColumnModel, mode: ['edit', 'create'],
+          canAdd: function(m) {
+            // We will disable it if it's in 'edit' mode
+            if (m.isNew()) {
+              return true;
+            } else {
+              return false;
+            }
+          },
+          canEdit: function(m) {
+            // We will disable it if it's in 'edit' mode
+            if (m.isNew()) {
+              return true;
+            } else {
+              return false;
+            }
+          },
+          canDelete: function(m) {
+            // We will disable it if it's in 'edit' mode
+            if (m.isNew()) {
+              return true;
+            } else {
+              return false;
+            }
+          },
+          control: 'unique-col-collection', uniqueCol : ['colname']
+        },{
+          id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema'
+        }
+        ],
+        validate: function(keys) {
+          var err = {},
+              changedAttrs = this.changed,
+              msg = undefined;
+
+          // Nothing to validate
+          if (keys && keys.length == 0) {
+            this.errorModel.clear();
+            return null;
+          } else {
+            this.errorModel.clear();
+          }
+
+          if (_.isUndefined(this.get('name'))
+              || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Name can not be empty.') }}';
+            this.errorModel.set('name', msg);
+            return msg;
+          }
+          if (_.isUndefined(this.get('spcname'))
+              || String(this.get('spcname')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Tablespace can not be empty.') }}';
+            this.errorModel.set('spcname', msg);
+            return msg;
+          }
+          if (_.isUndefined(this.get('amname'))
+              || String(this.get('amname')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Access method can not be empty.') }}';
+            this.errorModel.set('amname', msg);
+            return msg;
+          }
+          // Checks if all columns has names
+          var cols = this.get('columns');
+          if(cols && cols.length > 0) {
+             if(!_.every(cols.pluck('colname'))) {
+               msg = '{{ _('You must specify column name.') }}';
+               this.errorModel.set('columns', msg);
+               return msg;
+             }
+          } else if(cols){
+               msg = '{{ _('You must specify at least one column.') }}';
+               this.errorModel.set('columns', msg);
+               return msg;
+          }
+          return null;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchema: function() {
+          if(this.node_info &&  'catalog' in this.node_info) {
+            return true;
+          }
+          return false;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchemaWithModelCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info) {
+            // We will disable control if it's in 'edit' mode
+            if (m.isNew()) {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          return true;
+        },
+        // Checks weather to enable/disable control
+        inSchemaWithColumnCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info) {
+            // We will disable control if it's system columns
+            // ie: it's position is less then 1
+            if (m.isNew()) {
+              return false;
+            } else {
+              // if we are in edit mode
+              if (!_.isUndefined(m.get('attnum')) && m.get('attnum') >= 1 ) {
+                return false;
+              } else {
+                return true;
+              }
+           }
+          }
+          return true;
+        }
+      }),
+      // Below function will enable right click menu for creating column
+      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, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to c reate table
+            if (_.indexOf(['schema'], d._type) > -1)
+              return true;
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1) {
+            return false;
+          } else {
+            return true;
+          }
+      }
+  });
+ }
+
+  return pgBrowser.Nodes['index'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py
new file mode 100644
index 0000000..f442338
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py
@@ -0,0 +1,497 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements Rule Node"""
+
+import json
+from flask import render_template, make_response, request, jsonify
+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
+import pgadmin.browser.server_groups.servers.databases.schemas as schemas
+from pgadmin.browser.server_groups.servers.databases.schemas.utils import \
+    parse_rule_definition
+from pgadmin.browser.collection import CollectionNodeModule
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class RuleModule(CollectionNodeModule):
+    """
+    class RuleModule(CollectionNodeModule):
+
+        A rule collection Node which inherits CollectionNodeModule
+        class and define methods:
+          get_nodes - To generate collection node.
+          script_load - tells when to load js file.
+          csssnppets - add css to page
+    """
+    NODE_TYPE = 'rule'
+    COLLECTION_LABEL = gettext("Rules")
+
+    def __init__(self, *args, **kwargs):
+        self.min_ver = None
+        self.max_ver = None
+
+        super(RuleModule, self).__init__(*args, **kwargs)
+
+    def BackendSupported(self, manager, **kwargs):
+        """
+        Load this module if tid is view, we will not load it under
+        material view
+        """
+        if super(RuleModule, self).BackendSupported(manager, **kwargs):
+            conn = manager.connection(did=kwargs['did'])
+            # If DB is not connected then return error to browser
+            if not conn.connected():
+                return precondition_required(
+                    gettext(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            if 'vid' not in kwargs:
+                return True
+
+            self.template_path = 'rules/sql'
+            SQL = render_template("/".join(
+                    [self.template_path, 'backend_support.sql']
+                  ), vid=kwargs['vid'])
+            status, res = conn.execute_scalar(SQL)
+            # check if any errors
+            if not status:
+                return internal_server_error(errormsg=res)
+            # Check tid is view not material view
+            # then true, othewise false
+            if res is True:
+                return res
+            else:
+                return res
+
+    def get_nodes(self, gid, sid, did, scid, **kwargs):
+        """
+        Generate the collection node
+        """
+        assert('tid' in kwargs or 'vid' in kwargs)
+        yield self.generate_browser_collection_node(
+          kwargs['tid'] if 'tid' in kwargs else kwargs['vid']
+          )
+
+    @property
+    def node_inode(self):
+        """
+        If a node has children return True otherwise False
+        """
+        return False
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for rule, when any of the database nodes are
+        initialized.
+        """
+        return schemas.SchemaModule.NODE_TYPE
+
+    @property
+    def csssnippets(self):
+        """
+        Returns a snippet of css to include in the page
+        """
+        snippets = [
+                render_template(
+                    "browser/css/collection.css",
+                    node_type=self.node_type,
+                    _=gettext
+                    ),
+                render_template(
+                    "rules/css/rule.css",
+                    node_type=self.node_type,
+                    _=gettext
+                    )
+                ]
+
+        for submodule in self.submodules:
+            snippets.extend(submodule.csssnippets)
+
+        return snippets
+
+
+# Create blueprint of RuleModule.
+blueprint = RuleModule(__name__)
+
+
+class RuleView(PGChildNodeView):
+    """
+    This is a class for rule node which inherits the
+    properties and methods from PGChildNodeView class and define
+    various methods to list, create, update and delete rule.
+
+    Variables:
+    ---------
+    * node_type - tells which type of node it is
+    * parent_ids - id with its type and name of parent nodes
+    * ids - id with type and name of extension module being used.
+    * operations - function routes mappings defined.
+    """
+    node_type = blueprint.node_type
+
+    parent_ids = [
+            {'type': 'int', 'id': 'gid'},
+            {'type': 'int', 'id': 'sid'},
+            {'type': 'int', 'id': 'did'},
+            {'type': 'int', 'id': 'scid'},
+            {'type': 'int', 'id': 'tid'}
+            ]
+    ids = [
+            {'type': 'int', 'id': 'rid'}
+            ]
+
+    operations = dict({
+        'obj': [
+            {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+            {'get': 'list', 'post': 'create'}
+        ],
+        'children': [{
+            'get': 'children'
+        }],
+        'delete': [{'delete': 'delete'}],
+        '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'}],
+        'configs': [{'get': 'configs'}]
+    })
+
+    def module_js(self):
+        """
+        This property defines whether Javascript exists for this node.
+        """
+        return make_response(
+                render_template(
+                    "rules/js/rules.js",
+                    _=gettext
+                    ),
+                200, {'Content-Type': 'application/x-javascript'}
+                )
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will check the
+        database connection before running a view. It will also attach
+        manager, conn & template_path properties to self
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            self.manager = get_driver(
+              PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
+            self.conn = self.manager.connection(did=kwargs['did'])
+
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    gettext(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+            self.template_path = 'rules/sql'
+            return f(*args, **kwargs)
+
+        return wrap
+
+    @check_precondition
+    def list(self, gid, sid, did, scid, tid):
+        """
+        Fetch all rule properties and render into properties tab
+        """
+
+        # fetch schema name by schema id
+        SQL = render_template("/".join(
+          [self.template_path, 'properties.sql']), tid=tid)
+        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, tid):
+        """
+        List all the rules under the Rules Collection node
+        """
+        res = []
+        SQL = render_template("/".join(
+          [self.template_path, 'properties.sql']), tid=tid)
+
+        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'],
+                        tid,
+                        row['name'],
+                        icon="icon-rule"
+                    ))
+
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid, rid):
+        """
+        Fetch the properties of an individual rule and render in properties tab
+
+        """
+        SQL = render_template("/".join(
+            [self.template_path, 'properties.sql']
+          ), rid=rid, datlastsysoid=self.datlastsysoid)
+        status, res = self.conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        return ajax_response(
+                response=parse_rule_definition(res),
+                status=200
+                )
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, tid):
+        """
+        This function will create a new rule object
+        """
+        required_args = [
+            'name',
+        ]
+
+        data = request.form if request.form else \
+            json.loads(request.data.decode())
+        for arg in required_args:
+            if arg not in data:
+                return make_json_response(
+                    status=410,
+                    success=0,
+                    errormsg=gettext(
+                        "Couldn't find the required parameter (%s)." % arg
+                    )
+                )
+        try:
+            SQL = render_template("/".join(
+                [self.template_path, 'create.sql']), data=data)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # Fetch the rule id against rule name to display node
+            # in tree browser
+            SQL = render_template("/".join(
+                [self.template_path, 'rule_id.sql']), rule_name=data['name'])
+            status, rule_id = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=rule_id)
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    rule_id,
+                    tid,
+                    data['name'],
+                    icon="icon-rule"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid, rid):
+        """
+        This function will update a rule object
+        """
+        data = request.form if request.form else \
+            json.loads(request.data.decode())
+        SQL = self.getSQL(gid, sid, data, tid, rid)
+        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=gettext("Rule updated"),
+                    data={
+                        'id': tid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': tid,
+                        'scid': scid,
+                        'did': did
+                    }
+                )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid, rid):
+        """
+        This function will drop a rule object
+        """
+        # Below will decide if it's simple drop or drop with cascade call
+        cascade = True if self.cmd == 'delete' else False
+
+        try:
+            # Get name for rule from did
+            SQL = render_template("/".join(
+              [self.template_path, 'delete.sql']), rid=rid)
+            status, res_data = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res_data)
+            # drop rule
+            rset = res_data['rows'][0]
+            SQL = render_template("/".join(
+                [self.template_path, 'delete.sql']),
+                  rulename=rset['rulename'],
+                  relname=rset['relname'],
+                  nspname=rset['nspname'],
+                  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("Rule dropped"),
+                data={
+                    'id': tid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid, rid=None):
+        """
+        This function returns modified SQL
+        """
+        data = request.args
+        SQL = self.getSQL(gid, sid, data, tid, rid)
+        return make_json_response(
+                data=SQL,
+                status=200
+                )
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, tid, rid):
+        """
+        This function will generate sql to render into the sql panel
+        """
+        SQL = render_template("/".join(
+          [self.template_path, 'properties.sql']), rid=rid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+        res_data = parse_rule_definition(res)
+        SQL = render_template("/".join(
+            [self.template_path, 'create.sql']),
+            data=res_data, display_comments=True)
+
+        return ajax_response(response=SQL)
+
+    def getSQL(self, gid, sid, data, tid, rid):
+        """
+        This function will generate sql from model data
+        """
+        try:
+            if rid is not None:
+                SQL = render_template("/".join(
+                  [self.template_path, 'properties.sql']), rid=rid)
+                status, res = self.conn.execute_dict(SQL)
+                res_data = []
+                res_data = parse_rule_definition(res)
+                if not status:
+                    return internal_server_error(errormsg=res)
+                old_data = res_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 SQL
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, tid, rid):
+        """
+        This function gets the dependents and returns an ajax response
+        for the rule node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            tid: View ID
+            rid: Rule ID
+        """
+        dependents_result = self.get_dependents(self.conn, rid)
+        return ajax_response(
+            response=dependents_result,
+            status=200
+            )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, tid, rid):
+        """
+        This function gets the dependencies and returns sn ajax response
+        for the rule node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            tid: View ID
+            rid: Rule ID
+        """
+        dependencies_result = self.get_dependencies(self.conn, rid)
+        return ajax_response(
+            response=dependencies_result,
+            status=200
+            )
+
+RuleView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/img/coll-rule.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/img/coll-rule.png
new file mode 100644
index 0000000000000000000000000000000000000000..cfe6ed27d98351a03e98451fe83e785aeb51351f
GIT binary patch
literal 357
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Q-Dv1E0BJDrt0oW+5b=H{C_&@
z|C4F|pG^AyxbOed8ULTo{{Ljk|Hl*lKkj*b(Es0~_J5B$|2=B`_pssL!=`@^>;65g
z`S+m8Y=`zEpec+cL4Lsu4$p3+0Xdun9+AaB+5?Q;PG;Ky8Bv}tjv*44r}laCH7M}7
zJY3k(rBU~;KGd5<=#Js+WtXq}O?@?enTE6ko10E>S;3Z`GiP+(9r|ThaVJc2?wlhg
z<wBFqmar9VnEKaN=E$TcORXQ5+JBvSzqq!FQQCtkVR^CB37{>iC9V-ADTyViR>?)F
zK#IZ0z|cU~&`8(7FvQ5f%EZ{p#8lhB)XKnM-aEZjC>nC}Q!>*kAsP%U!5V<7O{@&e
WAR10h4_yP)z~JfX=d#Wzp$P!7cZ;e3

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/img/rule.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/img/rule.png
new file mode 100644
index 0000000000000000000000000000000000000000..8b4978090e33bbe3d5f7ed249fd93c2da34f52d6
GIT binary patch
literal 373
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}cz{ocE0DgsQug(ks{c>t{C_(0
z|C1^IANT%y)b{UT{r{)4{y&-e|8f7nNA3R}HvW51{r~Cg|4*j>e>~yeqt1U1oBlnh
z`S+mw|I-=&pG^AyxclFumVXax|2?Sq_n>kK%jYzp(TpWQe!&b5&u)M?oCO|{#X#Bv
zjNMLV+W{G&o-U3d5|`KZnTs_T@Hi`bDEDUl`g^`}GN)YjKDBz@O#-F|X1Becro3=L
zVnUz`J9~da#}rq6_MWOkIpP6gt8f32>#JDS-4zfcy)J)|(ya4dQ}_S3nP9`r$iw)~
zA83JUiEBhjN@7W>RdP`(kYX@0Ff`CLG}1LN3^6jWGBLI?G1WFOwK6c6_fBsWiiX_$
ml+3hBhz0{oum+%N6DtEVh=x<sL)QQ`FnGH9xvX<aXaWEjK9-vR

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/templates/rules/css/rule.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/templates/rules/css/rule.css
new file mode 100644
index 0000000..3d21bcf
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/templates/rules/css/rule.css
@@ -0,0 +1,16 @@
+.icon-rule{
+  background-image: url('{{ url_for('NODE-rule.static', filename='img/rule.png') }}') !important;
+  border-radius: 10px;
+  background-repeat: no-repeat;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
+
+.sql_field_height_140 {
+  height: 140px;
+}
+
+.sql_field_height_280 {
+  height: 280px;
+}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/templates/rules/js/rules.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/templates/rules/js/rules.js
new file mode 100644
index 0000000..1d00a38
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/templates/rules/js/rules.js
@@ -0,0 +1,266 @@
+define(
+  ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+    'pgadmin.browser', 'codemirror'],
+
+function($, _, S, pgAdmin, pgBrowser, CodeMirror) {
+
+  /**
+    Create and add a rule collection into nodes
+    @param {variable} label - Label for Node
+    @param {variable} type - Type of Node
+    @param {variable} columns - List of columns to
+      display under under properties.
+   */
+  if (!pgBrowser.Nodes['coll-rule']) {
+    var rules = pgAdmin.Browser.Nodes['coll-rule'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'rule',
+        label: '{{ _("Rules") }}',
+        type: 'coll-rule',
+        columns: ["name", "owner", "comment"]
+      });
+  }
+
+
+  /**
+    Create and Add an Rule Node into nodes
+    @param {variable} parent_type - The list of nodes
+    under which this node to display
+    @param {variable} type - Type of Node
+    @param {variable} hasSQL - To show SQL tab
+    @param {variable} canDrop - Adds drop rule option
+    in the context menu
+    @param {variable} canDropCascade - Adds drop Cascade
+    rule option in the context menu
+   */
+  if (!pgBrowser.Nodes['rule']) {
+    pgAdmin.Browser.Nodes['rule'] = pgAdmin.Browser.Node.extend({
+      parent_type: ['table','view'],
+      type: 'rule',
+      sqlAlterHelp: 'sql-alterrule.html',
+      sqlCreateHelp: 'sql-createrule.html',
+      label: '{{ _("rule") }}',
+      collection_type: 'coll-table',
+      hasSQL:  true,
+      hasDepends: true,
+      canDrop: function(itemData, item, data){
+        pgBrowser.Nodes['schema'].canChildDrop.apply(this, [itemData, item, data]);
+        if(_.has(itemData, 'label') && itemData.label === '_RETURN')
+          return false;
+        else {
+          return true;
+        }
+      },
+      canDropCascade: function(itemData, item, data){
+        pgBrowser.Nodes['schema'].canChildDrop.apply(this, [itemData, item, data]);
+        if(_.has(itemData, 'label') && itemData.label === '_RETURN')
+          return false;
+        else {
+          return true;
+        }
+      },
+      Init: function() {
+
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        /**
+          Add "create rule" menu option into context and object menu
+          for the following nodes:
+          coll-rule, rule and view and table.
+          @property {data} - Allow create rule option on schema node or
+          system rules node.
+         */
+        pgBrowser.add_menus([{
+          name: 'create_rule_on_coll', node: 'coll-rule', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 1, label: '{{ _("Rule...") }}',
+          icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_rule_onView', node: 'view', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 5, label: '{{ _("Rule...") }}',
+          icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_rule', node: 'rule', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 1, label: '{{ _("Rule...") }}',
+          icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_rule', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _("Rule...") }}',
+          icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }
+        ]);
+      },
+
+      /**
+        Define model for the rule node and specify the node
+        properties of the model in schema.
+       */
+      model: pgAdmin.Browser.Node.Model.extend({
+        schema: [{
+          id: 'name', label: '{{ _("Name") }}',
+          type: 'text', disabled: function(m) {
+            // disable name field it it is system rule
+            if (m && m.get('name') == "_RETURN") {
+              return true;
+            }
+            if (m.isNew()) {
+              return false;
+            } else if (m.node_info.server.version >= 90400) {
+              return false;
+            }
+            return true;
+          }
+        },
+        {
+          id: 'oid', label:'{{ _("OID") }}',
+          type: 'text', disabled: true, mode: ['properties']
+        },
+        {
+          id: 'schema', label:'{{ _("") }}',
+          type: 'text', visible: false, disabled: function(m) {
+            // It is used while generating sql
+            m.set('schema', m.node_info.schema.label);
+          }
+        },
+        {
+          id: 'view', label:'{{ _("") }}',
+          type: 'text', visible: false, disabled: function(m){
+
+            // It is used while generating sql
+            m.set('view', this.node_data.label);
+          }
+        },
+        {
+          id: 'event', label:'{{ _("Event") }}', control: 'select2',
+          group: '{{ _("Definition") }}', type: 'text',
+          select2: {
+            width: '100%',
+            allowClear: false
+          },
+          options:[
+            {label: 'Select', value: 'Select'},
+            {label: 'Insert', value: 'Insert'},
+            {label: 'Update', value: 'Update'},
+            {label: 'Delete', value: 'Delete'}
+          ]
+        },
+        {
+          id: 'do_instead', label:'{{ _("Do Instead") }}', group: '{{ _("Definition") }}',
+          type: 'switch'
+        },
+        {
+          id: 'condition', label:'{{ _("Condition") }}',
+          type: 'text', group: '{{ _("Definition") }}',
+          control: Backform.SqlFieldControl
+        },
+        {
+          id: 'statements', label:'{{ _("Commands") }}',
+          type: 'text', group: '{{ _("Definition") }}',
+          control: Backform.SqlFieldControl
+        },
+        {
+          id: 'system_rule', label:'{{ _("System rule?") }}',
+          type: 'switch', mode: ['properties']
+        },
+        {
+          id: 'enabled', label:'{{ _("Enabled?") }}',
+          type: 'switch', mode: ['properties']
+        },
+        {
+          id: 'comment', label:'{{ _("Comment") }}', cell: 'string', type: 'multiline'
+        }
+        ],
+        validate: function() {
+
+          // Triggers specific error messages for fields
+          var err = {},
+            errmsg,
+            field_name = this.get('name');
+          if (_.isUndefined(field_name) || _.isNull(field_name) ||
+            String(field_name).replace(/^\s+|\s+$/g, '') === '')
+          {
+            err['name'] = '{{ _("Please specify name.") }}';
+            errmsg = errmsg || err['name'];
+            this.errorModel.set('name', errmsg);
+            return errmsg;
+          }
+          else
+          {
+            this.errorModel.unset('name');
+          }
+          return null;
+        }
+      }),
+
+      // Show or hide create rule menu option on parent node
+      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 rule
+          if (_.indexOf(['schema'], d._type) > -1)
+            return true;
+
+          if ('coll-rule' == d._type) {
+
+            //Check if we are not child of rule
+            prev_i = t.hasParent(i) ? t.parent(i) : null;
+            prev_d = prev_i ? t.itemData(prev_i) : null;
+            prev_j = t.hasParent(prev_i) ? t.parent(prev_i) : null;
+            prev_e = prev_j ? t.itemData(prev_j) : null;
+            prev_k = t.hasParent(prev_j) ? t.parent(prev_j) : null;
+            prev_f = prev_k ? t.itemData(prev_k) : null;
+            if( prev_f._type == 'catalog') {
+              return false;
+            } else {
+              return true;
+            }
+          }
+
+          /**
+            Check if it is view and its parent node is schema
+            then allow to create Rule
+           */
+          else if('view' == d._type){
+            prev_i = t.hasParent(i) ? t.parent(i) : null;
+            prev_d = prev_i ? t.itemData(prev_i) : null;
+            prev_j = t.hasParent(prev_i) ? t.parent(prev_i) : null;
+            prev_e = prev_j ? t.itemData(prev_j) : null;
+            if(prev_e._type == 'schema') {
+              return true;
+            }else{
+              return false;
+            }
+          }
+          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['coll-rule'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/img/coll-table.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/img/coll-table.png
new file mode 100644
index 0000000000000000000000000000000000000000..680e86457e8c48cd01329f0303d663b0304ab44d
GIT binary patch
literal 555
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf-T<EvS0Jr8^?BF1e^bu?TzmD?
zsXMRkJ%0Y|`SWMbo*ljMtZe5CgE=oc&i)J9@M-haw|DQ}Z9e_4>e%o613!~@ehb_5
z*>m*=+of*}=e<^+`SRYqd(Ef+dI3e2yw#Zb^8Wq%j~+dG{P^+Hr%&4tJb&=u!SdzH
z*Q{Cd=FOXT@7}$9`SRhzhs%~NTd`ur+O=!fuV4S_)vF1oK0kl{{PpYCFJ8P@xpL+E
z_wPS`{P_0m+f}PpZP>73)22;No;>;R;X^gZ0Y5ZmzF7BH^&ij!j3q&S!3+-1ZlnP@
zoCO|{#X#BvjNMLV+W{F%JzX3_BreCEKQ7i}Akg}-$5cydO$Teo7MG=+N#Fm<pXSlA
zF*^OfrrVmO&iz!g+vcn9N;KboWe>g3_MdIbtj55z467wYr(8Tf`DM;NgJ}*2Ts9xt
za8DvyRL7)e=Crp3yMhn8I<I_gRVTnDsxoVD`Gh<6ZiW+79FuomTDG#o_)gn1l_K@t
zbJLlaXW8s861@8^@vEZ8?}M3QA`drnbuQa~?`a=@4deV=fh{M$if#frShd78q9i4;
zB-JXpC>2OC7#SEE=o%X78W@Hc8CaPZTbY<@8<<)d7|eU8w+cl=ZhlH;S|vn-fhAZ2
cP_>Dbff+=@sp+9>fEpM)UHx3vIVCg!0JlsKi2wiq

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/img/table-repl-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/img/table-repl-sm.png
new file mode 100644
index 0000000000000000000000000000000000000000..967bd937fb89c483a95146c88fb4c066f6da5711
GIT binary patch
literal 675
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47>xpaLR^7d#i`G`&i$Km{^#1O
zpHAI*b?@=>XV0HMd-m+;jb~*$Ul`1J(Q)=)^XY$8$A0G@_?f)(TiB-0o~u9DE`4h_
z@3s2Om(8dDdI7mh-fGNzx%ukbyLa#2yLa#Y{ris|J$n53@zbYI+YdaSw90wIwfqwo
z*FAoGfBDMAH*a5Fvu4ekH*em(d-w9?%PH&JH{32+bv<wDN}uk<R{dMu)~;Q<e*OAa
zuU>gH?KAH>pEkLwcYDb6lTnjSMzn9Td;a|S>({S~kKHdkaxd?|-Ppw^%jf&_9uI9h
z=v%+m{Qdj)A3uJ4`}S?Y;d{CJ?}SW0kv_e={eWNN9=GZpj#VoSH*DCjY15{heRqN;
z9}AyzI&#6O^#`_{y}aw(m0jnr?*8!ML;jpIsdLYCufG;M>s0K@OF`34WUhOff8dA4
z%okCUjx~DRx&#b0#*!evU<QY0H`0I{&H|6fVj%4S#%?FG?SPCGo-U3d5|@(`6xa-u
z7&{n*jk&d@t=ZYt#m!wB+Nbx&I|O({xQMK3a_3P|&{I_66rC_>+O&yNCx<@>XyMS%
zT)kq|%G22yS((l(B_&_K@bZY7^z`^PFfCiQEbW=tw6JS3X1CO~DKJ`Bv%9OOi-$`m
zo0s!XXjnddd;j|R^$pt!6DEjxC|IbN=-4P3oeI0`tSzM>AS^9Df5MC@JZ0`&N5tB$
zgxJ`zGT48TG(363Ckg0g)e_f;l9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!vr
zU}|MxFz=n-DijU5`6-!cl@JXEmS7D))h1R3W)KahriZQpYGCkm^>bP0l+XkKRw_GR

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/img/table-repl.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/img/table-repl.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a082c67b8c307b673d32fc0df12fb5e857fc1f7
GIT binary patch
literal 839
zcmV-N1GxN&P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D0004@P)t-s0000X
zmFJDn|B}!9qSN)d*zM8Z>EGk$<>u$+=H}Yr<*d%<W3lKio9K(r|Aox{e#rcF!}@H#
z_+hyAR<iR;tnfgm?l++9Eu88mnCXSg{cFDXL8k6Dq3kZ4>a^DG-QC^Z-rnEe-{9cj
z;o;%p;^O1u<K^Y$hrj3O=;-O`>9WCpoxM|ysXD2tspRD3tgNi;?CkCB?eFjJ>gwu{
zsX?E@R<hi7k*`Civx26$XpXHyqqbtLt*x)Guj}jU(AB@Hy?3eBZjQ1*lCnpTy-be1
zOMaOmNP@miior~Y!B~lpo6TUB&0delR))7j@bK{R^78KP?sLZ9am3zk!QEuA)KrS3
zh_ynB#ZrX8OMkLFv9YnTva)c*-fqC%Xt>!@hogbGU53F;fxbw4x<YxcHu3TC^Yiod
z_4Q+})MvNZSDnjMiJhglYLB5*q_=92qE+_x_W1btVXV`1qR&u~#a^q^TBXo%l)1IF
zwYIjlxVX6Y_xF>v*<+N)Ih*5}x!G5j$xe;LSDnnO!`mg6=3t=LR+PtVpU^d$<Y+BQ
z_y7O^0d!JMQvg8b*k%9#010qNS#tmY07w7;07w8v$!k6U007-dL_t&-(_>&@U_=27
zKmjIZ7FITP4o*%kZXRBc03W}AppdYLsF=8fBv=KXlz^Z#P(Vgj4k940prELvtfH!>
z&Zz+wK=3uSzyjJjx|({b`UZwZ#wK6^Q!{f5ODk&|TRS^@uz-W3le3Gfo4b~Yp%Ej4
zho_g9m$#3vpTE0tKwuD1AUGs6EIcAID%w3JHZ}k#5Eq}2n3SB7n&zG!n2`w-h|kK-
z$<50z$Sce&DlP&Fl$4g0S5(FZq*qnf)YLLE)YUgMHZ`}jg47q))`G2VYwzgnf_WbR
zhg&kzn38;O0000bbVXQnWMOn=I%9HWVRU5xGB7bPEip1JFfmjzFgi3dIy5yaFf}?b
zFrMx%ssI20C3HntbYx+4WjbwdWNBu305UK!FfA}SEif@uGBY|fG&(RgD=;-WFfhuO
RRjdF2002ovPDHLkV1h^asowwq

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/img/table.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/img/table.png
new file mode 100644
index 0000000000000000000000000000000000000000..37b2227d8fe0cf76bde23855676403c57ccfb569
GIT binary patch
literal 593
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMfjR2nzS0Jr8^?C1w|C7%DUUd2E
zt{ZPJ-hX-j@$;w8o<Do`?AF7lYtB7S-uOaq_KWWG|69-et2^<h^zg5oeLoU+d<)z3
z*?Y|ghvo0g7rfD(^Ga>T%hof0v-kaQT=vdv;Tyd<ueMx!clYkyd-v|$zkmP1g9i^E
zK791((c{ODpFVxszW@1)7cXAEeEIzO^OY-CK6&zF&6+iD-n@DH_U-%k?_a%owS4*V
z6)RS(UAuPu`t`40zy9#y!>3Q5-o1ObdiClJ8#Zj*xN*~_O&>pg{QUXz*RNl<Zr%Fr
z+qWM-er(&eZO4us-@kt^zO}6y=r_iaAirP+hi5m^fE>;OkH}&m?E%JaC$sH<j18VH
zjv*44L(g9qYBCULeVEWMTP@D5z~~&PxFE^#;otincP*^m-2M4%e`HI`6~&eoAG7KD
zBHj|ywXfb{f7QF}MppT*z5fz8Tx+k09M7NOlP>dY(w2yI*ETJB#CxDVyM_6hsq?*{
z%bR2yj^^b{bb0Zwd(Bu=J=bCx>jtGG3nR*NpRqG!KlWJZ=J(tnh2i}T<FyU3e_r!C
zY?tI_F5B<g&(Uyv`fuYt;f8p5Rhdig_cLtbm*#(5whH7s)e_f;l9a@fRIB8oR3OD*
zWMF8ZYiOivU>IU#U}a)#Wn!vrU}|MxFz=n-DijU5`6-!cl@JXEmS7D))h1R3W)Kah
TriZQpYGCkm^>bP0l+XkKw!A9P

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/alter.sql
new file mode 100644
index 0000000..0fb0ea5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/alter.sql
@@ -0,0 +1,4 @@
+{% if data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..8d8c10a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/create.sql
@@ -0,0 +1,4 @@
+{% if data %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    ADD{% if data.name %} CONSTRAINT {{ conn|qtIdent(data.name) }}{% endif%} CHECK ({{ data.consrc }});
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..5a85b4f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/delete.sql
@@ -0,0 +1,3 @@
+{% if data %}
+ALTER TABLE {{ conn|qtIdent(data.nspname, data.relname) }} DROP CONSTRAINT {{ conn|qtIdent(data.name) }};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_name.sql
new file mode 100644
index 0000000..12dfa15
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_name.sql
@@ -0,0 +1,4 @@
+SELECT conname as name
+FROM pg_constraint ct
+WHERE contype = 'c'
+AND  ct.oid = {{cid}}::oid
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..fa521e6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,7 @@
+SELECT
+    oid, conname as name
+FROM
+    pg_constraint
+WHERE
+    conrelid = {{tid}}::oid
+    AND conname={{ name|qtLiteral }};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_oid_with_transaction.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_oid_with_transaction.sql
new file mode 100644
index 0000000..0f2e29f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_oid_with_transaction.sql
@@ -0,0 +1,5 @@
+SELECT ct.oid,
+    ct.conname as name
+FROM pg_constraint ct
+WHERE contype='c' AND
+    conrelid = {{tid}}::oid LIMIT 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_parent.sql
new file mode 100644
index 0000000..a652857
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/get_parent.sql
@@ -0,0 +1,7 @@
+SELECT nsp.nspname AS schema,
+    rel.relname AS table
+FROM
+    pg_class rel
+JOIN pg_namespace nsp
+ON rel.relnamespace = nsp.oid::int
+WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..0701c9f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/nodes.sql
@@ -0,0 +1,6 @@
+SELECT c.oid, conname as name
+    FROM pg_constraint c
+WHERE contype = 'c'
+{% if tid %}
+    AND conrelid = {{ tid }}::oid
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..18cdb35
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/properties.sql
@@ -0,0 +1,13 @@
+SELECT c.oid, conname as name, relname, nspname, description as comment ,
+       pg_get_expr(conbin, conrelid, true) as consrc
+    FROM pg_constraint c
+    JOIN pg_class cl ON cl.oid=conrelid
+    JOIN pg_namespace nl ON nl.oid=relnamespace
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=c.oid AND
+                           des.classoid='pg_constraint'::regclass)
+WHERE contype = 'c'
+    AND conrelid = {{ tid }}::oid
+{% if cid %}
+    AND c.oid = {{ cid }}::oid
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..57d81c7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.1_plus/update.sql
@@ -0,0 +1,4 @@
+{% if data.comment is defined and data.comment != o_data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(o_data.name) }} ON {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/alter.sql
new file mode 100644
index 0000000..0fb0ea5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/alter.sql
@@ -0,0 +1,4 @@
+{% if data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/create.sql
new file mode 100644
index 0000000..2c7a57f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/create.sql
@@ -0,0 +1,6 @@
+{% if data %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    ADD{% if data.name %} CONSTRAINT {{ conn|qtIdent(data.name) }}{% endif%} CHECK ({{ data.consrc }}){% if data.convalidated %}
+
+    NOT VALID{% endif %}{% if data.connoinherit %} NO INHERIT{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/delete.sql
new file mode 100644
index 0000000..5a85b4f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/delete.sql
@@ -0,0 +1,3 @@
+{% if data %}
+ALTER TABLE {{ conn|qtIdent(data.nspname, data.relname) }} DROP CONSTRAINT {{ conn|qtIdent(data.name) }};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_name.sql
new file mode 100644
index 0000000..a92f893
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_name.sql
@@ -0,0 +1,5 @@
+SELECT conname as name,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype = 'c'
+AND  ct.oid = {{cid}}::oid
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_oid.sql
new file mode 100644
index 0000000..46f32c9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_oid.sql
@@ -0,0 +1,8 @@
+SELECT
+    oid, conname as name,
+    NOT convalidated as convalidated
+FROM
+    pg_constraint
+WHERE
+    conrelid = {{tid}}::oid
+    AND conname={{ name|qtLiteral }};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_oid_with_transaction.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_oid_with_transaction.sql
new file mode 100644
index 0000000..b497e3f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_oid_with_transaction.sql
@@ -0,0 +1,6 @@
+SELECT ct.oid,
+    ct.conname as name,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype='c' AND
+    conrelid = {{tid}}::oid LIMIT 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_parent.sql
new file mode 100644
index 0000000..a652857
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/get_parent.sql
@@ -0,0 +1,7 @@
+SELECT nsp.nspname AS schema,
+    rel.relname AS table
+FROM
+    pg_class rel
+JOIN pg_namespace nsp
+ON rel.relnamespace = nsp.oid::int
+WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/nodes.sql
new file mode 100644
index 0000000..6fdc3c1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/nodes.sql
@@ -0,0 +1,7 @@
+SELECT c.oid, conname as name,
+    NOT convalidated as convalidated
+    FROM pg_constraint c
+WHERE contype = 'c'
+{% if tid %}
+    AND conrelid = {{ tid }}::oid
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/properties.sql
new file mode 100644
index 0000000..509d317
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/properties.sql
@@ -0,0 +1,14 @@
+SELECT c.oid, conname as name, relname, nspname, description as comment,
+       pg_get_expr(conbin, conrelid, true) as consrc,
+       connoinherit, NOT convalidated as convalidated
+    FROM pg_constraint c
+    JOIN pg_class cl ON cl.oid=conrelid
+    JOIN pg_namespace nl ON nl.oid=relnamespace
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=c.oid AND
+                           des.classoid='pg_constraint'::regclass)
+WHERE contype = 'c'
+    AND conrelid = {{ tid }}::oid
+{% if cid %}
+    AND c.oid = {{ cid }}::oid
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/update.sql
new file mode 100644
index 0000000..6c27923
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/update.sql
@@ -0,0 +1,13 @@
+{% if data %}
+{% if data.name != o_data.name %}
+ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif -%}
+{% if 'convalidated' in data and o_data.convalidated != data.convalidated and not data.convalidated %}
+
+ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    VALIDATE CONSTRAINT {{ conn|qtIdent(data.name) }};{% endif -%}
+{% if data.comment is defined and data.comment != o_data.comment %}
+
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    IS {{ data.comment|qtLiteral }};{% endif %}
+{% endif -%}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/validate.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/validate.sql
new file mode 100644
index 0000000..5a62c80
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/check_constraint/sql/9.2_plus/validate.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    VALIDATE CONSTRAINT {{ conn|qtIdent(data.name) }};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/macros/privilege.macros b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/macros/privilege.macros
new file mode 100644
index 0000000..7eafd60
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/macros/privilege.macros
@@ -0,0 +1,13 @@
+{% macro APPLY(conn, schema_name, table_object, column_object, role, privs, with_grant_privs) -%}
+{% if privs %}
+GRANT {% for p in privs %}{% if loop.index != 1 %}, {% endif %}{{p}}({{conn|qtIdent(column_object)}}){% endfor %}
+ ON {{ conn|qtIdent(schema_name, table_object) }} TO {{ conn|qtIdent(role) }};
+{% endif %}
+{% if with_grant_privs %}
+GRANT {% for p in with_grant_privs %}{% if loop.index != 1 %}, {% endif %}{{p}}({{conn|qtIdent(column_object)}}){% endfor %}
+ ON {{ conn|qtIdent(schema_name, table_object) }} TO {{ conn|qtIdent(role) }} WITH GRANT OPTION;
+{% endif %}
+{%- endmacro %}
+{% macro RESETALL(conn, schema_name, table_object, column_object, role) -%}
+REVOKE ALL({{ conn|qtIdent(column_object) }}) ON {{ conn|qtIdent(schema_name, table_object) }} FROM {{ conn|qtIdent(role) }};
+{%- endmacro %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/macros/security.macros b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/macros/security.macros
new file mode 100644
index 0000000..39587c3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/macros/security.macros
@@ -0,0 +1,6 @@
+{% macro APPLY(conn, type, schema_name, parent_object, child_object, provider, label) -%}
+SECURITY LABEL FOR {{ conn|qtIdent(provider) }} ON {{ type }} {{ conn|qtIdent(schema_name, parent_object, child_object) }} IS {{ label|qtLiteral }};
+{%- endmacro %}
+{% macro DROP(conn, type, schema_name, parent_object, child_object, provider) -%}
+SECURITY LABEL FOR {{ conn|qtIdent(provider) }} ON {{ type }} {{ conn|qtIdent(schema_name, parent_object, child_object) }} IS NULL;
+{%- endmacro %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/acl.sql
new file mode 100644
index 0000000..5c44a96
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/acl.sql
@@ -0,0 +1,34 @@
+SELECT 'attacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+  (SELECT
+    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
+  FROM
+    (SELECT attacl
+        FROM pg_attribute att
+        WHERE att.attrelid = {{tid}}::oid
+        AND att.attnum = {{clid}}::int
+    ) acl,
+    (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+        AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+        aclexplode(attacl) as d FROM pg_attribute att
+        WHERE att.attrelid = {{tid}}::oid
+        AND att.attnum = {{clid}}::int) 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
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..51eea52
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/create.sql
@@ -0,0 +1,38 @@
+{% import 'column/macros/security.macros' as SECLABLE %}
+{% import 'column/macros/privilege.macros' as PRIVILEGE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+{###  Add column ###}
+{% if data.name and  data.cltype %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ADD COLUMN {{conn|qtIdent(data.name)}} {{data.cltype}}{% if data.attlen %}
+({{data.attlen}}{% if data.attprecision%}, {{data.attprecision}}{% endif %}){% endif %}{% if data.hasSqrBracket %}
+[]{% endif %}{% if data.collspcname %}
+ COLLATE {{data.collspcname}}{% endif %}{% if data.attnotnull %}
+ NOT NULL{% endif %}{% if data.defval %}
+ DEFAULT {{data.defval}}{% endif %};
+
+{% endif %}
+{###  Add comments ###}
+{% if data and data.description %}
+COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
+    IS {{data.description|qtLiteral}};
+
+{% endif %}
+{###  Add variables to column ###}
+{% if data.attoptions %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, data.attoptions) }}
+
+{% endif %}
+{###  ACL ###}
+{% if data.attacl %}
+{% for priv in data.attacl %}
+{{ PRIVILEGE.APPLY(conn, data.schema, data.table, data.name, priv.grantee, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{###  Security Lables ###}
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..0e16251
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}} DROP COLUMN {{conn|qtIdent(data.name)}};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/depend.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/depend.sql
new file mode 100644
index 0000000..f5f39e7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/depend.sql
@@ -0,0 +1,9 @@
+SELECT
+    ref.relname AS refname, d2.refclassid, dep.deptype AS deptype
+FROM pg_depend dep
+    LEFT JOIN pg_depend d2 ON dep.objid=d2.objid AND dep.refobjid != d2.refobjid
+    LEFT JOIN pg_class ref ON ref.oid=d2.refobjid
+    LEFT JOIN pg_attribute att ON d2.refclassid=att.attrelid AND d2.refobjsubid=att.attnum
+    {{ where }} AND
+    dep.classid=(SELECT oid FROM pg_class WHERE relname='pg_attrdef') AND
+    dep.refobjid NOT IN (SELECT d3.refobjid FROM pg_depend d3 WHERE d3.objid=d2.refobjid)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/edit_mode_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/edit_mode_types.sql
new file mode 100644
index 0000000..8bc6385
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/edit_mode_types.sql
@@ -0,0 +1,5 @@
+SELECT tt.oid, format_type(tt.oid,NULL) AS typname
+  FROM pg_cast
+  JOIN pg_type tt ON tt.oid=casttarget
+WHERE castsource={{type_id}}
+   AND castcontext IN ('i', 'a')
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..803c4d4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+	CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+	  concat(quote_ident(nspname), '.', quote_ident(collname))
+	ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+    WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_inherited_tables.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_inherited_tables.sql
new file mode 100644
index 0000000..37934b8
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_inherited_tables.sql
@@ -0,0 +1,12 @@
+SELECT array_to_string(array_agg(inhrelname), ', ') inhrelname, attrname
+FROM
+ (SELECT
+   inhparent::regclass AS inhrelname,
+   a.attname AS attrname
+  FROM pg_inherits i
+  LEFT JOIN pg_attribute a ON
+   (attrelid = inhparent AND attnum > 0)
+  WHERE inhrelid = {{tid}}::oid
+  ORDER BY inhseqno
+ ) a
+GROUP BY attrname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_parent.sql
new file mode 100644
index 0000000..5dd5d3c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_parent.sql
@@ -0,0 +1,5 @@
+SELECT nsp.nspname AS schema ,rel.relname AS table
+FROM pg_class rel
+    JOIN pg_namespace nsp
+    ON rel.relnamespace = nsp.oid::int
+    WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_position.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_position.sql
new file mode 100644
index 0000000..cea5721
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_position.sql
@@ -0,0 +1,4 @@
+SELECT att.attnum
+FROM pg_attribute att
+    WHERE att.attrelid = {{tid}}::oid
+    AND att.attname = {{data.name|qtLiteral}}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_types.sql
new file mode 100644
index 0000000..469096c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/get_types.sql
@@ -0,0 +1,14 @@
+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'))
+    AND nsp.nspname != 'information_schema'
+    ) AS dummy
+    ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/is_referenced.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/is_referenced.sql
new file mode 100644
index 0000000..7d0bfc3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/is_referenced.sql
@@ -0,0 +1,5 @@
+SELECT COUNT(1)
+FROM pg_depend dep
+    JOIN pg_class cl ON dep.classid=cl.oid AND relname='pg_rewrite'
+    WHERE refobjid= {{tid}}::oid
+    AND refobjsubid= {{clid|qtLiteral}};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..36ed8f6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/nodes.sql
@@ -0,0 +1,18 @@
+SELECT att.attname as name, att.attnum as OID, format_type(ty.oid,NULL) AS datatype
+FROM pg_attribute att
+    JOIN pg_type ty ON ty.oid=atttypid
+    JOIN pg_namespace tn ON tn.oid=ty.typnamespace
+    JOIN pg_class cl ON cl.oid=att.attrelid
+    JOIN pg_namespace na ON na.oid=cl.relnamespace
+    LEFT OUTER JOIN pg_type et ON et.oid=ty.typelem
+    LEFT OUTER JOIN pg_attrdef def ON adrelid=att.attrelid AND adnum=att.attnum
+    LEFT OUTER JOIN (pg_depend JOIN pg_class cs ON objid=cs.oid AND cs.relkind='S') ON refobjid=att.attrelid AND refobjsubid=att.attnum
+    LEFT OUTER JOIN pg_namespace ns ON ns.oid=cs.relnamespace
+    LEFT OUTER JOIN pg_index pi ON pi.indrelid=att.attrelid AND indisprimary
+WHERE att.attrelid = {{tid}}::oid
+{### To show system objects ###}
+{% if not show_sys_objects %}
+    AND att.attnum > 0
+{% endif %}
+    AND att.attisdropped IS FALSE
+    ORDER BY att.attnum
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..d536906
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/properties.sql
@@ -0,0 +1,45 @@
+SELECT att.attname as name, att.*, def.*, pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS defval,
+        CASE WHEN att.attndims > 0 THEN 1 ELSE 0 END AS isarray,
+        format_type(ty.oid,NULL) AS typname,
+        format_type(ty.oid,att.atttypmod) AS displaytypname,
+        tn.nspname as typnspname, et.typname as elemtypname,
+        ty.typstorage AS defaultstorage, cl.relname, na.nspname,
+        concat(quote_ident(na.nspname) ,'.', quote_ident(cl.relname)) AS parent_tbl,
+	att.attstattarget, description, cs.relname AS sername,
+	ns.nspname AS serschema,
+	(SELECT count(1) FROM pg_type t2 WHERE t2.typname=ty.typname) > 1 AS isdup,
+	indkey, coll.collname, nspc.nspname as collnspname , attoptions,
+	-- Start pgAdmin4, added to save time on client side parsing
+	CASE WHEN length(coll.collname) > 0 AND length(nspc.nspname) > 0  THEN
+	  concat(quote_ident(coll.collname),'.',quote_ident(nspc.nspname))
+	ELSE '' END AS collspcname,
+	CASE WHEN strpos(format_type(ty.oid,att.atttypmod), '.') > 0 THEN
+	  split_part(format_type(ty.oid,att.atttypmod), '.', 2)
+	ELSE format_type(ty.oid,att.atttypmod) END AS cltype,
+	-- End pgAdmin4
+	EXISTS(SELECT 1 FROM pg_constraint WHERE conrelid=att.attrelid AND contype='f' AND att.attnum=ANY(conkey)) As is_fk,
+	(SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=att.atttypid AND sl1.objsubid=0) AS seclabels,
+	(CASE WHEN (att.attnum < 1) THEN true ElSE false END) AS is_sys_column
+FROM pg_attribute att
+  JOIN pg_type ty ON ty.oid=atttypid
+  JOIN pg_namespace tn ON tn.oid=ty.typnamespace
+  JOIN pg_class cl ON cl.oid=att.attrelid
+  JOIN pg_namespace na ON na.oid=cl.relnamespace
+  LEFT OUTER JOIN pg_type et ON et.oid=ty.typelem
+  LEFT OUTER JOIN pg_attrdef def ON adrelid=att.attrelid AND adnum=att.attnum
+  LEFT OUTER JOIN pg_description des ON (des.objoid=att.attrelid AND des.objsubid=att.attnum AND des.classoid='pg_class'::regclass)
+  LEFT OUTER JOIN (pg_depend JOIN pg_class cs ON objid=cs.oid AND cs.relkind='S') ON refobjid=att.attrelid AND refobjsubid=att.attnum
+  LEFT OUTER JOIN pg_namespace ns ON ns.oid=cs.relnamespace
+  LEFT OUTER JOIN pg_index pi ON pi.indrelid=att.attrelid AND indisprimary
+  LEFT OUTER JOIN pg_collation coll ON att.attcollation=coll.oid
+  LEFT OUTER JOIN pg_namespace nspc ON coll.collnamespace=nspc.oid
+WHERE att.attrelid = {{tid}}::oid
+{% if clid %}
+    AND att.attnum = {{clid}}::int
+{% endif %}
+{### To show system objects ###}
+{% if not show_sys_objects %}
+    AND att.attnum > 0
+{% endif %}
+    AND att.attisdropped IS FALSE
+    ORDER BY att.attnum
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..a36c6fc
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.1_plus/update.sql
@@ -0,0 +1,107 @@
+{% import 'column/macros/security.macros' as SECLABLE %}
+{% import 'column/macros/privilege.macros' as PRIVILEGE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+{###  Rename column name ###}
+{% if data.name != o_data.name %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    RENAME {{conn|qtIdent(o_data.name)}} TO {{conn|qtIdent(data.name)}};
+
+{% endif %}
+{###  Alter column type and collation ###}
+{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen and data.attlen != o_data.attlen) or (data.attprecision and data.attprecision != o_data.attprecision) %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ALTER COLUMN {{conn|qtIdent(data.name)}} TYPE {% if data.cltype %}{{data.cltype}} {% else %}{{o_data.cltype}} {% endif %}{% if data.attlen %}
+({{data.attlen}}{% if data.attprecision%}, {{data.attprecision}}{% endif %}){% endif %}{% if data.hasSqrBracket %}
+[]{% endif %}{% if data.collspcname and data.collspcname != o_data.collspcname %}
+ COLLATE {{data.collspcname}}{% endif %};
+
+{% endif %}
+{###  Alter column default value ###}
+{% if data.defval and data.defval != o_data.defval %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ALTER COLUMN {{conn|qtIdent(data.name)}} SET DEFAULT {{data.defval}};
+
+{% endif %}
+{###  Alter column not null value ###}
+{% if 'attnotnull' in data and data.attnotnull != o_data.attnotnull %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ALTER COLUMN {{conn|qtIdent(data.name)}} {% if data.attnotnull %}SET{% else %}DROP{% endif %} NOT NULL;
+
+{% endif %}
+{###  Alter column statistics value ###}
+{% if data.attstattarget and data.attstattarget != o_data.attstattarget %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ALTER COLUMN {{conn|qtIdent(data.name)}} SET STATISTICS {{data.attstattarget}};
+
+{% endif %}
+{###  Alter column storage value ###}
+{% if data.attstorage and data.attstorage != o_data.attstorage %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ALTER COLUMN {{conn|qtIdent(data.name)}} SET STORAGE {%if data.attstorage == 'p' %}
+PLAIN{% elif data.attstorage == 'm'%}MAIN{% elif data.attstorage == 'e'%}
+EXTERNAL{% elif data.attstorage == 'x'%}EXTENDED{% endif %};
+
+{% endif %}
+{% if data.description %}
+COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
+    IS {{data.description|qtLiteral}};
+
+{% endif %}
+{### Update column variables ###}
+{% if 'attoptions' in data and data.attoptions|length > 0 %}
+{% set variables = data.attoptions %}
+{% if 'deleted' in variables and variables.deleted|length > 0 %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    {{ VARIABLE.UNSET(conn, 'COLUMN', data.name, variables.deleted) }}
+{% endif %}
+{% if 'added' in variables and variables.added|length > 0 %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.added) }}
+{% endif %}
+{% if 'changed' in variables and variables.changed|length > 0 %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.changed) }}
+{% endif %}
+
+{% endif %}
+{### Update column privileges ###}
+{# Change the privileges #}
+{% if data.attacl %}
+{% if 'deleted' in data.attacl %}
+{% for priv in data.attacl.deleted %}
+{{ PRIVILEGE.RESETALL(conn, data.schema, data.table, data.name, priv.grantee) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.attacl %}
+{% for priv in data.attacl.changed %}
+{{ PRIVILEGE.RESETALL(conn, data.schema, data.table, data.name, priv.grantee) }}
+{{ PRIVILEGE.APPLY(conn, data.schema, data.table, data.name, priv.grantee, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.attacl %}
+{% for priv in data.attacl.added %}
+{{ PRIVILEGE.APPLY(conn, data.schema, data.table, data.name, priv.grantee, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{### Uppdate tablespace securitylabel ###}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'COLUMN', data.schema, data.table, data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/acl.sql
new file mode 100644
index 0000000..5c44a96
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/acl.sql
@@ -0,0 +1,34 @@
+SELECT 'attacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+  (SELECT
+    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
+  FROM
+    (SELECT attacl
+        FROM pg_attribute att
+        WHERE att.attrelid = {{tid}}::oid
+        AND att.attnum = {{clid}}::int
+    ) acl,
+    (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+        AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+        aclexplode(attacl) as d FROM pg_attribute att
+        WHERE att.attrelid = {{tid}}::oid
+        AND att.attnum = {{clid}}::int) 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
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/create.sql
new file mode 100644
index 0000000..51eea52
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/create.sql
@@ -0,0 +1,38 @@
+{% import 'column/macros/security.macros' as SECLABLE %}
+{% import 'column/macros/privilege.macros' as PRIVILEGE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+{###  Add column ###}
+{% if data.name and  data.cltype %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ADD COLUMN {{conn|qtIdent(data.name)}} {{data.cltype}}{% if data.attlen %}
+({{data.attlen}}{% if data.attprecision%}, {{data.attprecision}}{% endif %}){% endif %}{% if data.hasSqrBracket %}
+[]{% endif %}{% if data.collspcname %}
+ COLLATE {{data.collspcname}}{% endif %}{% if data.attnotnull %}
+ NOT NULL{% endif %}{% if data.defval %}
+ DEFAULT {{data.defval}}{% endif %};
+
+{% endif %}
+{###  Add comments ###}
+{% if data and data.description %}
+COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
+    IS {{data.description|qtLiteral}};
+
+{% endif %}
+{###  Add variables to column ###}
+{% if data.attoptions %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, data.attoptions) }}
+
+{% endif %}
+{###  ACL ###}
+{% if data.attacl %}
+{% for priv in data.attacl %}
+{{ PRIVILEGE.APPLY(conn, data.schema, data.table, data.name, priv.grantee, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{###  Security Lables ###}
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/delete.sql
new file mode 100644
index 0000000..0e16251
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/delete.sql
@@ -0,0 +1 @@
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}} DROP COLUMN {{conn|qtIdent(data.name)}};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/depend.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/depend.sql
new file mode 100644
index 0000000..f5f39e7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/depend.sql
@@ -0,0 +1,9 @@
+SELECT
+    ref.relname AS refname, d2.refclassid, dep.deptype AS deptype
+FROM pg_depend dep
+    LEFT JOIN pg_depend d2 ON dep.objid=d2.objid AND dep.refobjid != d2.refobjid
+    LEFT JOIN pg_class ref ON ref.oid=d2.refobjid
+    LEFT JOIN pg_attribute att ON d2.refclassid=att.attrelid AND d2.refobjsubid=att.attnum
+    {{ where }} AND
+    dep.classid=(SELECT oid FROM pg_class WHERE relname='pg_attrdef') AND
+    dep.refobjid NOT IN (SELECT d3.refobjid FROM pg_depend d3 WHERE d3.objid=d2.refobjid)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/edit_mode_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/edit_mode_types.sql
new file mode 100644
index 0000000..0c112c5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/edit_mode_types.sql
@@ -0,0 +1,5 @@
+SELECT tt.oid, format_type(tt.oid,NULL) AS typname
+FROM pg_cast
+    JOIN pg_type tt ON tt.oid=casttarget
+WHERE castsource={{type_id}}
+    AND castcontext IN ('i', 'a')
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_collations.sql
new file mode 100644
index 0000000..7418742
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+	CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+	  concat(quote_ident(nspname), '.', quote_ident(collname))
+	ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+    WHERE c.collnamespace=n.oid
+    ORDER BY nspname, collname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_inherited_tables.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_inherited_tables.sql
new file mode 100644
index 0000000..37934b8
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_inherited_tables.sql
@@ -0,0 +1,12 @@
+SELECT array_to_string(array_agg(inhrelname), ', ') inhrelname, attrname
+FROM
+ (SELECT
+   inhparent::regclass AS inhrelname,
+   a.attname AS attrname
+  FROM pg_inherits i
+  LEFT JOIN pg_attribute a ON
+   (attrelid = inhparent AND attnum > 0)
+  WHERE inhrelid = {{tid}}::oid
+  ORDER BY inhseqno
+ ) a
+GROUP BY attrname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_parent.sql
new file mode 100644
index 0000000..5dd5d3c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_parent.sql
@@ -0,0 +1,5 @@
+SELECT nsp.nspname AS schema ,rel.relname AS table
+FROM pg_class rel
+    JOIN pg_namespace nsp
+    ON rel.relnamespace = nsp.oid::int
+    WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_position.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_position.sql
new file mode 100644
index 0000000..cea5721
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_position.sql
@@ -0,0 +1,4 @@
+SELECT att.attnum
+FROM pg_attribute att
+    WHERE att.attrelid = {{tid}}::oid
+    AND att.attname = {{data.name|qtLiteral}}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_types.sql
new file mode 100644
index 0000000..469096c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/get_types.sql
@@ -0,0 +1,14 @@
+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'))
+    AND nsp.nspname != 'information_schema'
+    ) AS dummy
+    ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/is_referenced.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/is_referenced.sql
new file mode 100644
index 0000000..7d0bfc3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/is_referenced.sql
@@ -0,0 +1,5 @@
+SELECT COUNT(1)
+FROM pg_depend dep
+    JOIN pg_class cl ON dep.classid=cl.oid AND relname='pg_rewrite'
+    WHERE refobjid= {{tid}}::oid
+    AND refobjsubid= {{clid|qtLiteral}};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql
new file mode 100644
index 0000000..7a15333
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql
@@ -0,0 +1,18 @@
+SELECT att.attname as name, att.attnum as OID, format_type(ty.oid,NULL) AS datatype
+FROM pg_attribute att
+  JOIN pg_type ty ON ty.oid=atttypid
+  JOIN pg_namespace tn ON tn.oid=ty.typnamespace
+  JOIN pg_class cl ON cl.oid=att.attrelid
+  JOIN pg_namespace na ON na.oid=cl.relnamespace
+  LEFT OUTER JOIN pg_type et ON et.oid=ty.typelem
+  LEFT OUTER JOIN pg_attrdef def ON adrelid=att.attrelid AND adnum=att.attnum
+  LEFT OUTER JOIN (pg_depend JOIN pg_class cs ON objid=cs.oid AND cs.relkind='S') ON refobjid=att.attrelid AND refobjsubid=att.attnum
+  LEFT OUTER JOIN pg_namespace ns ON ns.oid=cs.relnamespace
+  LEFT OUTER JOIN pg_index pi ON pi.indrelid=att.attrelid AND indisprimary
+WHERE att.attrelid = {{tid}}::oid
+    {### To show system objects ###}
+    {% if not show_sys_objects %}
+    AND att.attnum > 0
+    {% endif %}
+    AND att.attisdropped IS FALSE
+    ORDER BY att.attnum
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/properties.sql
new file mode 100644
index 0000000..d536906
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/properties.sql
@@ -0,0 +1,45 @@
+SELECT att.attname as name, att.*, def.*, pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS defval,
+        CASE WHEN att.attndims > 0 THEN 1 ELSE 0 END AS isarray,
+        format_type(ty.oid,NULL) AS typname,
+        format_type(ty.oid,att.atttypmod) AS displaytypname,
+        tn.nspname as typnspname, et.typname as elemtypname,
+        ty.typstorage AS defaultstorage, cl.relname, na.nspname,
+        concat(quote_ident(na.nspname) ,'.', quote_ident(cl.relname)) AS parent_tbl,
+	att.attstattarget, description, cs.relname AS sername,
+	ns.nspname AS serschema,
+	(SELECT count(1) FROM pg_type t2 WHERE t2.typname=ty.typname) > 1 AS isdup,
+	indkey, coll.collname, nspc.nspname as collnspname , attoptions,
+	-- Start pgAdmin4, added to save time on client side parsing
+	CASE WHEN length(coll.collname) > 0 AND length(nspc.nspname) > 0  THEN
+	  concat(quote_ident(coll.collname),'.',quote_ident(nspc.nspname))
+	ELSE '' END AS collspcname,
+	CASE WHEN strpos(format_type(ty.oid,att.atttypmod), '.') > 0 THEN
+	  split_part(format_type(ty.oid,att.atttypmod), '.', 2)
+	ELSE format_type(ty.oid,att.atttypmod) END AS cltype,
+	-- End pgAdmin4
+	EXISTS(SELECT 1 FROM pg_constraint WHERE conrelid=att.attrelid AND contype='f' AND att.attnum=ANY(conkey)) As is_fk,
+	(SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=att.atttypid AND sl1.objsubid=0) AS seclabels,
+	(CASE WHEN (att.attnum < 1) THEN true ElSE false END) AS is_sys_column
+FROM pg_attribute att
+  JOIN pg_type ty ON ty.oid=atttypid
+  JOIN pg_namespace tn ON tn.oid=ty.typnamespace
+  JOIN pg_class cl ON cl.oid=att.attrelid
+  JOIN pg_namespace na ON na.oid=cl.relnamespace
+  LEFT OUTER JOIN pg_type et ON et.oid=ty.typelem
+  LEFT OUTER JOIN pg_attrdef def ON adrelid=att.attrelid AND adnum=att.attnum
+  LEFT OUTER JOIN pg_description des ON (des.objoid=att.attrelid AND des.objsubid=att.attnum AND des.classoid='pg_class'::regclass)
+  LEFT OUTER JOIN (pg_depend JOIN pg_class cs ON objid=cs.oid AND cs.relkind='S') ON refobjid=att.attrelid AND refobjsubid=att.attnum
+  LEFT OUTER JOIN pg_namespace ns ON ns.oid=cs.relnamespace
+  LEFT OUTER JOIN pg_index pi ON pi.indrelid=att.attrelid AND indisprimary
+  LEFT OUTER JOIN pg_collation coll ON att.attcollation=coll.oid
+  LEFT OUTER JOIN pg_namespace nspc ON coll.collnamespace=nspc.oid
+WHERE att.attrelid = {{tid}}::oid
+{% if clid %}
+    AND att.attnum = {{clid}}::int
+{% endif %}
+{### To show system objects ###}
+{% if not show_sys_objects %}
+    AND att.attnum > 0
+{% endif %}
+    AND att.attisdropped IS FALSE
+    ORDER BY att.attnum
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/update.sql
new file mode 100644
index 0000000..bcfb90c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/update.sql
@@ -0,0 +1,105 @@
+{% import 'column/macros/security.macros' as SECLABLE %}
+{% import 'column/macros/privilege.macros' as PRIVILEGE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+{###  Rename column name ###}
+{% if data.name != o_data.name %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    RENAME {{conn|qtIdent(o_data.name)}} TO {{conn|qtIdent(data.name)}};
+
+{% endif %}
+{###  Alter column type and collation ###}
+{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen and data.attlen != o_data.attlen) or (data.attprecision and data.attprecision != o_data.attprecision) %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ALTER COLUMN {{conn|qtIdent(data.name)}} TYPE {% if data.cltype %}{{data.cltype}} {% else %}{{o_data.cltype}} {% endif %}{% if data.attlen %}
+({{data.attlen}}{% if data.attprecision%}, {{data.attprecision}}{% endif %}){% endif %}{% if data.hasSqrBracket %}
+[]{% endif %}{% if data.collspcname and data.collspcname != o_data.collspcname %}
+ COLLATE {{data.collspcname}}{% endif %};
+
+{% endif %}
+{###  Alter column default value ###}
+{% if data.defval and data.defval != o_data.defval %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ALTER COLUMN {{conn|qtIdent(data.name)}} SET DEFAULT {{data.defval}};
+
+{% endif %}
+{###  Alter column not null value ###}
+{% if 'attnotnull' in data and data.attnotnull != o_data.attnotnull %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ALTER COLUMN {{conn|qtIdent(data.name)}} {% if data.attnotnull %}SET{% else %}DROP{% endif %} NOT NULL;
+
+{% endif %}
+{###  Alter column statistics value ###}
+{% if data.attstattarget and data.attstattarget != o_data.attstattarget %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ALTER COLUMN {{conn|qtIdent(data.name)}} SET STATISTICS {{data.attstattarget}};
+
+{% endif %}
+{###  Alter column storage value ###}
+{% if data.attstorage and data.attstorage != o_data.attstorage %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    ALTER COLUMN {{conn|qtIdent(data.name)}} SET STORAGE {%if data.attstorage == 'p' %}
+PLAIN{% elif data.attstorage == 'm'%}MAIN{% elif data.attstorage == 'e'%}
+EXTERNAL{% elif data.attstorage == 'x'%}EXTENDED{% endif %};
+
+{% endif %}
+{% if data.description %}
+COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
+    IS {{data.description|qtLiteral}};
+
+{% endif %}
+{### Update column variables ###}
+{% if 'attoptions' in data and data.attoptions|length > 0 %}
+{% set variables = data.attoptions %}
+{% if 'deleted' in variables and variables.deleted|length > 0 %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    {{ VARIABLE.UNSET(conn, 'COLUMN', data.name, variables.deleted) }}
+{% endif %}
+{% if 'added' in variables and variables.added|length > 0 %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.added) }}
+{% endif %}
+{% if 'changed' in variables and variables.changed|length > 0 %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    {{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.changed) }}
+{% endif %}
+{% endif %}
+{### Update column privileges ###}
+{# Change the privileges #}
+{% if data.attacl %}
+{% if 'deleted' in data.attacl %}
+{% for priv in data.attacl.deleted %}
+{{ PRIVILEGE.RESETALL(conn, data.schema, data.table, data.name, priv.grantee) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.attacl %}
+{% for priv in data.attacl.changed %}
+{{ PRIVILEGE.RESETALL(conn, data.schema, data.table, data.name, priv.grantee) }}
+{{ PRIVILEGE.APPLY(conn, data.schema, data.table, data.name, priv.grantee, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.attacl %}
+{% for priv in data.attacl.added %}
+{{ PRIVILEGE.APPLY(conn, data.schema, data.table, data.name, priv.grantee, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{### Uppdate tablespace securitylabel ###}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'COLUMN', data.schema, data.table, data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.table, data.name, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/alter.sql
new file mode 100644
index 0000000..0fb0ea5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/alter.sql
@@ -0,0 +1,4 @@
+{% if data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/begin.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/begin.sql
new file mode 100644
index 0000000..58bfee1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/begin.sql
@@ -0,0 +1 @@
+BEGIN;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..c6a53cd
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/create.sql
@@ -0,0 +1,12 @@
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    ADD{% if data.name %} CONSTRAINT {{ conn|qtIdent(data.name) }}{% endif%} EXCLUDE {% if data.amname and data.amname != '' %}USING {{data.amname}}{% endif %} (
+    {% for col in data.columns %}{% if loop.index != 1 %},
+    {% endif %}{{ conn|qtIdent(col.column)}} {% if col.oper_class and col.oper_class != '' %}{{col.oper_class}} {% endif%}{% if col.order %}ASC{% else %}DESC{% endif %} NULLS {% if col.nulls_order %}FIRST{% else %}LAST{% endif %} WITH {{col.operator}}{% endfor %}){% if data.fillfactor %}
+    WITH (FILLFACTOR={{data.fillfactor}}){% endif %}{% if data.spcname and data.spcname != "pg_default" %}
+
+    USING INDEX TABLESPACE {{ conn|qtIdent(data.spcname) }}{% endif %}
+{% if data.condeferrable %}
+
+    DEFERRABLE{% if data.condeferred %}
+ INITIALLY DEFERRED{% endif%}
+{% endif%}{% if data.constraint %} WHERE ({{data.constraint}}){% endif%};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..2096795
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/delete.sql
@@ -0,0 +1,3 @@
+{% if data %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }} DROP CONSTRAINT {{ conn|qtIdent(data.name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/end.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/end.sql
new file mode 100644
index 0000000..92d09d5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/end.sql
@@ -0,0 +1 @@
+END;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_access_methods.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_access_methods.sql
new file mode 100644
index 0000000..5d74057
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_access_methods.sql
@@ -0,0 +1,6 @@
+SELECT amname
+FROM pg_am
+WHERE EXISTS (SELECT 1
+              FROM pg_proc
+              WHERE oid=amgettuple)
+ORDER BY amname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_constraint_cols.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_constraint_cols.sql
new file mode 100644
index 0000000..cc1a902
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_constraint_cols.sql
@@ -0,0 +1,22 @@
+{% for n in range(colcnt|int) %}
+{% if loop.index != 1 %}
+UNION
+{% endif %}
+SELECT
+  i.indoption[{{loop.index -1}}] AS options,
+  pg_get_indexdef(i.indexrelid, {{loop.index}}, true) AS coldef,
+  op.oprname,
+  CASE WHEN (o.opcdefault = FALSE) THEN o.opcname ELSE null END AS opcname
+,
+  coll.collname,
+  nspc.nspname as collnspname,
+  format_type(ty.oid,NULL) AS col_type
+FROM pg_index i
+JOIN pg_attribute a ON (a.attrelid = i.indexrelid AND attnum = {{loop.index}})
+JOIN pg_type ty ON ty.oid=a.atttypid
+LEFT OUTER JOIN pg_opclass o ON (o.oid = i.indclass[{{loop.index -1}}])
+LEFT OUTER JOIN pg_constraint c ON (c.conindid = i.indexrelid) LEFT OUTER JOIN pg_operator op ON (op.oid = c.conexclop[{{loop.index}}])
+LEFT OUTER JOIN pg_collation coll ON a.attcollation=coll.oid
+LEFT OUTER JOIN pg_namespace nspc ON coll.collnamespace=nspc.oid
+WHERE i.indexrelid = {{cid}}::oid
+{% endfor %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_name.sql
new file mode 100644
index 0000000..7aaa522
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_name.sql
@@ -0,0 +1,3 @@
+SELECT conname as name
+FROM pg_constraint ct
+WHERE ct.conindid = {{cid}}::oid
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..168e13b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,4 @@
+SELECT ct.oid
+FROM pg_constraint ct
+WHERE contype='x' AND
+ct.conname = {{ name|qtLiteral }};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_oid_with_transaction.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_oid_with_transaction.sql
new file mode 100644
index 0000000..abe636a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_oid_with_transaction.sql
@@ -0,0 +1,6 @@
+SELECT ct.oid,
+    ct.conname as name,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype='f' AND
+    conrelid = {{tid}}::oid LIMIT 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_oper_class.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_oper_class.sql
new file mode 100644
index 0000000..c6739d0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_oper_class.sql
@@ -0,0 +1,7 @@
+SELECT opcname
+FROM pg_opclass opc,
+pg_am am
+WHERE opcmethod=am.oid AND
+      am.amname ={{indextype|qtLiteral}} AND
+      NOT opcdefault
+ORDER BY 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_operator.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_operator.sql
new file mode 100644
index 0000000..9978b96
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_operator.sql
@@ -0,0 +1,29 @@
+SELECT DISTINCT op.oprname as oprname
+FROM pg_operator op,
+( SELECT oid
+  FROM (SELECT format_type(t.oid,NULL) AS typname,
+      t.oid as oid
+      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'))
+    {% if not show_sysobj %}
+      AND nsp.nspname != 'information_schema'
+    {% endif %}
+      UNION SELECT 'bigserial', 0
+      UNION SELECT 'serial', 0) t1
+      WHERE typname = {{type|qtLiteral}}) AS types
+WHERE oprcom > 0 AND
+      (op.oprleft=types.oid OR op.oprright=types.oid)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_parent.sql
new file mode 100644
index 0000000..a652857
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/get_parent.sql
@@ -0,0 +1,7 @@
+SELECT nsp.nspname AS schema,
+    rel.relname AS table
+FROM
+    pg_class rel
+JOIN pg_namespace nsp
+ON rel.relnamespace = nsp.oid::int
+WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..c67c40d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/nodes.sql
@@ -0,0 +1,7 @@
+SELECT conindid as oid,
+    conname as name,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype='x' AND
+    conrelid = {{tid}}::oid
+ORDER BY conname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..3a1c897
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/properties.sql
@@ -0,0 +1,30 @@
+SELECT cls.oid,
+    cls.relname as name,
+    indnatts,
+    amname,
+    COALESCE(spcname, 'pg_default') as spcname,
+    CASE contype
+        WHEN 'p' THEN desp.description
+        WHEN 'u' THEN desp.description
+        WHEN 'x' THEN desp.description
+        ELSE des.description
+    END AS comment,
+    condeferrable,
+    condeferred,
+    substring(array_to_string(cls.reloptions, ',') from 'fillfactor=([0-9]*)') AS fillfactor
+FROM pg_index idx
+JOIN pg_class cls ON cls.oid=indexrelid
+JOIN pg_class tab ON tab.oid=indrelid
+LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace
+JOIN pg_namespace n ON n.oid=tab.relnamespace
+JOIN pg_am am ON am.oid=cls.relam
+LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
+LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
+LEFT OUTER JOIN pg_description des ON (des.objoid=cls.oid AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0 AND desp.classoid='pg_constraint'::regclass)
+WHERE indrelid = {{tid}}::oid
+{% if cid %}
+AND cls.oid = {{cid}}::oid
+{% endif %}
+AND contype='x'
+ORDER BY cls.relname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..4d70f21
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.1_plus/update.sql
@@ -0,0 +1,22 @@
+{### SQL to update exclusion constraint object ###}
+{% if data %}
+{# ==== To update exclusion constraint name ==== #}
+{% if data.name != o_data.name %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{# ==== To update exclusion constraint tablespace ==== #}
+{% if data.spcname and data.spcname != o_data.spcname %}
+ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
+    SET TABLESPACE {{ conn|qtIdent(data.spcname) }};
+{% endif %}
+{% if data.fillfactor and data.fillfactor != o_data.fillfactor %}
+ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
+    SET (FILLFACTOR={{ data.fillfactor }});
+{% endif %}
+{# ==== To update exclusion constraint comments ==== #}
+{% if data.comment is defined and data.comment != o_data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/alter.sql
new file mode 100644
index 0000000..0fb0ea5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/alter.sql
@@ -0,0 +1,4 @@
+{% if data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/begin.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/begin.sql
new file mode 100644
index 0000000..58bfee1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/begin.sql
@@ -0,0 +1 @@
+BEGIN;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/create.sql
new file mode 100644
index 0000000..c6a53cd
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/create.sql
@@ -0,0 +1,12 @@
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    ADD{% if data.name %} CONSTRAINT {{ conn|qtIdent(data.name) }}{% endif%} EXCLUDE {% if data.amname and data.amname != '' %}USING {{data.amname}}{% endif %} (
+    {% for col in data.columns %}{% if loop.index != 1 %},
+    {% endif %}{{ conn|qtIdent(col.column)}} {% if col.oper_class and col.oper_class != '' %}{{col.oper_class}} {% endif%}{% if col.order %}ASC{% else %}DESC{% endif %} NULLS {% if col.nulls_order %}FIRST{% else %}LAST{% endif %} WITH {{col.operator}}{% endfor %}){% if data.fillfactor %}
+    WITH (FILLFACTOR={{data.fillfactor}}){% endif %}{% if data.spcname and data.spcname != "pg_default" %}
+
+    USING INDEX TABLESPACE {{ conn|qtIdent(data.spcname) }}{% endif %}
+{% if data.condeferrable %}
+
+    DEFERRABLE{% if data.condeferred %}
+ INITIALLY DEFERRED{% endif%}
+{% endif%}{% if data.constraint %} WHERE ({{data.constraint}}){% endif%};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/delete.sql
new file mode 100644
index 0000000..2096795
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/delete.sql
@@ -0,0 +1,3 @@
+{% if data %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }} DROP CONSTRAINT {{ conn|qtIdent(data.name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/end.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/end.sql
new file mode 100644
index 0000000..92d09d5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/end.sql
@@ -0,0 +1 @@
+END;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_access_methods.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_access_methods.sql
new file mode 100644
index 0000000..5d74057
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_access_methods.sql
@@ -0,0 +1,6 @@
+SELECT amname
+FROM pg_am
+WHERE EXISTS (SELECT 1
+              FROM pg_proc
+              WHERE oid=amgettuple)
+ORDER BY amname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_constraint_cols.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_constraint_cols.sql
new file mode 100644
index 0000000..c119ccb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_constraint_cols.sql
@@ -0,0 +1,22 @@
+{% for n in range(colcnt|int) %}
+{% if loop.index != 1 %}
+UNION
+{% endif %}
+SELECT
+  i.indoption[{{loop.index -1}}] AS options,
+  pg_get_indexdef(i.indexrelid, {{loop.index}}, true) AS coldef,
+  op.oprname,
+  CASE WHEN (o.opcdefault = FALSE) THEN o.opcname ELSE null END AS opcname
+,
+  coll.collname,
+  nspc.nspname as collnspname,
+  format_type(ty.oid,NULL) AS datatype
+FROM pg_index i
+JOIN pg_attribute a ON (a.attrelid = i.indexrelid AND attnum = {{loop.index}})
+JOIN pg_type ty ON ty.oid=a.atttypid
+LEFT OUTER JOIN pg_opclass o ON (o.oid = i.indclass[{{loop.index -1}}])
+LEFT OUTER JOIN pg_constraint c ON (c.conindid = i.indexrelid) LEFT OUTER JOIN pg_operator op ON (op.oid = c.conexclop[{{loop.index}}])
+LEFT OUTER JOIN pg_collation coll ON a.attcollation=coll.oid
+LEFT OUTER JOIN pg_namespace nspc ON coll.collnamespace=nspc.oid
+WHERE i.indexrelid = {{cid}}::oid
+{% endfor %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_name.sql
new file mode 100644
index 0000000..7aaa522
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_name.sql
@@ -0,0 +1,3 @@
+SELECT conname as name
+FROM pg_constraint ct
+WHERE ct.conindid = {{cid}}::oid
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_oid.sql
new file mode 100644
index 0000000..168e13b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_oid.sql
@@ -0,0 +1,4 @@
+SELECT ct.oid
+FROM pg_constraint ct
+WHERE contype='x' AND
+ct.conname = {{ name|qtLiteral }};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_oid_with_transaction.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_oid_with_transaction.sql
new file mode 100644
index 0000000..abe636a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_oid_with_transaction.sql
@@ -0,0 +1,6 @@
+SELECT ct.oid,
+    ct.conname as name,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype='f' AND
+    conrelid = {{tid}}::oid LIMIT 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_oper_class.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_oper_class.sql
new file mode 100644
index 0000000..c6739d0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_oper_class.sql
@@ -0,0 +1,7 @@
+SELECT opcname
+FROM pg_opclass opc,
+pg_am am
+WHERE opcmethod=am.oid AND
+      am.amname ={{indextype|qtLiteral}} AND
+      NOT opcdefault
+ORDER BY 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_operator.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_operator.sql
new file mode 100644
index 0000000..675a7de
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_operator.sql
@@ -0,0 +1,30 @@
+SELECT DISTINCT op.oprname as oprname
+FROM pg_operator op,
+( SELECT oid
+  FROM (SELECT format_type(t.oid,NULL) AS typname,
+      t.oid as oid
+      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'))
+    {% if not show_sysobj %}
+      AND nsp.nspname != 'information_schema'
+    {% endif %}
+      UNION SELECT 'smallserial', 0
+      UNION SELECT 'bigserial', 0
+      UNION SELECT 'serial', 0) t1
+      WHERE typname = {{type|qtLiteral}}) AS types
+WHERE oprcom > 0 AND
+      (op.oprleft=types.oid OR op.oprright=types.oid)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_parent.sql
new file mode 100644
index 0000000..a652857
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/get_parent.sql
@@ -0,0 +1,7 @@
+SELECT nsp.nspname AS schema,
+    rel.relname AS table
+FROM
+    pg_class rel
+JOIN pg_namespace nsp
+ON rel.relnamespace = nsp.oid::int
+WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/nodes.sql
new file mode 100644
index 0000000..c67c40d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/nodes.sql
@@ -0,0 +1,7 @@
+SELECT conindid as oid,
+    conname as name,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype='x' AND
+    conrelid = {{tid}}::oid
+ORDER BY conname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/properties.sql
new file mode 100644
index 0000000..3a1c897
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/properties.sql
@@ -0,0 +1,30 @@
+SELECT cls.oid,
+    cls.relname as name,
+    indnatts,
+    amname,
+    COALESCE(spcname, 'pg_default') as spcname,
+    CASE contype
+        WHEN 'p' THEN desp.description
+        WHEN 'u' THEN desp.description
+        WHEN 'x' THEN desp.description
+        ELSE des.description
+    END AS comment,
+    condeferrable,
+    condeferred,
+    substring(array_to_string(cls.reloptions, ',') from 'fillfactor=([0-9]*)') AS fillfactor
+FROM pg_index idx
+JOIN pg_class cls ON cls.oid=indexrelid
+JOIN pg_class tab ON tab.oid=indrelid
+LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace
+JOIN pg_namespace n ON n.oid=tab.relnamespace
+JOIN pg_am am ON am.oid=cls.relam
+LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
+LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
+LEFT OUTER JOIN pg_description des ON (des.objoid=cls.oid AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0 AND desp.classoid='pg_constraint'::regclass)
+WHERE indrelid = {{tid}}::oid
+{% if cid %}
+AND cls.oid = {{cid}}::oid
+{% endif %}
+AND contype='x'
+ORDER BY cls.relname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/update.sql
new file mode 100644
index 0000000..4d70f21
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/exclusion_constraint/sql/9.2_plus/update.sql
@@ -0,0 +1,22 @@
+{### SQL to update exclusion constraint object ###}
+{% if data %}
+{# ==== To update exclusion constraint name ==== #}
+{% if data.name != o_data.name %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{# ==== To update exclusion constraint tablespace ==== #}
+{% if data.spcname and data.spcname != o_data.spcname %}
+ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
+    SET TABLESPACE {{ conn|qtIdent(data.spcname) }};
+{% endif %}
+{% if data.fillfactor and data.fillfactor != o_data.fillfactor %}
+ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
+    SET (FILLFACTOR={{ data.fillfactor }});
+{% endif %}
+{# ==== To update exclusion constraint comments ==== #}
+{% if data.comment is defined and data.comment != o_data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/alter.sql
new file mode 100644
index 0000000..0fb0ea5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/alter.sql
@@ -0,0 +1,4 @@
+{% if data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/begin.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/begin.sql
new file mode 100644
index 0000000..58bfee1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/begin.sql
@@ -0,0 +1 @@
+BEGIN;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/create.sql
new file mode 100644
index 0000000..7a4eda4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/create.sql
@@ -0,0 +1,27 @@
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    ADD{% if data.name %} CONSTRAINT {{ conn|qtIdent(data.name) }}{% endif%} FOREIGN KEY ({% for columnobj in data.columns %}{% if loop.index != 1 %}
+, {% endif %}{{ conn|qtIdent(columnobj.local_column)}}{% endfor %})
+    REFERENCES {{ conn|qtIdent(data.remote_schema, data.remote_table) }} ({% for columnobj in data.columns %}{% if loop.index != 1 %}
+, {% endif %}{{ conn|qtIdent(columnobj.referenced)}}{% endfor %}) {% if data.confmatchtype %}MATCH FULL{% else %}MATCH SIMPLE{% endif%}
+
+    ON UPDATE{% if data.confupdtype  == 'a' %}
+ NO ACTION{% elif data.confupdtype  == 'r' %}
+ RESTRICT{% elif data.confupdtype  == 'c' %}
+ CASCADE{% elif data.confupdtype  == 'n' %}
+ SET NULL{% elif data.confupdtype  == 'd' %}
+ SET DEFAULT{% endif %}
+
+    ON DELETE{% if data.confdeltype  == 'a' %}
+ NO ACTION{% elif data.confdeltype  == 'r' %}
+ RESTRICT{% elif data.confdeltype  == 'c' %}
+ CASCADE{% elif data.confdeltype  == 'n' %}
+ SET NULL{% elif data.confdeltype  == 'd' %}
+ SET DEFAULT{% endif %}
+{% if data.condeferrable %}
+
+    DEFERRABLE{% if data.condeferred %}
+ INITIALLY DEFERRED{% endif%}
+{% endif%}
+{% if data.convalidated %}
+
+    NOT VALID{% endif%};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/create_index.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/create_index.sql
new file mode 100644
index 0000000..f9c1e88
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/create_index.sql
@@ -0,0 +1,5 @@
+{% if data.autoindex %}
+CREATE INDEX {{ conn|qtIdent(data.coveringindex) }}
+    ON {{ conn|qtIdent(data.schema, data.table) }}({% for columnobj in data.columns %}{% if loop.index != 1 %}
+, {% endif %}{{ conn|qtIdent(columnobj.local_column)}}{% endfor %});
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/delete.sql
new file mode 100644
index 0000000..2096795
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/delete.sql
@@ -0,0 +1,3 @@
+{% if data %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }} DROP CONSTRAINT {{ conn|qtIdent(data.name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/end.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/end.sql
new file mode 100644
index 0000000..92d09d5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/end.sql
@@ -0,0 +1 @@
+END;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_cols.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_cols.sql
new file mode 100644
index 0000000..4b2fee2
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_cols.sql
@@ -0,0 +1,7 @@
+{% for n in range(colcnt|int) %}
+{% if loop.index != 1 %}
+UNION SELECT  pg_get_indexdef({{ cid|string }}, {{ loop.index|string }}, true) AS column
+{% else %}
+SELECT  pg_get_indexdef({{ cid|string }} , {{ loop.index|string }} , true) AS column
+{% endif %}
+{% endfor %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_constraint_cols.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_constraint_cols.sql
new file mode 100644
index 0000000..a96e575
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_constraint_cols.sql
@@ -0,0 +1,13 @@
+{% for keypair in keys %}
+{% if loop.index != 1 %}
+UNION
+{% endif %}
+SELECT a1.attname as conattname,
+    a2.attname as confattname
+FROM pg_attribute a1,
+    pg_attribute a2
+WHERE a1.attrelid={{tid}}::oid
+    AND a1.attnum={{keypair[1]}}
+    AND a2.attrelid={{confrelid}}::oid
+    AND a2.attnum={{keypair[0]}}
+{% endfor %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_constraints.sql
new file mode 100644
index 0000000..d2f358c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_constraints.sql
@@ -0,0 +1,37 @@
+SELECT   cls.oid, cls.relname as idxname, indnatts
+  FROM pg_index idx
+  JOIN pg_class cls ON cls.oid=indexrelid
+  LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
+  LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
+WHERE idx.indrelid = {{tid}}::oid
+    AND con.contype='p'
+
+UNION
+
+SELECT  cls.oid, cls.relname as idxname, indnatts
+    FROM pg_index idx
+    JOIN pg_class cls ON cls.oid=indexrelid
+    LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
+    LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
+WHERE idx.indrelid = {{tid}}::oid
+    AND con.contype='x'
+
+UNION
+
+SELECT  cls.oid, cls.relname as idxname, indnatts
+    FROM pg_index idx
+    JOIN pg_class cls ON cls.oid=indexrelid
+    LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
+    LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
+WHERE idx.indrelid = {{tid}}::oid
+    AND con.contype='u'
+
+UNION
+
+SELECT  cls.oid, cls.relname as idxname, indnatts
+    FROM pg_index idx
+    JOIN pg_class cls ON cls.oid=indexrelid
+    LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
+    LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
+WHERE idx.indrelid = {{tid}}::oid
+   AND conname IS NULL
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_name.sql
new file mode 100644
index 0000000..07fdae2
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_name.sql
@@ -0,0 +1,3 @@
+SELECT conname as name
+FROM pg_constraint ct
+WHERE ct.oid = {{fkid}}::oid
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_oid.sql
new file mode 100644
index 0000000..576c976
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_oid.sql
@@ -0,0 +1,5 @@
+SELECT ct.oid,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype='f' AND
+ct.conname = {{ name|qtLiteral }};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_oid_with_transaction.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_oid_with_transaction.sql
new file mode 100644
index 0000000..abe636a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_oid_with_transaction.sql
@@ -0,0 +1,6 @@
+SELECT ct.oid,
+    ct.conname as name,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype='f' AND
+    conrelid = {{tid}}::oid LIMIT 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_parent.sql
new file mode 100644
index 0000000..a652857
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/get_parent.sql
@@ -0,0 +1,7 @@
+SELECT nsp.nspname AS schema,
+    rel.relname AS table
+FROM
+    pg_class rel
+JOIN pg_namespace nsp
+ON rel.relnamespace = nsp.oid::int
+WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/nodes.sql
new file mode 100644
index 0000000..9667588
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/nodes.sql
@@ -0,0 +1,7 @@
+SELECT ct.oid,
+    conname as name,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype='f' AND
+    conrelid = {{tid}}::oid
+ORDER BY conname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/properties.sql
new file mode 100644
index 0000000..1f506ee
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/properties.sql
@@ -0,0 +1,31 @@
+SELECT ct.oid,
+      conname as name,
+      condeferrable,
+      condeferred,
+      confupdtype,
+      confdeltype,
+      CASE confmatchtype
+        WHEN 's' THEN FALSE
+        WHEN 'f' THEN TRUE
+      END AS confmatchtype,
+      conkey,
+      confkey,
+      confrelid,
+      nl.nspname as fknsp,
+      cl.relname as fktab,
+      nr.nspname as refnsp,
+      cr.relname as reftab,
+      description as comment,
+      NOT convalidated as convalidated
+FROM pg_constraint ct
+JOIN pg_class cl ON cl.oid=conrelid
+JOIN pg_namespace nl ON nl.oid=cl.relnamespace
+JOIN pg_class cr ON cr.oid=confrelid
+JOIN pg_namespace nr ON nr.oid=cr.relnamespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=ct.oid AND des.classoid='pg_constraint'::regclass)
+WHERE contype='f' AND
+conrelid = {{tid}}::oid
+{% if cid %}
+AND ct.oid = {{cid}}::oid
+{% endif %}
+ORDER BY conname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/update.sql
new file mode 100644
index 0000000..ee8f8a9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/update.sql
@@ -0,0 +1,18 @@
+{### SQL to update foreign key object ###}
+{% if data %}
+{# ==== To update foreign key name ==== #}
+{% if data.name != o_data.name %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{# ==== To update foreign key validate ==== #}
+{% if 'convalidated' in data and o_data.convalidated != data.convalidated and not data.convalidated %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    VALIDATE CONSTRAINT {{ conn|qtIdent(data.name) }};
+{% endif %}
+{# ==== To update foreign key comments ==== #}
+{% if data.comment is defined and data.comment != o_data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/validate.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/validate.sql
new file mode 100644
index 0000000..5a62c80
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/validate.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    VALIDATE CONSTRAINT {{ conn|qtIdent(data.name) }};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/alter.sql
new file mode 100644
index 0000000..68a4444
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/alter.sql
@@ -0,0 +1,11 @@
+{## Alter index to use cluster type ##}
+{% if data.indisclustered %}
+
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    CLUSTER ON {{conn|qtIdent(data.name)}};
+{% endif %}
+{## Changes description ##}
+{% if data.description is defined %}
+
+COMMENT ON INDEX {{conn|qtIdent(data.schema, data.name)}}
+    IS {{data.description|qtLiteral}};{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/backend_support.sql
new file mode 100644
index 0000000..6f66ed1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/backend_support.sql
@@ -0,0 +1,9 @@
+{#=============Checks if it is materialized view========#}
+{% if vid %}
+SELECT
+    CASE WHEN c.relkind = 'm' THEN True ELSE False END As m_view
+FROM
+    pg_class c
+WHERE
+    c.oid = {{ vid }}::oid
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/column_details.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/column_details.sql
new file mode 100644
index 0000000..2cf540a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/column_details.sql
@@ -0,0 +1,30 @@
+SELECT
+    i.indexrelid,
+    CASE i.indoption[i.attnum - 1]
+    WHEN 0 THEN ARRAY['ASC', 'NULLS LAST']
+    WHEN 1 THEN ARRAY['DESC', 'NULLS FIRST']
+    WHEN 2 THEN ARRAY['ASC', 'NULLS FIRST']
+    WHEN 3 THEN ARRAY['DESC', 'NULLS  ']
+    ELSE ARRAY['UNKNOWN OPTION' || i.indoption[i.attnum - 1], '']
+    END::text[] AS options,
+    i.attnum,
+    pg_get_indexdef(i.indexrelid, i.attnum, true) as attdef,
+    CASE WHEN (o.opcdefault = FALSE) THEN o.opcname ELSE null END AS opcname,
+    op.oprname AS oprname,
+	CASE WHEN length(nspc.nspname) > 0 AND length(coll.collname) > 0  THEN
+	  concat(quote_ident(nspc.nspname), '.', quote_ident(coll.collname))
+	ELSE '' END AS collnspname
+FROM (
+      SELECT
+          indexrelid, i.indoption, i.indclass,
+          unnest(ARRAY(SELECT generate_series(1, i.indnatts) AS n)) AS attnum
+      FROM
+          pg_index i
+      WHERE i.indexrelid = {{idx}}::OID
+) i
+    LEFT JOIN pg_opclass o ON (o.oid = i.indclass[i.attnum - 1])
+    LEFT OUTER JOIN pg_constraint c ON (c.conindid = i.indexrelid)
+    LEFT OUTER JOIN pg_operator op ON (op.oid = c.conexclop[i.attnum])
+    LEFT JOIN pg_attribute a ON (a.attrelid = i.indexrelid AND a.attnum = i.attnum)
+    LEFT OUTER JOIN pg_collation coll ON a.attcollation=coll.oid
+    LEFT OUTER JOIN pg_namespace nspc ON coll.collnamespace=nspc.oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..b31c827
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/create.sql
@@ -0,0 +1,12 @@
+CREATE {% if data.indisunique %}UNIQUE {% endif %}INDEX {% if data.isconcurrent %}CONCURRENTLY {% endif %}{{conn|qtIdent(data.name)}}
+    ON {{conn|qtIdent(data.schema, data.table)}} {% if data.amname %}USING {{conn|qtIdent(data.amname)}}{% endif %}
+
+    ({% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(c.colname)}}{% if c.collspcname %} COLLATE {{c.collspcname}} {% endif %}{% if c.op_class %}
+ {{c.op_class}}{% endif %}{% if c.sort_order is defined %}{% if c.sort_order %} DESC{% else %} ASC{% endif %}{% endif %}{% if c.nulls is defined %} NULLS {% if c.nulls %}
+FIRST{% else %}LAST{% endif %}{% endif %}{% endfor %}){% if data.fillfactor %}
+
+    WITH (FILLFACTOR={{data.fillfactor}}){% endif %}{% if data.spcname %}
+
+    TABLESPACE {{conn|qtIdent(data.spcname)}}{% endif %}{% if data.indconstraint %}
+
+    WHERE {{data.indconstraint}}{% endif %};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..f3808fa
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+DROP INDEX {{conn|qtIdent(data.nspname, data.name)}}{% if cascade %} cascade{% endif %};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_am.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_am.sql
new file mode 100644
index 0000000..5bb579b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_am.sql
@@ -0,0 +1,3 @@
+-- Fetches access methods
+SELECT oid, amname
+FROM pg_am
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..7418742
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+	CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+	  concat(quote_ident(nspname), '.', quote_ident(collname))
+	ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+    WHERE c.collnamespace=n.oid
+    ORDER BY nspname, collname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..c32402f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,7 @@
+SELECT DISTINCT ON(cls.relname) cls.oid
+FROM pg_index idx
+    JOIN pg_class cls ON cls.oid=indexrelid
+    JOIN pg_class tab ON tab.oid=indrelid
+    JOIN pg_namespace n ON n.oid=tab.relnamespace
+WHERE indrelid = {{tid}}::OID
+    AND cls.relname = {{data.name|qtLiteral}};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_op_class.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_op_class.sql
new file mode 100644
index 0000000..af0133a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_op_class.sql
@@ -0,0 +1,5 @@
+SELECT opcname,  opcmethod
+FROM pg_opclass
+    WHERE opcmethod = {{oid}}::OID
+    AND NOT opcdefault
+    ORDER BY 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_parent.sql
new file mode 100644
index 0000000..5dd5d3c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/get_parent.sql
@@ -0,0 +1,5 @@
+SELECT nsp.nspname AS schema ,rel.relname AS table
+FROM pg_class rel
+    JOIN pg_namespace nsp
+    ON rel.relnamespace = nsp.oid::int
+    WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..9e9c948
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/nodes.sql
@@ -0,0 +1,12 @@
+SELECT DISTINCT ON(cls.relname) cls.oid, cls.relname as name
+FROM pg_index idx
+    JOIN pg_class cls ON cls.oid=indexrelid
+    JOIN pg_class tab ON tab.oid=indrelid
+    LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace
+    JOIN pg_namespace n ON n.oid=tab.relnamespace
+    JOIN pg_am am ON am.oid=cls.relam
+    LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
+    LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
+WHERE indrelid = {{tid}}::OID
+    AND conname is NULL
+    ORDER BY cls.relname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..2641d4c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/properties.sql
@@ -0,0 +1,22 @@
+SELECT DISTINCT ON(cls.relname) cls.oid, cls.relname as name, indrelid, indkey, indisclustered,
+    indisvalid, indisunique, indisprimary, n.nspname,indnatts,cls.reltablespace AS spcoid,
+    COALESCE(spcname, 'pg_default') as spcname, tab.relname as tabname, indclass, con.oid AS conoid,
+    CASE WHEN contype IN ('p', 'u', 'x') THEN desp.description
+         ELSE des.description END AS description,
+    pg_get_expr(indpred, indrelid, true) as indconstraint, contype, condeferrable, condeferred, amname,
+    substring(array_to_string(cls.reloptions, ',') from 'fillfactor=([0-9]*)') AS fillfactor
+    {% if datlastsysoid %}, (CASE WHEN cls.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_idx {% endif %}
+FROM pg_index idx
+    JOIN pg_class cls ON cls.oid=indexrelid
+    JOIN pg_class tab ON tab.oid=indrelid
+    LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace
+    JOIN pg_namespace n ON n.oid=tab.relnamespace
+    JOIN pg_am am ON am.oid=cls.relam
+    LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
+    LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
+    LEFT OUTER JOIN pg_description des ON (des.objoid=cls.oid AND des.classoid='pg_class'::regclass)
+    LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0 AND desp.classoid='pg_constraint'::regclass)
+WHERE indrelid = {{tid}}::OID
+    AND conname is NULL
+    {% if idx %}AND cls.oid = {{idx}}::OID {% endif %}
+    ORDER BY cls.relname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..f2acd6c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index/sql/9.1_plus/update.sql
@@ -0,0 +1,24 @@
+{## Changes name ##}
+{% if data.name and o_data.name != data.name %}
+ALTER INDEX {{conn|qtIdent(data.schema, o_data.name)}}
+    RENAME TO {{conn|qtIdent(data.name)}};
+{% endif %}
+{## Changes fillfactor ##}
+{% if data.fillfactor and o_data.fillfactor != data.fillfactor %}
+ALTER INDEX {{conn|qtIdent(data.schema, data.name)}}
+    SET (FILLFACTOR={{data.fillfactor}});
+{% endif %}
+{## Changes tablespace ##}
+{% if data.spcname and o_data.spcname != data.spcname %}
+ALTER INDEX {{conn|qtIdent(data.schema, data.name)}}
+    SET TABLESPACE {{conn|qtIdent(data.spcname)}};
+{% endif %}
+{## Alter index to use cluster type ##}
+{% if data.indisclustered is defined and o_data.indisclustered != data.indisclustered %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    CLUSTER ON {{conn|qtIdent(data.name)}};
+{% endif %}
+{## Changes description ##}
+{% if data.description is defined and o_data.description != data.description %}
+COMMENT ON INDEX {{conn|qtIdent(data.schema, data.name)}}
+    IS {{data.description|qtLiteral}};{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/alter.sql
new file mode 100644
index 0000000..0fb0ea5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/alter.sql
@@ -0,0 +1,4 @@
+{% if data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/begin.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/begin.sql
new file mode 100644
index 0000000..58bfee1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/begin.sql
@@ -0,0 +1 @@
+BEGIN;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/create.sql
new file mode 100644
index 0000000..61a717e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/create.sql
@@ -0,0 +1,12 @@
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    ADD{% if data.name %} CONSTRAINT {{ conn|qtIdent(data.name) }}{% endif%} {{constraint_name}} {% if data.index %}USING INDEX {{ conn|qtIdent(data.index) }}{% else %}
+({% for columnobj in data.columns %}{% if loop.index != 1 %}
+, {% endif %}{{ conn|qtIdent(columnobj.column)}}{% endfor %}){% if data.fillfactor %}
+
+    WITH (FILLFACTOR={{data.fillfactor}}){% endif %}{% if data.spcname and data.spcname != "pg_default" %}
+
+    USING INDEX TABLESPACE {{ conn|qtIdent(data.spcname) }}{% endif %}{% endif %}{% if data.condeferrable %}
+
+    DEFERRABLE{% if data.condeferred %}
+ INITIALLY DEFERRED{% endif%}
+{% endif%};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/delete.sql
new file mode 100644
index 0000000..2096795
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/delete.sql
@@ -0,0 +1,3 @@
+{% if data %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }} DROP CONSTRAINT {{ conn|qtIdent(data.name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/end.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/end.sql
new file mode 100644
index 0000000..92d09d5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/end.sql
@@ -0,0 +1 @@
+END;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_constraint_cols.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_constraint_cols.sql
new file mode 100644
index 0000000..4b2fee2
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_constraint_cols.sql
@@ -0,0 +1,7 @@
+{% for n in range(colcnt|int) %}
+{% if loop.index != 1 %}
+UNION SELECT  pg_get_indexdef({{ cid|string }}, {{ loop.index|string }}, true) AS column
+{% else %}
+SELECT  pg_get_indexdef({{ cid|string }} , {{ loop.index|string }} , true) AS column
+{% endif %}
+{% endfor %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_indices.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_indices.sql
new file mode 100644
index 0000000..b9cab21
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_indices.sql
@@ -0,0 +1,3 @@
+SELECT relname FROM pg_class, pg_index
+WHERE pg_class.oid=indexrelid
+AND indrelid={{ tid }}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_name.sql
new file mode 100644
index 0000000..a1beafb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_name.sql
@@ -0,0 +1,15 @@
+SELECT cls.relname as name
+FROM pg_index idx
+JOIN pg_class cls ON cls.oid=indexrelid
+LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND
+                            dep.objid = cls.oid AND
+                            dep.refobjsubid = '0'
+                            AND dep.refclassid=(SELECT oid
+                                                FROM pg_class
+                                                WHERE relname='pg_constraint') AND
+                            dep.deptype='i')
+LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND
+                                      con.oid = dep.refobjid)
+WHERE indrelid = {{tid}}::oid
+AND contype='{{constraint_type}}'
+AND cls.oid = {{cid}}::oid;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_oid.sql
new file mode 100644
index 0000000..861ff50
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_oid.sql
@@ -0,0 +1,4 @@
+SELECT ct.conindid as oid
+FROM pg_constraint ct
+WHERE contype='{{constraint_type}}' AND
+ct.conname = {{ name|qtLiteral }};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_oid_with_transaction.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_oid_with_transaction.sql
new file mode 100644
index 0000000..9ad8020
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_oid_with_transaction.sql
@@ -0,0 +1,5 @@
+SELECT ct.conindid as oid,
+    ct.conname as name
+FROM pg_constraint ct
+WHERE contype='{{constraint_type}}' AND
+    conrelid = {{tid}}::oid LIMIT 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_parent.sql
new file mode 100644
index 0000000..a652857
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/get_parent.sql
@@ -0,0 +1,7 @@
+SELECT nsp.nspname AS schema,
+    rel.relname AS table
+FROM
+    pg_class rel
+JOIN pg_namespace nsp
+ON rel.relnamespace = nsp.oid::int
+WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/nodes.sql
new file mode 100644
index 0000000..ed96b44
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/nodes.sql
@@ -0,0 +1,14 @@
+SELECT cls.oid, cls.relname as name
+FROM pg_index idx
+JOIN pg_class cls ON cls.oid=indexrelid
+LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND
+                            dep.objid = cls.oid AND
+                            dep.refobjsubid = '0' AND
+                            dep.refclassid=(SELECT oid
+                                            FROM pg_class
+                                            WHERE relname='pg_constraint') AND
+                            dep.deptype='i')
+LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND
+                                      con.oid = dep.refobjid)
+WHERE indrelid = {{tid}}::oid
+AND contype='{{constraint_type}}'
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/properties.sql
new file mode 100644
index 0000000..0eabdd7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/properties.sql
@@ -0,0 +1,29 @@
+SELECT cls.oid,
+    cls.relname as name,
+    indnatts,
+    COALESCE(spcname, 'pg_default') as spcname,
+    CASE contype
+        WHEN 'p' THEN desp.description
+        WHEN 'u' THEN desp.description
+        WHEN 'x' THEN desp.description
+        ELSE des.description
+    END AS comment,
+    condeferrable,
+    condeferred,
+    substring(array_to_string(cls.reloptions, ',') from 'fillfactor=([0-9]*)') AS fillfactor
+FROM pg_index idx
+JOIN pg_class cls ON cls.oid=indexrelid
+JOIN pg_class tab ON tab.oid=indrelid
+LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace
+JOIN pg_namespace n ON n.oid=tab.relnamespace
+JOIN pg_am am ON am.oid=cls.relam
+LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
+LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
+LEFT OUTER JOIN pg_description des ON (des.objoid=cls.oid AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0 AND desp.classoid='pg_constraint'::regclass)
+WHERE indrelid = {{tid}}::oid
+{% if cid %}
+AND cls.oid = {{cid}}::oid
+{% endif %}
+AND contype='{{constraint_type}}'
+ORDER BY cls.relname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/update.sql
new file mode 100644
index 0000000..6618532
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/index_constraint/sql/update.sql
@@ -0,0 +1,22 @@
+{### SQL to update constraint object ###}
+{% if data %}
+{# ==== To update constraint name ==== #}
+{% if data.name != o_data.name %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{# ==== To update constraint tablespace ==== #}
+{% if data.spcname and data.spcname != o_data.spcname %}
+ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
+    SET TABLESPACE {{ conn|qtIdent(data.spcname) }};
+{% endif %}
+{% if data.fillfactor and data.fillfactor != o_data.fillfactor %}
+ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
+    SET (FILLFACTOR={{ data.fillfactor }});
+{% endif %}
+{# ==== To update constraint comments ==== #}
+{% if data.comment is defined and data.comment != o_data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/backend_support.sql
new file mode 100644
index 0000000..bb5e8d8
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/backend_support.sql
@@ -0,0 +1,9 @@
+{#=============Checks if it is materialized view========#}
+{% if vid %}
+SELECT
+    CASE WHEN c.relkind = 'm' THEN False ELSE True END As m_view
+FROM
+    pg_class c
+WHERE
+    c.oid = {{ vid }}::oid
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/create.sql
new file mode 100644
index 0000000..fad2c7a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/create.sql
@@ -0,0 +1,27 @@
+{# ============Create Rule============= #}
+{% if display_comments %}
+-- Rule: {{ data.name }} ON {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP Rule {{ data.name }} ON {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.view %}
+CREATE OR REPLACE RULE {{ conn|qtIdent(data.name) }} AS
+    ON {{ data.event|upper if data.event else 'SELECT' }} TO {{ conn|qtIdent(data.schema, data.view) }}
+{% if data.condition %}
+    WHERE {{ data.condition }}
+{% endif %}
+    DO{% if data.do_instead in ['true', True] %}
+{{ ' INSTEAD' }}
+{% else %}
+{{ '' }}
+{% endif %}
+{% if data.statements %}
+{{ data.statements.rstrip(';') }};
+{% else %}
+  NOTHING;
+{% endif %}
+{% if data.comment %}
+
+COMMENT ON RULE {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.view) }} IS {{ data.comment|qtLiteral }};{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/delete.sql
new file mode 100644
index 0000000..cf18913
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/delete.sql
@@ -0,0 +1,16 @@
+{# ======== Drop/Cascade Rule ========= #}
+{% if rid %}
+SELECT
+    rw.rulename,
+    cl.relname,
+    nsp.nspname
+FROM
+    pg_rewrite rw
+JOIN pg_class cl ON cl.oid=rw.ev_class
+JOIN pg_namespace nsp ON nsp.oid=cl.relnamespace
+WHERE
+    rw.oid={{ rid }};
+{% endif %}
+{% if rulename and relname and nspname %}
+DROP RULE {{ conn|qtIdent(rulename) }} ON {{ conn|qtIdent(nspname, relname) }} {% if cascade %} CASCADE {% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/properties.sql
new file mode 100644
index 0000000..afdd139
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/properties.sql
@@ -0,0 +1,27 @@
+{# =================== Fetch Rules ==================== #}
+{% if tid or rid %}
+SELECT
+    rw.oid AS oid,
+    rw.rulename AS name,
+    relname AS view,
+    CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,
+    nspname AS schema,
+    description AS comment,
+    {# ===== Check whether it is system rule or not ===== #}
+    CASE WHEN rw.rulename = '_RETURN' THEN True ELSE False END AS system_rule,
+    CASE WHEN rw.ev_enabled != 'D' THEN True ELSE False END AS enabled,
+    pg_get_ruledef(rw.oid, true) AS definition
+FROM
+    pg_rewrite rw
+JOIN pg_class cl ON cl.oid=rw.ev_class
+JOIN pg_namespace nsp ON nsp.oid=cl.relnamespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=rw.oid AND des.classoid='pg_rewrite'::regclass)
+WHERE
+  {% if tid %}
+      ev_class = {{ tid }}
+  {% elif rid %}
+      rw.oid = {{ rid }}
+  {% endif %}
+ORDER BY
+    rw.rulename
+ {% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/rule_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/rule_id.sql
new file mode 100644
index 0000000..3dc6dba
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/rule_id.sql
@@ -0,0 +1,9 @@
+{#========Below will provide rule id for last created rule========#}
+{% if rule_name %}
+SELECT
+    rw.oid
+FROM
+    pg_rewrite rw
+WHERE
+    rw.rulename={{ rule_name|qtLiteral }}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/update.sql
new file mode 100644
index 0000000..099d6e4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/rules/sql/update.sql
@@ -0,0 +1,33 @@
+{# ===== Update Rule ===== #}
+{% if data.name is defined %}
+{% set rule_name = data.name %}
+{% else %}
+{% set rule_name = o_data.name %}
+{% endif %}
+{% if data.name and data.name != o_data.name %}
+ALTER RULE {{ conn|qtIdent(o_data.name) }} ON {{ conn|qtIdent(o_data.schema, o_data.view) }} RENAME TO {{ conn|qtIdent(data.name) }};{{ '\r\r' }}
+{% endif %}
+{% if data.event or data.do_instead or data.condition or data.statements %}
+CREATE OR REPLACE RULE {{ conn|qtIdent(rule_name) }} AS
+    ON {% if data.event and data.event != o_data.event %}{{ data.event|upper }}{% else %}{{ o_data.event|upper }}{% endif %}
+ TO {{ conn|qtIdent(o_data.schema, o_data.view) }}
+{% if data.condition and o_data.condition != data.condition %}
+    WHERE {{ data.condition }}
+{% elif data.condition is not defined and o_data.condition %}
+    WHERE {{ o_data.condition }}
+{% endif %}
+    DO {% if (('do_instead' not in data and o_data.do_instead in ['true', True]) or (data.do_instead in ['true', True])) %}{{ 'INSTEAD' }}{% endif %}
+{% if data.statements and data.statements != o_data.statements %}
+
+{{ data.statements.rstrip(';') }};
+{% elif data.statements is not defined and o_data.statements %}
+
+{{ o_data.statements.rstrip(';') }};
+{% else %}
+ NOTHING;
+{% endif %}
+
+{% endif %}
+{% set old_comment = o_data.comment|default('', true) %}
+{% if (data.comment is defined and (data.comment != old_comment)) %}
+COMMENT ON RULE {{ conn|qtIdent(rule_name) }} ON {{ conn|qtIdent(o_data.schema, o_data.view) }} IS {{ data.comment|qtLiteral }};{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js
new file mode 100644
index 0000000..ae2b40f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/js/table.js
@@ -0,0 +1,954 @@
+define(
+    [
+    'jquery', 'underscore', 'underscore.string', 'pgadmin',
+    'pgadmin.browser', 'alertify', 'pgadmin.browser.collection',
+    'pgadmin.node.column', 'pgadmin.node.constraints'
+    ],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+  if (!pgBrowser.Nodes['coll-table']) {
+    var databases = pgAdmin.Browser.Nodes['coll-table'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'table',
+        label: '{{ _('Tables') }}',
+        type: 'coll-table',
+        columns: ['name', 'relowner', 'description']
+      });
+  };
+
+  if (!pgBrowser.Nodes['table']) {
+    pgAdmin.Browser.Nodes['table'] = pgBrowser.Node.extend({
+      type: 'table',
+      label: '{{ _('Table') }}',
+      collection_type: 'coll-table',
+      hasSQL: true,
+      hasDepends: true,
+      sqlAlterHelp: 'sql-altertable.html',
+      sqlCreateHelp: 'sql-createtable.html',
+      parent_type: ['schema', 'catalog'],
+      hasScriptTypes: ['create', 'select', 'insert', 'update', 'delete'],
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_table_on_coll', node: 'coll-table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 1, label: '{{ _('Table...') }}',
+          icon: 'wcTabIcon icon-table', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_table', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 1, label: '{{ _('Table...') }}',
+          icon: 'wcTabIcon icon-table', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_table__on_schema', node: 'schema', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Table...') }}',
+          icon: 'wcTabIcon icon-table', data: {action: 'create', check: false},
+          enable: 'canCreate'
+        },{
+          name: 'truncate_table', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'truncate_table',
+          category: 'Truncate', priority: 3, label: '{{ _('Truncate') }}',
+          icon: 'fa fa-eraser', enable : 'canCreate'
+        },{
+          name: 'truncate_table_cascade', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'truncate_table_cascade',
+          category: 'Truncate', priority: 3, label: '{{ _('Truncate Cascade') }}',
+          icon: 'fa fa-eraser', enable : 'canCreate'
+        },{
+          // To enable/disable all triggers for the table
+          name: 'enable_all_triggers', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'enable_triggers_on_table',
+          category: 'Trigger(s)', priority: 4, label: '{{ _('Enable All') }}',
+          icon: 'fa fa-check', enable : 'canCreate_with_trigger_enable'
+        },{
+          name: 'disable_all_triggers', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'disable_triggers_on_table',
+          category: 'Trigger(s)', priority: 4, label: '{{ _('Disable All') }}',
+          icon: 'fa fa-times', enable : 'canCreate_with_trigger_disable'
+        },{
+          name: 'reset_table_stats', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'reset_table_stats',
+          category: 'Reset', priority: 4, label: '{{ _('Reset statistics') }}',
+          icon: 'fa fa-bar-chart', enable : 'canCreate'
+        }
+        ]);
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      callbacks: {
+        /* Enable trigger(s) on table */
+        enable_triggers_on_table: function(args) {
+            var params = {'enable': true };
+            this.callbacks.set_triggers.apply(this, [args, params]);
+        },
+        /* Disable trigger(s) on table */
+        disable_triggers_on_table: function(args) {
+            var params = {'enable': false };
+            this.callbacks.set_triggers.apply(this, [args, params]);
+        },
+        set_triggers: function(args, params) {
+          // This function will send request to enable or
+          // disable triggers on table level
+          var input = args || {};
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+          if (!d)
+            return false;
+
+          $.ajax({
+            url: obj.generate_url(i, 'set_trigger' , d, true),
+            type:'PUT',
+            data: params,
+            dataType: "json",
+            success: function(res) {
+              if (res.success == 1) {
+                alertify.success("{{ _('" + res.info + "') }}");
+                t.unload(i);
+                t.setInode(i);
+                t.deselect(i);
+                setTimeout(function() {
+                  t.select(i);
+                }, 10);
+              }
+            },
+            error: function(xhr, status, error) {
+              try {
+                var err = $.parseJSON(xhr.responseText);
+                if (err.success == 0) {
+                  msg = S('{{ _(' + err.errormsg + ')}}').value();
+                  alertify.error("{{ _('" + err.errormsg + "') }}");
+                }
+              } catch (e) {}
+              t.unload(i);
+            }
+          });
+        },
+        /* Truncate table */
+        truncate_table: function(args) {
+            var params = {'cascade': false };
+            this.callbacks.truncate.apply(this, [args, params]);
+        },
+        /* Truncate table with cascade */
+        truncate_table_cascade: function(args) {
+            var params = {'cascade': true };
+            this.callbacks.truncate.apply(this, [args, params]);
+        },
+        truncate: function(args, params) {
+          var input = args || {};
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+          if (!d)
+            return false;
+
+          alertify.confirm(
+            S('{{ _('Are you sure you want to truncate table - %%s ?') }}').sprintf(d.label).value(),
+            function (e) {
+            if (e) {
+              var data = d;
+              $.ajax({
+                url: obj.generate_url(i, 'truncate' , d, true),
+                type:'PUT',
+                data: params,
+                dataType: "json",
+                success: function(res) {
+                  if (res.success == 1) {
+                    alertify.success("{{ _('" + res.info + "') }}");
+                    t.removeIcon(i);
+                    data.icon = 'icon-table';
+                    t.addIcon(i, {icon: data.icon});
+                    t.unload(i);
+                    t.setInode(i);
+                    t.deselect(i);
+                    // Fetch updated data from server
+                    setTimeout(function() {
+                      t.select(i);
+                    }, 10);
+                  }
+                },
+                error: function(xhr, status, error) {
+                  try {
+                    var err = $.parseJSON(xhr.responseText);
+                    if (err.success == 0) {
+                      msg = S('{{ _(' + err.errormsg + ')}}').value();
+                      alertify.error("{{ _('" + err.errormsg + "') }}");
+                    }
+                  } catch (e) {}
+                  t.unload(i);
+                }
+              });
+            }
+         });
+       },
+       reset_table_stats: function(args) {
+          var input = args || {};
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+          if (!d)
+            return false;
+
+          alertify.confirm(
+            S('{{ _('Are you sure you want to reset table statistics - %%s ?') }}').sprintf(d.label).value(),
+            function (e) {
+            if (e) {
+              var data = d;
+              $.ajax({
+                url: obj.generate_url(i, 'reset' , d, true),
+                type:'DELETE',
+                success: function(res) {
+                  if (res.success == 1) {
+                    alertify.success("{{ _('" + res.info + "') }}");
+                    t.removeIcon(i);
+                    data.icon = 'icon-table';
+                    t.addIcon(i, {icon: data.icon});
+                    t.unload(i);
+                    t.setInode(i);
+                    t.deselect(i);
+                    // Fetch updated data from server
+                    setTimeout(function() {
+                      t.select(i);
+                    }, 10);
+                  }
+                },
+                error: function(xhr, status, error) {
+                  try {
+                    var err = $.parseJSON(xhr.responseText);
+                    if (err.success == 0) {
+                      msg = S('{{ _(' + err.errormsg + ')}}').value();
+                      alertify.error("{{ _('" + err.errormsg + "') }}");
+                    }
+                  } catch (e) {}
+                  t.unload(i);
+                }
+              });
+            }
+         });
+       }
+      },
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          spcoid: undefined,
+          spcname: 'pg_default',
+          relowner: undefined,
+          relacl: undefined,
+          relhasoids: undefined,
+          relhassubclass: undefined,
+          reltuples: undefined,
+          description: undefined,
+          conname: undefined,
+          conkey: undefined,
+          isrepl: undefined,
+          triggercount: undefined,
+          relpersistence: undefined,
+          fillfactor: undefined,
+          reloftype: undefined,
+          typname: undefined,
+          labels: undefined,
+          providers: undefined,
+          is_sys_table: undefined,
+          coll_inherits: [],
+          hastoasttable: true,
+          toast_autovacuum_enabled: false,
+          autovacuum_enabled: false,
+          primary_key: []
+       },
+        // Default values!
+        initialize: function(attrs, args) {
+          var self = this,
+              isNew = (_.size(attrs) === 0);
+
+          if (isNew) {
+            var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+            var schemaInfo = args.node_info.schema;
+
+            this.set({'relowner': userInfo.name}, {silent: true});
+            this.set({'schema': schemaInfo.label}, {silent: true});
+          }
+          pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+
+        },
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', cell: 'string',
+          type: 'text', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema'
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'relowner', label:'{{ _('Owner') }}', cell: 'string',
+          type: 'text', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema', control: 'node-list-by-name',
+          node: 'role', select2: { allowClear: false }
+        },{
+          id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
+          control: 'node-list-by-name',
+          type: 'text', mode: ['create', 'edit'], node: 'schema',
+          disabled: 'inSchema', filter: function(d) {
+            // If schema name start with pg_* then we need to exclude them
+            if(d && d.label.match(/^pg_/))
+            {
+              return false;
+            }
+            return true;
+          }
+        },{
+          id: 'spcname', label:'{{ _('Tablespace') }}', cell: 'string', control: 'node-list-by-name',
+          type: 'text', mode: ['properties', 'create', 'edit'], node: 'tablespace',
+          disabled: 'inSchema', select2:{allowClear:false},
+          filter: function(d) {
+            // If tablespace name is not "pg_global" then we need to exclude them
+            if(d && d.label.match(/pg_global/))
+            {
+              return false;
+            }
+            return true;
+          }
+        },{
+          id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema'
+        },{
+          id: 'typname', label:'{{ _('Of type') }}', cell: 'string', control: 'node-ajax-options',
+          type: 'text', mode: ['properties', 'create', 'edit'],
+          disabled: 'checkOfType', url: 'get_oftype', group: '{{ _('Advanced') }}', deps: ['coll_inherits'],
+          transform: function(data, cell) {
+            var control = cell || this,
+              m = control.model;
+              m.of_types_tables = data;
+              return data;
+          },
+          control: Backform.NodeAjaxOptionsControl.extend({
+              // When of_types changes we need to clear columns collection
+              onChange: function() {
+                Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+                var self = this,
+                  tbl_oid = undefined,
+                  tbl_name = self.model.get('typname'),
+                  data = undefined,
+                  arg = undefined,
+                  column_collection = self.model.get('columns');
+
+                if (!_.isUndefined(tbl_name) &&
+                    tbl_name !== '' && column_collection.length !== 0) {
+                  var msg = '{{ _('Changing of type table will clear columns collection') }}';
+                  alertify.confirm(msg, function (e) {
+                    if (e) {
+                      // User clicks Ok, lets clear columns collection
+                      column_collection.reset();
+                    } else {
+                      return this;
+                    }
+                  });
+                } else if (!_.isUndefined(tbl_name) && tbl_name === '') {
+                  column_collection.reset();
+                }
+
+                // Run Ajax now to fetch columns
+                if (!_.isUndefined(tbl_name) && tbl_name !== '') {
+                  arg = { 'tname': tbl_name }
+                  data = self.model.fetch_columns_ajax.apply(self, [arg]);
+                  // Add into column collection
+                  column_collection.set(data, { merge:false,remove:false });
+                }
+              }
+            })
+        },{
+          id: 'fillfactor', label:'{{ _('Fill factor') }}', cell: 'integer',
+          type: 'int', mode: ['create', 'edit'], min: 10, max: 100,
+          disabled: 'inSchema',group: '{{ _('Advanced') }}'
+        },{
+          id: 'relhasoids', label:'{{ _('Has OIDs?') }}', cell: 'switch',
+          type: 'switch', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema', group: '{{ _('Advanced') }}'
+        },{
+          id: 'relpersistence', label:'{{ _('Unlogged?') }}', cell: 'switch',
+          type: 'switch', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchemaWithModelCheck',
+          group: '{{ _('Advanced') }}'
+        },{
+          id: 'conname', label:'{{ _('Primary Key') }}', cell: 'string',
+          type: 'text', mode: ['properties'],
+          disabled: 'inSchema'
+        },{
+          id: 'reltuples', label:'{{ _('Rows (estimated)') }}', cell: 'string',
+          type: 'text', mode: ['properties'],
+          disabled: 'inSchema'
+        },{
+          id: 'rows_cnt', label:'{{ _('Rows (counted)') }}', cell: 'string',
+          type: 'text', mode: ['properties'],
+          disabled: 'inSchema'
+        },{
+          id: 'relhassubclass', label:'{{ _('Inherits tables?') }}', cell: 'switch',
+          type: 'switch', mode: ['properties'],
+          disabled: 'inSchema'
+        },{
+          id: 'is_sys_table', label:'{{ _('System tabel?') }}', cell: 'switch',
+          type: 'switch', mode: ['properties'],
+          disabled: 'inSchema'
+        },{
+          id: 'coll_inherits', label: '{{ _('Inherited from table(s)') }}',
+          url: 'get_inherits', type: 'array',
+          disabled: 'checkInheritance', deps: ['typname'],
+          select2: { multiple: true, allowClear: true,
+          placeholder: '{{ _('Select to inherit from...') }}'},
+          transform: function(data, cell) {
+            var control = cell || this,
+              m = control.model;
+              m.inherited_tables_list = data;
+              return data;
+          },
+          control: Backform.MultiSelectAjaxControl.extend({
+              // When changes we need to add/clear columns collection
+              onChange: function() {
+                Backform.MultiSelectAjaxControl.prototype.onChange.apply(this, arguments);
+                var self = this,
+                // current table list and previous table list
+                cTbl_list = self.model.get('coll_inherits') || [],
+                pTbl_list = self.model.previous('coll_inherits') || [];
+
+                if (!_.isUndefined(cTbl_list)) {
+                  var tbl_name = undefined,
+                    tid = undefined;
+
+                  // Add columns logic
+                  // If new table is added in list
+                  if(cTbl_list.length > 1 && cTbl_list.length > pTbl_list.length) {
+                    // Find newly added table from current list
+                    tbl_name = _.difference(cTbl_list, pTbl_list);
+                    tid = this.get_table_oid(tbl_name[0]);
+                    this.add_columns(tid);
+                  } else if (cTbl_list.length == 1) {
+                    // First table added
+                    tid = this.get_table_oid(cTbl_list[0]);
+                    this.add_columns(tid);
+                  }
+
+                  // Remove columns logic
+                  if(cTbl_list.length > 0 && cTbl_list.length < pTbl_list.length) {
+                    // Find deleted table from previous list
+                    tbl_name = _.difference(pTbl_list, cTbl_list);
+                    this.remove_columns(tbl_name[0]);
+                  } else if (pTbl_list.length === 1 && cTbl_list.length < 1) {
+                    // We got last table from list
+                    tbl_name = pTbl_list[0];
+                    this.remove_columns(tbl_name);
+                  }
+
+                }
+              },
+              add_columns: function(tid) {
+                // Create copy of old model if anything goes wrong at-least we have backup
+                // Then send AJAX request to fetch table specific columns
+                var self = this,
+                  url = 'get_columns',
+                  m = self.model.top || self.model,
+                  data = undefined,
+                  old_columns = _.clone(m.get('columns')),
+                  column_collection = m.get('columns');
+
+                var arg = {'tid': tid}
+                data = self.model.fetch_columns_ajax.apply(self, [arg]);
+
+                // Update existing column collection
+                column_collection.set(data, { merge:false,remove:false });
+              },
+              remove_columns: function(tblname) {
+                // Remove all the column models for deleted table
+                var tid = this.get_table_oid(tblname),
+                  column_collection = this.model.get('columns');
+                column_collection.remove(column_collection.where({'inheritedid': tid }));
+              },
+              get_table_oid: function(tblname) {
+                // Here we will fetch the table oid from table name
+                var tbl_oid = undefined;
+                // iterate over list to find table oid
+                _.each(this.model.inherited_tables_list, function(obj) {
+                    if(obj.label === tblname) {
+                      tbl_oid = obj.tid;
+                    }
+                });
+                return tbl_oid;
+              }
+            })
+        },{
+          id: 'inherited_tables_cnt', label:'{{ _('Inherited tables count') }}', cell: 'string',
+          type: 'text', mode: ['properties'],
+          disabled: 'inSchema'
+        },{
+          type: 'nested', control: 'fieldset', mode: ['edit', 'create'],
+          schema:[{
+            // Here we will create tab control for columns
+              id: 'columns', label:'{{ _('Columns') }}', type: 'collection',
+              group: '{{ _('Columns') }}',
+              model: pgBrowser.Nodes['column'].model,
+              subnode: pgBrowser.Nodes['column'].model,
+              mode: ['create', 'edit'],
+              disabled: 'inSchema',
+              canAdd: 'check_grid_add_condition',
+              canEdit: true, canDelete: true,
+              // For each row edit/delete button enable/disable
+              canEditRow: 'check_grid_row_edit_delete',
+              canDeleteRow: 'check_grid_row_edit_delete',
+              uniqueCol : ['name'],
+              columns : ['name' , 'cltype', 'is_primary_key', 'inheritedfrom'],
+              control: Backform.UniqueColCollectionControl.extend({
+                initialize: function() {
+                  Backform.UniqueColCollectionControl.prototype.initialize.apply(this, arguments);
+                  var self = this,
+                      collection = self.model.get(self.field.get('name'));
+
+                  collection.on("change:is_primary_key", function(m) {
+                    var primary_key_coll = self.model.get('primary_key'),
+                        column_name = m.get('name'),
+                        primary_key;
+
+                    if(m.get('is_primary_key')) {
+                    // Add column to primary key.
+                      if (primary_key_coll.length < 1) {
+                        primary_key = new (primary_key_coll.model)({}, {
+                          top: self.model,
+                          collection: primary_key_coll,
+                          handler: primary_key_coll
+                        });
+                        primary_key_coll.add(primary_key);
+                      } else {
+                        primary_key = primary_key_coll.first();
+                      }
+                      // Do not alter existing primary key columns.
+                      if (_.isUndefined(primary_key.get('oid'))) {
+                        var primary_key_column_coll = primary_key.get('columns'),
+                          primary_key_column_exist = primary_key_column_coll.where({column:column_name});
+
+                        if (primary_key_column_exist.length == 0) {
+                          var primary_key_column = new (primary_key_column_coll.model)(
+                              {column: column_name}, { silent: true,
+                              top: self.model,
+                              collection: primary_key_coll,
+                              handler: primary_key_coll
+                            });
+
+                          primary_key_column_coll.add(primary_key_column);
+                        }
+
+                        primary_key_column_coll.trigger('pgadmin:multicolumn:updated', primary_key_column_coll);
+                      }
+
+                    } else {
+                    // remove column from primary key.
+                      if (primary_key_coll.length > 0) {
+                        var primary_key = primary_key_coll.first();
+                        // Do not alter existing primary key columns.
+                        if (!_.isUndefined(primary_key.get('oid'))) {
+                          return;
+                        }
+
+                        var  primary_key_column_coll = primary_key.get('columns'),
+                            removedCols = primary_key_column_coll.where({column:column_name});
+                        if (removedCols.length > 0) {
+                          primary_key_column_coll.remove(removedCols);
+                          _.each(removedCols, function(m) {
+                            m.destroy();
+                          })
+                          if (primary_key_column_coll.length == 0) {
+                            setTimeout(function () {
+                              // There will be only on primary key so remove the first one.
+                              primary_key_coll.remove(primary_key_coll.first());
+                              /* Ideally above line of code should be "primary_key_coll.reset()".
+                               * But our custom DataCollection (extended from Backbone collection in datamodel.js)
+                               * does not respond to reset event, it only supports add, remove, change events.
+                               * And hence no custom event listeners/validators get called for reset event.
+                               */
+                            }, 10);
+                          }
+                        }
+                        primary_key_column_coll.trigger('pgadmin:multicolumn:updated', primary_key_column_coll);
+                      }
+                    }
+                  })
+                },
+                remove: function() {
+                  var collection = this.model.get(this.field.get('name'));
+                  if (collection) {
+                    collection.off("change:is_primary_key");
+                  }
+
+                  Backform.UniqueColCollectionControl.prototype.remove.apply(this, arguments);
+                }
+              }),
+              allowMultipleEmptyRow: false
+          }]
+        },{
+          type: 'nested', control: 'fieldset',
+          schema:[{
+              // Here we will create tab control for constraints
+              type: 'nested', control: 'tab', group: '{{ _('Constraints') }}',
+              mode: ['edit', 'create'],
+              schema: [{
+                  id: 'primary_key', label: '{{ _('Primary Key') }}',
+                  model: pgBrowser.Nodes['primary_key'].model,
+                  subnode: pgBrowser.Nodes['primary_key'].model,
+                  editable: false, type: 'collection',
+                  group: '{{ _('Primary Key') }}', mode: ['edit', 'create'],
+                  canEdit: true, canDelete: true,
+                  control: 'unique-col-collection',
+                  columns : ['name', 'columns'],
+                  canAdd: true,
+                  canAddRow: function(m) {
+                   // User can only add one primary key
+                   var columns = m.get('columns');
+
+                   return (m.get('primary_key') &&
+                            m.get('primary_key').length < 1 &&
+                            _.some(columns.pluck('name')));
+                  }
+                },{
+                  id: 'foreign_key', label: '{{ _('Foreign Key') }}',
+                  model: pgBrowser.Nodes['foreign_key'].model,
+                  subnode: pgBrowser.Nodes['foreign_key'].model,
+                  editable: false, type: 'collection',
+                  group: '{{ _('Foreign Key') }}', mode: ['edit', 'create'],
+                  canEdit: true, canDelete: true,
+                  control: 'unique-col-collection',
+                  canAdd: true,
+                  columns : ['name', 'columns'],
+                  canAddRow: function(m) {
+                   // User can only add if there is at least one column with name.
+                   var columns = m.get('columns');
+                   return _.some(columns.pluck('name'));
+                  }
+                },{
+                  id: 'check_constraint', label: '{{ _('Check Constraint') }}',
+                  model: pgBrowser.Nodes['check_constraints'].model,
+                  subnode: pgBrowser.Nodes['check_constraints'].model,
+                  editable: false, type: 'collection',
+                  group: '{{ _('Check') }}', mode: ['edit', 'create'],
+                  canEdit: true, canDelete: true,
+                  control: 'unique-col-collection',
+                  canAdd: true,
+                  columns : ['name', 'consrc']
+                },{
+                  id: 'unique_constraint', label: '{{ _('Unique Constraint') }}',
+                  model: pgBrowser.Nodes['unique_constraint'].model,
+                  subnode: pgBrowser.Nodes['unique_constraint'].model,
+                  editable: false, type: 'collection',
+                  group: '{{ _('Unique') }}', mode: ['edit', 'create'],
+                  canEdit: true, canDelete: true,
+                  control: 'unique-col-collection',
+                  columns : ['name', 'columns'],
+                  canAdd: true,
+                  canAddRow: function(m) {
+                   // User can only add if there is at least one column with name.
+                   var columns = m.get('columns');
+                   return _.some(columns.pluck('name'));
+                  }
+                },{
+                  id: 'exclude_constraint', label: '{{ _('Exclude Constraint') }}',
+                  model: pgBrowser.Nodes['exclusion_constraint'].model,
+                  subnode: pgBrowser.Nodes['exclusion_constraint'].model,
+                  editable: false, type: 'collection',
+                  group: '{{ _('Exclude') }}', mode: ['edit', 'create'],
+                  canEdit: true, canDelete: true,
+                  control: 'unique-col-collection',
+                  columns : ['name', 'columns', 'constraint'],
+                  canAdd: true,
+                  canAddRow: function(m) {
+                   // User can only add if there is at least one column with name.
+                   var columns = m.get('columns');
+                   return _.some(columns.pluck('name'));
+                  }
+            }]
+          }]
+        },{
+          type: 'nested', control: 'fieldset', label: '{{ _('Like') }}',
+          group: '{{ _('Advanced') }}',
+          schema:[{
+            id: 'like_relation', label:'{{ _('Relation') }}', cell: 'string',
+            type: 'text', mode: ['create', 'edit'],
+            control: 'node-ajax-options', url: 'get_relations',
+            disabled: 'inSchemaWithModelCheck', group: '{{ _('Like') }}'
+          },{
+            id: 'like_default_value', label:'{{ _('With Default values?') }}', cell: 'switch',
+            type: 'switch', mode: ['create', 'edit'],
+            disabled: 'inSchemaWithModelCheck', group: '{{ _('Like') }}'
+          },{
+            id: 'like_constraints', label:'{{ _('With Constraints?') }}', cell: 'switch',
+            type: 'switch', mode: ['create', 'edit'],
+            disabled: 'inSchemaWithModelCheck', group: '{{ _('Like') }}'
+          },{
+            id: 'like_indexes', label:'{{ _('With Indexes?') }}', cell: 'switch',
+            type: 'switch', mode: ['create', 'edit'],
+            disabled: 'inSchemaWithModelCheck', group: '{{ _('Like') }}'
+          },{
+            id: 'like_storage', label:'{{ _('With Storage?') }}', cell: 'switch',
+            type: 'switch', mode: ['create', 'edit'],
+            disabled: 'inSchemaWithModelCheck', group: '{{ _('Like') }}'
+          },{
+            id: 'like_comments', label:'{{ _('With Comments?') }}', cell: 'switch',
+            type: 'switch', mode: ['create', 'edit'],
+            disabled: 'inSchemaWithModelCheck', group: '{{ _('Like') }}'
+          }]
+        },{
+          // Here we will create tab control for auto-vacuum
+          type: 'nested', control: 'tab', group: '{{ _('Auto vacuum') }}',
+          mode: ['edit', 'create'],
+          schema: Backform.VacuumSettingsSchema
+        },{
+          id: 'relacl_str', label:'{{ _('Privileges') }}', cell: 'string',
+          type: 'text', mode: ['properties'], group: '{{ _('Security') }}',
+          disabled: 'inSchema'
+        },{
+          id: 'relacl', label: 'Privileges', type: 'collection',
+          group: '{{ _('Security') }}', control: 'unique-col-collection',
+          model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({
+          privileges: ['a','r','w','d','D','x','t']}),
+          mode: ['edit', 'create'], canAdd: true, canDelete: true,
+          uniqueCol : ['grantee']
+        },{
+          id: 'seclabels', label: '{{ _('Security Labels') }}',
+          model: pgAdmin.Browser.SecurityModel, editable: false, type: 'collection',
+          group: '{{ _('Security') }}', mode: ['edit', 'create'],
+          min_version: 90100, canAdd: true,
+          canEdit: false, canDelete: true, control: 'unique-col-collection'
+        },{
+          id: 'vacuum_settings_str', label: '{{ _('Storage Settings') }}',
+          type: 'multiline', group: '{{ _('Advanced') }}', mode: ['properties']
+        }
+        ],
+        validate: function() {
+          var err = {},
+              changedAttrs = this.changed,
+              msg = undefined,
+              name = this.get('name'),
+              schema = this.get('schema'),
+              relowner = this.get('relowner');
+
+          this.errorModel.clear();
+
+          if (_.isUndefined(name) || _.isNull(name) ||
+            String(name).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Table name can not be empty.') }}';
+            this.errorModel.set('name', msg);
+            return msg;
+          } else if (_.isUndefined(schema) || _.isNull(schema) ||
+            String(schema).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Table schema can not be empty.') }}';
+            this.errorModel.set('schema', msg);
+            return msg;
+          } else if (_.isUndefined(relowner) || _.isNull(relowner) ||
+            String(relowner).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Table owner can not be empty.') }}';
+            this.errorModel.set('relowner', msg);
+            return msg;
+          }
+          return null;
+        },
+        // We will disable everything if we are under catalog node
+        inSchema: function() {
+          if(this.node_info &&  'catalog' in this.node_info)
+          {
+            return true;
+          }
+          return false;
+        },
+       isInheritedTable: function(m) {
+         if(!m.inSchema.apply(this, [m])) {
+          if(
+               (!_.isUndefined(m.get('coll_inherits')) && m.get('coll_inherits').length != 0)
+             ||
+               (!_.isUndefined(m.get('typname')) && String(m.get('typname')).replace(/^\s+|\s+$/g, '') !== '')
+             ) {
+            // Either of_types or coll_inherits has value
+            return false;
+          } else {
+            return true;
+          }
+         } else {
+           return false;
+         }
+        },
+        // We will disable it if Oftype is defined
+        checkInheritance: function(m) {
+        //coll_inherits || typname
+          if(!m.inSchema.apply(this, [m]) &&
+              ( _.isUndefined(m.get('typname')) ||
+                _.isNull(m.get('typname')) ||
+                String(m.get('typname')).replace(/^\s+|\s+$/g, '') == '')) {
+            return false;
+          }
+          return true;
+        },
+        // Check for column grid when to Add
+        check_grid_add_condition: function(m) {
+          var enable_flag = true;
+          if(!m.inSchema.apply(this, [m])) {
+            // if of_type then disable add in grid
+            if (!_.isUndefined(m.get('typname')) && !_.isNull(m.get('typname'))) {
+                    enable_flag = false;
+                }
+          }
+          return enable_flag;
+        },
+        // Check for column grid when to edit/delete (for each row)
+        check_grid_row_edit_delete: function(m) {
+          var flag = true;
+          if(!_.isUndefined(m.get('inheritedfrom')) &&
+               !_.isNull(m.get('inheritedfrom')) &&
+               String(m.get('inheritedfrom')).replace(/^\s+|\s+$/g, '') !== '') {
+            flag = false;
+          }
+          return flag;
+        },
+        // We will disable it if Inheritance is defined
+        checkOfType: function(m) {
+        //coll_inherits || typname
+          if(!m.inSchemaWithModelCheck.apply(this, [m]) &&
+              (_.isUndefined(m.get('coll_inherits')) ||
+               _.isNull(m.get('coll_inherits')) ||
+               String(m.get('coll_inherits')).replace(/^\s+|\s+$/g, '') == '')) {
+            return false;
+          }
+          return true;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchemaWithModelCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info)
+          {
+            // We will disbale control if it's in 'edit' mode
+            if (m.isNew()) {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          return true;
+        },
+        isTableAutoVacuumEnable: function(m) {
+          // We need to check additional condition to toggle enable/disable
+          // for table auto-vacuum
+          if(!m.inSchema.apply(this, [m]) &&
+              m.get('autovacuum_enabled') === true) {
+            return false;
+          }
+          return true;
+        },
+        isToastTableAutoVacuumEnable: function(m) {
+          // We need to check additional condition to toggle enable/disable
+          // for toast table auto-vacuum
+          if(!m.inSchemaWithModelCheck.apply(this, [m]) &&
+              m.get('toast_autovacuum_enabled') == true) {
+            return false;
+          }
+          return true;
+        },
+        fetch_columns_ajax: function(arg) {
+        var self = this,
+          url = 'get_columns',
+          m = self.model.top || self.model,
+          old_columns = _.clone(m.get('columns'))
+          data = undefined;
+
+          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);
+            // Fetching Columns data for the selected table.
+            $.ajax({
+              async: false,
+              url: full_url,
+              data: arg,
+              success: function(res) {
+                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);
+            data = (data && data.data) || [];
+            return data;
+          }
+        }
+      }),
+      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 table
+            if (_.indexOf(['schema'], d._type) > -1)
+              return true;
+
+            if ('coll-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;
+      },
+      // Check to whether table has disable trigger(s)
+      canCreate_with_trigger_enable: function(itemData, item, data) {
+        if(this.canCreate.apply(this, [itemData, item, data])) {
+          // We are here means we can create menu, now let's check condition
+          if(itemData.tigger_count > 0) {
+            return true;
+          } else {
+            return false;
+          }
+        }
+      },
+      // Check to whether table has enable trigger(s)
+      canCreate_with_trigger_disable: function(itemData, item, data) {
+        if(this.canCreate.apply(this, [itemData, item, data])) {
+          // We are here means we can create menu, now let's check condition
+          if(itemData.tigger_count > 0 && itemData.has_enable_triggers > 0) {
+            return true;
+          } else {
+            return false;
+          }
+        }
+      }
+  });
+
+  }
+
+  return pgBrowser.Nodes['table'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/acl.sql
new file mode 100644
index 0000000..56f1f76
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/acl.sql
@@ -0,0 +1,46 @@
+{### SQL to fetch privileges for tablespace ###}
+SELECT 'relacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor,
+    array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+  (SELECT
+    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
+  FROM
+    (SELECT rel.relacl
+        FROM pg_class rel
+          LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace
+          LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
+          LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
+          LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
+        WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ scid }}::oid
+            AND rel.oid = {{ tid }}::oid
+    ) acl,
+    (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+        AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+        aclexplode(rel.relacl) as d
+        FROM pg_class rel
+          LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace
+          LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
+          LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
+          LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
+        WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ scid }}::oid
+            AND rel.oid = {{ tid }}::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
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/backend_support.sql
new file mode 100644
index 0000000..f9b9564
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/backend_support.sql
@@ -0,0 +1,18 @@
+SELECT
+ CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport
+FROM pg_namespace nsp
+WHERE nsp.oid={{scid}}::int
+AND (
+	(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\\%'
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..c0fb5e4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/create.sql
@@ -0,0 +1,157 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+{% import 'column/macros/security.macros' as COLUMN_SECLABLE %}
+{% import 'column/macros/privilege.macros' as COLUMN_PRIVILEGE %}
+{% import 'table/sql/macros/constraints.macro' as CONSTRAINTS %}
+{#===========================================#}
+{#====== MAIN TABLE TEMPLATE STARTS HERE ======#}
+{#===========================================#}
+{#
+ If user has not provided any details but only name then
+ add empty bracket with table name
+#}
+{% set empty_bracket = ""%}
+{% if data.coll_inherits|length == 0 and  data.columns|length == 0 and not data.typname and not data.like_relation and data.primary_key|length == 0 and data.unique_constraint|length == 0 and data.foreign_key|length == 0 and data.check_constraint|length == 0 and data.exclude_constraint|length == 0 %}
+{% set empty_bracket = "\n(\n)"%}
+{% endif %}
+CREATE {% if data.relpersistence %}UNLOGGED {% endif %}TABLE {{conn|qtIdent(data.schema, data.name)}}{{empty_bracket}}
+{% if data.typname %}
+    OF {{ data.typname }}
+{% endif %}
+{% if data.like_relation or data.coll_inherits or data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 or data.exclude_constraint|length > 0 %}
+(
+{% endif %}
+{% if data.like_relation %}
+    LIKE {{ data.like_relation }}{% if data.like_default_value %}
+
+        INCLUDING DEFAULTS{% endif %}{% if data.like_constraints %}
+
+        INCLUDING CONSTRAINTS{% endif %}{% if data.like_indexes %}
+
+        INCLUDING INDEXES{% endif %}{% if data.like_storage %}
+
+        INCLUDING STORAGE{% endif %}{% if data.like_comments %}
+
+        INCLUDING COMMENTS{% endif %}{% if data.columns|length > 0 %},
+{% endif %}
+
+{% endif %}
+{### Add columns ###}
+{% if data.columns and data.columns|length > 0 %}
+{% for c in data.columns %}
+{% if c.name and c.cltype %}
+{% if loop.index != 1 %},
+{% endif %}
+    {{conn|qtIdent(c.name)}} {{c.cltype}}{% if c.attlen %}
+({{c.attlen}}{% if c.attprecision%}, {{c.attprecision}}{% endif %}){% endif %}{% if c.hasSqrBracket %}
+[]{% endif %}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.attnotnull %} NOT NULL{% endif %}{% if c.defval %} DEFAULT {{c.defval}}{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{# Macro to render for constraints #}
+{% if data.primary_key|length > 0 %}{% if data.columns|length > 0 %},{% endif %}
+{{CONSTRAINTS.PRIMARY_KEY(conn, data.primary_key[0])}}{% endif %}{% if data.unique_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 %},{% endif %}
+{{CONSTRAINTS.UNIQUE(conn, data.unique_constraint)}}{% endif %}{% if data.foreign_key|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 %},{% endif %}
+{{CONSTRAINTS.FOREIGN_KEY(conn, data.foreign_key)}}{% endif %}{% if data.check_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 %},{% endif %}
+{{CONSTRAINTS.CHECK(conn, data.check_constraint)}}{% endif %}{% if data.exclude_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 %},{% endif %}
+{{CONSTRAINTS.EXCLUDE(conn, data.exclude_constraint)}}{% endif %}
+{% if data.like_relation or data.coll_inherits or data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 or data.exclude_constraint|length > 0 %}
+
+)
+{% endif %}
+{### If we are inheriting it from another table(s) ###}
+{% if data.coll_inherits %}
+    INHERITS ({% for val in data.coll_inherits %}{% if loop.index != 1 %}, {% endif %}{{val}}{% endfor %})
+{% endif %}
+WITH (
+    OIDS = {% if data.relhasoids %}TRUE{% else %}FALSE{% endif %}{% if data.fillfactor %},
+    FILLFACTOR = {{ data.fillfactor }}{% endif %}{% if data.autovacuum_custom %},
+    autovacuum_enabled = {% if data.autovacuum_enabled %}TRUE{% else %}FALSE{% endif %}{% endif %}{% if data.toast_autovacuum %},
+    toast.autovacuum_enabled = {% if data.toast_autovacuum_enabled %}TRUE{% else %}FALSE{% endif %}
+{% endif %}{% if data.autovacuum_enabled and data.vacuum_table|length > 0 %}
+{% for opt in data.vacuum_table %}{% if opt.name and opt.value %}
+,
+    {{opt.name}} = {{opt.value}}{% endif %}
+{% endfor %}{% endif %}{% if data.toast_autovacuum_enabled and data.vacuum_toast|length > 0 %}
+{% for opt in data.vacuum_toast %}{% if opt.name and opt.value %}
+,
+    toast.{{opt.name}} = {{opt.value}}{% endif %}
+{% endfor %}{% endif %}
+
+)
+{### SQL for Tablespace ###}
+{% if data.spcname %}
+TABLESPACE {{ conn|qtIdent(data.spcname) }};
+{% endif %}
+{### Alter SQL for Owner ###}
+{% if data.relowner %}
+
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    OWNER to {{conn|qtIdent(data.relowner)}};
+{% endif %}
+{### Security Labels on Table ###}
+{% if data.seclabels and data.seclabels|length > 0 %}
+
+{% for r in data.seclabels %}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
+{% endfor %}
+{% endif %}
+{###  ACL on Table ###}
+{% if data.relacl %}
+
+{% for priv in data.relacl %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{### SQL for COMMENT ###}
+{% if data.description %}
+COMMENT ON TABLE {{conn|qtIdent(data.schema, data.name)}}
+    IS {{data.description|qtLiteral}};
+{% endif %}
+{#===========================================#}
+{#====== MAIN TABLE TEMPLATE ENDS HERE ======#}
+{#===========================================#}
+{#===========================================#}
+{#  COLUMN SPECIFIC TEMPLATES STARTS HERE    #}
+{#===========================================#}
+{% if data.columns and data.columns|length > 0 %}
+{% for c in data.columns %}
+{% if c.description %}
+
+COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.name, c.name)}}
+    IS {{c.description|qtLiteral}};
+{% endif %}
+{###  Add variables to column ###}
+{% if c.attoptions and c.attoptions|length > 0 %}
+
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    {{ VARIABLE.SET(conn, 'COLUMN', c.name, c.attoptions) }}
+{% endif %}
+{###  ACL ###}
+{% if c.attacl and c.attacl|length > 0 %}
+
+{% for priv in c.attacl %}
+    {{ COLUMN_PRIVILEGE.APPLY(conn, data.schema, data.name, c.name, priv.grantee, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{###  Security Lables ###}
+{% if c.seclabels and c.seclabels|length > 0 %}
+
+{% for r in c.seclabels %}
+{{ COLUMN_SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.name, c.name, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{#===========================================#}
+{#   COLUMN SPECIFIC TEMPLATES ENDS HERE     #}
+{#===========================================#}
+{#======================================#}
+{#   CONSTRAINTS SPECIFIC TEMPLATES     #}
+{#======================================#}
+{{CONSTRAINTS.CONSTRAINT_COMMENTS(conn, data.schema, data.name, data.primary_key)}}
+{{CONSTRAINTS.CONSTRAINT_COMMENTS(conn, data.schema, data.name, data.unique_constraint)}}
+{{CONSTRAINTS.CONSTRAINT_COMMENTS(conn, data.schema, data.name, data.foreign_key)}}
+{{CONSTRAINTS.CONSTRAINT_COMMENTS(conn, data.schema, data.name, data.check_constraint)}}
+{{CONSTRAINTS.CONSTRAINT_COMMENTS(conn, data.schema, data.name, data.exclude_constraint)}}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..01d0314
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+DROP TABLE {{conn|qtIdent(data.schema, data.name)}}{% if cascade %} CASCADE{% endif %};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/depend.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/depend.sql
new file mode 100644
index 0000000..f5f39e7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/depend.sql
@@ -0,0 +1,9 @@
+SELECT
+    ref.relname AS refname, d2.refclassid, dep.deptype AS deptype
+FROM pg_depend dep
+    LEFT JOIN pg_depend d2 ON dep.objid=d2.objid AND dep.refobjid != d2.refobjid
+    LEFT JOIN pg_class ref ON ref.oid=d2.refobjid
+    LEFT JOIN pg_attribute att ON d2.refclassid=att.attrelid AND d2.refobjsubid=att.attnum
+    {{ where }} AND
+    dep.classid=(SELECT oid FROM pg_class WHERE relname='pg_attrdef') AND
+    dep.refobjid NOT IN (SELECT d3.refobjid FROM pg_depend d3 WHERE d3.objid=d2.refobjid)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/enable_disable_trigger.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/enable_disable_trigger.sql
new file mode 100644
index 0000000..a4ab154
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/enable_disable_trigger.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
+    {% if is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER ALL;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_columns_for_table.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_columns_for_table.sql
new file mode 100644
index 0000000..3d6bbb7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_columns_for_table.sql
@@ -0,0 +1,16 @@
+SELECT
+    a.attname AS name, format_type(a.atttypid, NULL) AS cltype,
+    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
+{% if tid %}
+    c.oid = {{tid}}::OID
+{% else %}
+    c.relname = {{tname|qtLiteral}}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_inherits.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_inherits.sql
new file mode 100644
index 0000000..50f9a1d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_inherits.sql
@@ -0,0 +1,13 @@
+SELECT c.oid, c.relname , nspname,
+CASE WHEN (nspname NOT LIKE E'pg\_%' AND nspname <> 'public') THEN
+ quote_ident(nspname)||'.'||quote_ident(c.relname)
+ELSE quote_ident(c.relname)
+END AS inherits
+FROM pg_class c
+JOIN pg_namespace n
+ON n.oid=c.relnamespace
+WHERE relkind='r'
+{% if not show_system_objects %}
+AND (n.nspname NOT LIKE E'pg\_%' AND n.nspname NOT in ('information_schema'))
+{% endif %}
+ORDER BY relnamespace, c.relname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_oftype.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_oftype.sql
new file mode 100644
index 0000000..aed42f2
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_oftype.sql
@@ -0,0 +1,6 @@
+SELECT t.oid,
+  quote_ident(n.nspname)||'.'||quote_ident(t.typname) AS typname
+  FROM pg_type t, pg_namespace n
+WHERE t.typtype='c' AND t.typnamespace=n.oid
+  AND NOT (n.nspname like 'pg_%' OR n.nspname='information_schema')
+ORDER BY typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..e9dc772
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,5 @@
+SELECT rel.oid as tid
+FROM pg_class rel
+WHERE rel.relkind IN ('r','s','t')
+AND rel.relnamespace = {{ scid }}::oid
+AND rel.relname = {{data.name|qtLiteral}}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_relations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_relations.sql
new file mode 100644
index 0000000..431ee88
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_relations.sql
@@ -0,0 +1,6 @@
+SELECT c.oid, quote_ident(n.nspname)||'.'||quote_ident(c.relname) AS like_relation
+FROM pg_class c, pg_namespace n
+WHERE c.relnamespace=n.oid
+AND
+c.relkind IN ('r', 'v', 'f')
+ORDER BY 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_tables_for_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_tables_for_constraints.sql
new file mode 100644
index 0000000..1362463
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_tables_for_constraints.sql
@@ -0,0 +1,8 @@
+SELECT cl.oid as value, quote_ident(nspname)||'.'||quote_ident(relname) AS label
+FROM pg_namespace nsp, pg_class cl
+WHERE relnamespace=nsp.oid AND relkind='r'
+   AND nsp.nspname NOT LIKE E'pg\_temp\_%'
+   {% if not show_sysobj %}
+   AND (nsp.nspname NOT LIKE E'pg\_%' AND nsp.nspname NOT in ('information_schema'))
+   {% endif %}
+ORDER BY nspname, relname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_types_where_condition.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_types_where_condition.sql
new file mode 100644
index 0000000..fadfc99
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/get_types_where_condition.sql
@@ -0,0 +1,10 @@
+{### Additional where condition for get_types route for column node ###}
+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 not show_system_objects %}
+AND nsp.nspname != 'information_schema'
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..43f14cb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/nodes.sql
@@ -0,0 +1,6 @@
+SELECT rel.oid, rel.relname AS name,
+    (SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE) AS triggercount,
+    (SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE AND tgenabled = 'O') AS has_enable_triggers
+FROM pg_class rel
+    WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ scid }}::oid
+    ORDER BY rel.relname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..99727c9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/properties.sql
@@ -0,0 +1,65 @@
+SELECT rel.oid, rel.relname AS name, rel.reltablespace AS spcoid,rel.relacl AS relacl_str,
+  (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+  (select nspname FROM pg_namespace WHERE oid = {{scid}}::oid ) as schema,
+  pg_get_userbyid(rel.relowner) AS relowner, rel.relhasoids,
+  rel.relhassubclass, rel.reltuples, des.description, con.conname, con.conkey,
+	EXISTS(select 1 FROM pg_trigger
+			JOIN pg_proc pt ON pt.oid=tgfoid AND pt.proname='logtrigger'
+			JOIN pg_proc pc ON pc.pronamespace=pt.pronamespace AND pc.proname='slonyversion'
+			WHERE tgrelid=rel.oid) AS isrepl,
+	(SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE) AS triggercount,
+	(SELECT ARRAY(SELECT CASE WHEN (nspname NOT LIKE E'pg\_%' AND nspname <> 'public') THEN
+            quote_ident(nspname)||'.'||quote_ident(c.relname)
+            ELSE quote_ident(c.relname) END AS inherited_tables
+    FROM pg_inherits i
+    JOIN pg_class c ON c.oid = i.inhparent
+    JOIN pg_namespace n ON n.oid=c.relnamespace
+    WHERE i.inhrelid = rel.oid ORDER BY inhseqno)) AS coll_inherits,
+  (SELECT count(*)
+		FROM pg_inherits i
+      JOIN pg_class c ON c.oid = i.inhparent
+      JOIN pg_namespace n ON n.oid=c.relnamespace
+		WHERE i.inhrelid = rel.oid) AS inherited_tables_cnt,
+	(CASE WHEN rel.relpersistence = 'u' THEN true ELSE false END) AS relpersistence,
+	substring(array_to_string(rel.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor,
+	(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') = 'true')
+	  THEN true ELSE false END) AS autovacuum_enabled,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age,
+	(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') =  'true')
+	  THEN true ELSE false END) AS toast_autovacuum_enabled,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age,
+	array_to_string(rel.reloptions, ',') AS table_vacuum_settings_str,
+	array_to_string(tst.reloptions, ',') AS toast_table_vacuum_settings_str,
+	rel.reloptions AS reloptions, tst.reloptions AS toast_reloptions, rel.reloftype, typ.typname,
+	(CASE WHEN rel.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable,
+    -- Added for pgAdmin4
+	(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean  THEN true ELSE false END) AS autovacuum_custom,
+	(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean  AND rel.reltoastrelid != 0 THEN true ELSE false END) AS toast_autovacuum,
+
+	(SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=rel.oid AND sl1.objsubid=0) AS seclabels,
+	(CASE WHEN rel.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_table
+FROM pg_class rel
+  LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace
+  LEFT OUTER JOIN pg_description des ON (des.objoid=rel.oid AND des.objsubid=0 AND des.classoid='pg_class'::regclass)
+  LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
+  LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
+  LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
+WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ scid }}::oid
+{% if tid %}  AND rel.oid = {{ tid }}::oid {% endif %}
+ORDER BY rel.relname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/reset_stats.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/reset_stats.sql
new file mode 100644
index 0000000..36eca0d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/reset_stats.sql
@@ -0,0 +1 @@
+SELECT pg_stat_reset_single_table_counters({{tid}})
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/sql.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/sql.sql
new file mode 100644
index 0000000..17b35ae
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/sql.sql
@@ -0,0 +1 @@
+TAKE ASHESH'S HELP ON THIS TASK :-)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/truncate.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/truncate.sql
new file mode 100644
index 0000000..6a276f3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/truncate.sql
@@ -0,0 +1 @@
+TRUNCATE TABLE {{conn|qtIdent(data.schema, data.name)}}{% if cascade %} CASCADE{% endif %};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..a97ac30
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.1_plus/update.sql
@@ -0,0 +1,201 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+{#####################################################}
+{## Rename table ##}
+{#####################################################}
+{% if data.name and data.name != o_data.name %}
+ALTER TABLE {{conn|qtIdent(o_data.schema, o_data.name)}}
+    RENAME TO {{conn|qtIdent(data.name)}};
+
+{% endif %}
+{#####################################################}
+{## Change table schema ##}
+{#####################################################}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER TABLE {{conn|qtIdent(o_data.schema, data.name)}}
+  SET SCHEMA {{conn|qtIdent(data.schema)}};
+
+{% endif %}
+{#####################################################}
+{## Change table owner ##}
+{#####################################################}
+{% if data.relowner and data.relowner != o_data.relowner %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    OWNER TO {{conn|qtIdent(data.relowner)}};
+
+{% endif %}
+{#####################################################}
+{## Update Inherits table definition ##}
+{#####################################################}
+{% if data.coll_inherits_added|length > 0 %}
+{% for val in data.coll_inherits_added %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    INHERIT {{val}};
+
+{% endfor %}
+{% endif %}
+{% if data.coll_inherits_removed|length > 0 %}
+{% for val in data.coll_inherits_removed %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    NO INHERIT {{val}};
+
+{% endfor %}
+{% endif %}
+{#####################################################}
+{## Change hasOID attribute of table ##}
+{#####################################################}
+{% if data.relhasoids is defined and data.relhasoids != o_data.relhasoids %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    SET {% if data.relhasoids %}WITH{% else %}WITHOUT{% endif %} OIDS;
+
+{% endif %}
+{#####################################################}
+{## Change tablespace ##}
+{#####################################################}
+{% if data.spcname and data.spcname != o_data.spcname %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    SET TABLESPACE {{conn|qtIdent(data.spcname)}};
+
+{% endif %}
+{#####################################################}
+{## change fillfactore settings ##}
+{#####################################################}
+{% if data.fillfactor and data.fillfactor != o_data.fillfactor %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    SET (FILLFACTOR={{data.fillfactor}});
+
+{% endif %}
+{###############################}
+{## Table AutoVacuum settings ##}
+{###############################}
+{% if o_data.autovacuum_custom and data.autovacuum_custom == false %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} RESET (
+    autovacuum_enabled,
+    autovacuum_analyze_scale_factor,
+    autovacuum_analyze_threshold,
+    autovacuum_freeze_max_age,
+    autovacuum_vacuum_cost_delay,
+    autovacuum_vacuum_cost_limit,
+    autovacuum_vacuum_scale_factor,
+    autovacuum_vacuum_threshold,
+    autovacuum_freeze_min_age,
+    autovacuum_freeze_table_age
+);
+{% elif data.autovacuum_enabled != o_data.autovacuum_enabled %}
+{% if data.autovacuum_enabled and o_data.autovacuum_enabled == false %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+    autovacuum_enabled = true{% elif data.autovacuum_enabled == false and o_data.autovacuum_enabled %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+       autovacuum_enabled = false{% endif %}
+{% if (data.autovacuum_enabled or o_data.autovacuum_enabled )and data.vacuum_table and data.vacuum_table.changed|length > 0 %}
+{% for opt in data.vacuum_table.changed %}{% if opt.name and opt.value %}
+{% if flag or (data.autovacuum_enabled and o_data.autovacuum_enabled == false) or (data.autovacuum_enabled == false and o_data.autovacuum_enabled) %}
+,
+{% else %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+{% set flag = true %}
+{% endif %}
+    {{opt.name}} = {{opt.value}}{% endif %}
+{% if loop.index == data.vacuum_table.changed|length and (flag or (data.autovacuum_enabled and o_data.autovacuum_enabled == false) or (data.autovacuum_enabled == false and o_data.autovacuum_enabled))%}
+
+);
+{% endif %}
+{% endfor %}
+{% elif (data.autovacuum_enabled and o_data.autovacuum_enabled == false) or (data.autovacuum_enabled == false and o_data.autovacuum_enabled) %}
+
+);
+{% endif %}
+{% endif %}
+{#####################################}
+{## Toast table AutoVacuum settings ##}
+{#####################################}
+{% if o_data.toast_autovacuum and data.toast_autovacuum == false %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} RESET (
+    toast.autovacuum_enabled,
+    toast.autovacuum_freeze_max_age,
+    toast.autovacuum_vacuum_cost_delay,
+    toast.autovacuum_vacuum_cost_limit,
+    toast.autovacuum_vacuum_scale_factor,
+    toast.autovacuum_vacuum_threshold,
+    toast.autovacuum_freeze_min_age,
+    toast.autovacuum_freeze_table_age,
+    toast.autovacuum_analyze_threshold,
+    toast.autovacuum_analyze_scale_factor
+);
+{% elif data.toast_autovacuum_enabled != o_data.toast_autovacuum_enabled %}
+{% if data.toast_autovacuum_enabled and o_data.toast_autovacuum_enabled == false %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+    toast.autovacuum_enabled = true{% elif data.toast_autovacuum_enabled == false and o_data.toast_autovacuum_enabled %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+       toast.autovacuum_enabled = false{% endif %}
+{% if (data.toast_autovacuum_enabled or o_data.toast_autovacuum_enabled )and data.vacuum_toast and data.vacuum_toast.changed|length > 0 %}
+{% for opt in data.vacuum_toast.changed %}{% if opt.name and opt.value %}
+{% if flag or (data.toast_autovacuum_enabled and o_data.toast_autovacuum_enabled == false) or (data.toast_autovacuum_enabled == false and o_data.toast_autovacuum_enabled) %}
+,
+{% else %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+{% set flag = true %}
+{% endif %}
+    toast.{{opt.name}} = {{opt.value}}{% endif %}
+{% if loop.index == data.vacuum_toast.changed|length and (flag or (data.toast_autovacuum_enabled and o_data.toast_autovacuum_enabled == false) or (data.toast_autovacuum_enabled == false and o_data.toast_autovacuum_enabled))%}
+
+);
+{% endif %}
+{% endfor %}
+{% elif (data.toast_autovacuum_enabled and o_data.toast_autovacuum_enabled == false) or (data.toast_autovacuum_enabled == false and o_data.toast_autovacuum_enabled) %}
+
+);
+{% endif %}
+{% endif %}
+{#####################################################}
+{## Change table comments ##}
+{#####################################################}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TABLE {{conn|qtIdent(data.schema, data.name)}}
+  IS {{data.description|qtLiteral}};
+
+{% endif %}
+{#####################################################}
+{## Update table Privileges ##}
+{#####################################################}
+{% if data.relacl %}
+{% if 'deleted' in data.relacl %}
+{% for priv in data.relacl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.relacl %}
+{% for priv in data.relacl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.relacl %}
+{% for priv in data.relacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{#####################################################}
+{## Update table SecurityLabel ##}
+{#####################################################}
+{% if data.seclabels and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABEL.UNSET(conn, 'TABLE', data.name, r.provider, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
+{% endfor %}
+{% endif %}
+
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/acl.sql
new file mode 100644
index 0000000..56f1f76
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/acl.sql
@@ -0,0 +1,46 @@
+{### SQL to fetch privileges for tablespace ###}
+SELECT 'relacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor,
+    array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+  (SELECT
+    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
+  FROM
+    (SELECT rel.relacl
+        FROM pg_class rel
+          LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace
+          LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
+          LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
+          LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
+        WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ scid }}::oid
+            AND rel.oid = {{ tid }}::oid
+    ) acl,
+    (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+        AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+        aclexplode(rel.relacl) as d
+        FROM pg_class rel
+          LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace
+          LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
+          LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
+          LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
+        WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ scid }}::oid
+            AND rel.oid = {{ tid }}::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
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/backend_support.sql
new file mode 100644
index 0000000..f9b9564
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/backend_support.sql
@@ -0,0 +1,18 @@
+SELECT
+ CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport
+FROM pg_namespace nsp
+WHERE nsp.oid={{scid}}::int
+AND (
+	(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\\%'
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql
new file mode 100644
index 0000000..c0fb5e4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/create.sql
@@ -0,0 +1,157 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+{% import 'column/macros/security.macros' as COLUMN_SECLABLE %}
+{% import 'column/macros/privilege.macros' as COLUMN_PRIVILEGE %}
+{% import 'table/sql/macros/constraints.macro' as CONSTRAINTS %}
+{#===========================================#}
+{#====== MAIN TABLE TEMPLATE STARTS HERE ======#}
+{#===========================================#}
+{#
+ If user has not provided any details but only name then
+ add empty bracket with table name
+#}
+{% set empty_bracket = ""%}
+{% if data.coll_inherits|length == 0 and  data.columns|length == 0 and not data.typname and not data.like_relation and data.primary_key|length == 0 and data.unique_constraint|length == 0 and data.foreign_key|length == 0 and data.check_constraint|length == 0 and data.exclude_constraint|length == 0 %}
+{% set empty_bracket = "\n(\n)"%}
+{% endif %}
+CREATE {% if data.relpersistence %}UNLOGGED {% endif %}TABLE {{conn|qtIdent(data.schema, data.name)}}{{empty_bracket}}
+{% if data.typname %}
+    OF {{ data.typname }}
+{% endif %}
+{% if data.like_relation or data.coll_inherits or data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 or data.exclude_constraint|length > 0 %}
+(
+{% endif %}
+{% if data.like_relation %}
+    LIKE {{ data.like_relation }}{% if data.like_default_value %}
+
+        INCLUDING DEFAULTS{% endif %}{% if data.like_constraints %}
+
+        INCLUDING CONSTRAINTS{% endif %}{% if data.like_indexes %}
+
+        INCLUDING INDEXES{% endif %}{% if data.like_storage %}
+
+        INCLUDING STORAGE{% endif %}{% if data.like_comments %}
+
+        INCLUDING COMMENTS{% endif %}{% if data.columns|length > 0 %},
+{% endif %}
+
+{% endif %}
+{### Add columns ###}
+{% if data.columns and data.columns|length > 0 %}
+{% for c in data.columns %}
+{% if c.name and c.cltype %}
+{% if loop.index != 1 %},
+{% endif %}
+    {{conn|qtIdent(c.name)}} {{c.cltype}}{% if c.attlen %}
+({{c.attlen}}{% if c.attprecision%}, {{c.attprecision}}{% endif %}){% endif %}{% if c.hasSqrBracket %}
+[]{% endif %}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.attnotnull %} NOT NULL{% endif %}{% if c.defval %} DEFAULT {{c.defval}}{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{# Macro to render for constraints #}
+{% if data.primary_key|length > 0 %}{% if data.columns|length > 0 %},{% endif %}
+{{CONSTRAINTS.PRIMARY_KEY(conn, data.primary_key[0])}}{% endif %}{% if data.unique_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 %},{% endif %}
+{{CONSTRAINTS.UNIQUE(conn, data.unique_constraint)}}{% endif %}{% if data.foreign_key|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 %},{% endif %}
+{{CONSTRAINTS.FOREIGN_KEY(conn, data.foreign_key)}}{% endif %}{% if data.check_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 %},{% endif %}
+{{CONSTRAINTS.CHECK(conn, data.check_constraint)}}{% endif %}{% if data.exclude_constraint|length > 0 %}{% if data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 %},{% endif %}
+{{CONSTRAINTS.EXCLUDE(conn, data.exclude_constraint)}}{% endif %}
+{% if data.like_relation or data.coll_inherits or data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 or data.exclude_constraint|length > 0 %}
+
+)
+{% endif %}
+{### If we are inheriting it from another table(s) ###}
+{% if data.coll_inherits %}
+    INHERITS ({% for val in data.coll_inherits %}{% if loop.index != 1 %}, {% endif %}{{val}}{% endfor %})
+{% endif %}
+WITH (
+    OIDS = {% if data.relhasoids %}TRUE{% else %}FALSE{% endif %}{% if data.fillfactor %},
+    FILLFACTOR = {{ data.fillfactor }}{% endif %}{% if data.autovacuum_custom %},
+    autovacuum_enabled = {% if data.autovacuum_enabled %}TRUE{% else %}FALSE{% endif %}{% endif %}{% if data.toast_autovacuum %},
+    toast.autovacuum_enabled = {% if data.toast_autovacuum_enabled %}TRUE{% else %}FALSE{% endif %}
+{% endif %}{% if data.autovacuum_enabled and data.vacuum_table|length > 0 %}
+{% for opt in data.vacuum_table %}{% if opt.name and opt.value %}
+,
+    {{opt.name}} = {{opt.value}}{% endif %}
+{% endfor %}{% endif %}{% if data.toast_autovacuum_enabled and data.vacuum_toast|length > 0 %}
+{% for opt in data.vacuum_toast %}{% if opt.name and opt.value %}
+,
+    toast.{{opt.name}} = {{opt.value}}{% endif %}
+{% endfor %}{% endif %}
+
+)
+{### SQL for Tablespace ###}
+{% if data.spcname %}
+TABLESPACE {{ conn|qtIdent(data.spcname) }};
+{% endif %}
+{### Alter SQL for Owner ###}
+{% if data.relowner %}
+
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    OWNER to {{conn|qtIdent(data.relowner)}};
+{% endif %}
+{### Security Labels on Table ###}
+{% if data.seclabels and data.seclabels|length > 0 %}
+
+{% for r in data.seclabels %}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
+{% endfor %}
+{% endif %}
+{###  ACL on Table ###}
+{% if data.relacl %}
+
+{% for priv in data.relacl %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{### SQL for COMMENT ###}
+{% if data.description %}
+COMMENT ON TABLE {{conn|qtIdent(data.schema, data.name)}}
+    IS {{data.description|qtLiteral}};
+{% endif %}
+{#===========================================#}
+{#====== MAIN TABLE TEMPLATE ENDS HERE ======#}
+{#===========================================#}
+{#===========================================#}
+{#  COLUMN SPECIFIC TEMPLATES STARTS HERE    #}
+{#===========================================#}
+{% if data.columns and data.columns|length > 0 %}
+{% for c in data.columns %}
+{% if c.description %}
+
+COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.name, c.name)}}
+    IS {{c.description|qtLiteral}};
+{% endif %}
+{###  Add variables to column ###}
+{% if c.attoptions and c.attoptions|length > 0 %}
+
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    {{ VARIABLE.SET(conn, 'COLUMN', c.name, c.attoptions) }}
+{% endif %}
+{###  ACL ###}
+{% if c.attacl and c.attacl|length > 0 %}
+
+{% for priv in c.attacl %}
+    {{ COLUMN_PRIVILEGE.APPLY(conn, data.schema, data.name, c.name, priv.grantee, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{###  Security Lables ###}
+{% if c.seclabels and c.seclabels|length > 0 %}
+
+{% for r in c.seclabels %}
+{{ COLUMN_SECLABLE.APPLY(conn, 'COLUMN',data.schema, data.name, c.name, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{#===========================================#}
+{#   COLUMN SPECIFIC TEMPLATES ENDS HERE     #}
+{#===========================================#}
+{#======================================#}
+{#   CONSTRAINTS SPECIFIC TEMPLATES     #}
+{#======================================#}
+{{CONSTRAINTS.CONSTRAINT_COMMENTS(conn, data.schema, data.name, data.primary_key)}}
+{{CONSTRAINTS.CONSTRAINT_COMMENTS(conn, data.schema, data.name, data.unique_constraint)}}
+{{CONSTRAINTS.CONSTRAINT_COMMENTS(conn, data.schema, data.name, data.foreign_key)}}
+{{CONSTRAINTS.CONSTRAINT_COMMENTS(conn, data.schema, data.name, data.check_constraint)}}
+{{CONSTRAINTS.CONSTRAINT_COMMENTS(conn, data.schema, data.name, data.exclude_constraint)}}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/delete.sql
new file mode 100644
index 0000000..01d0314
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/delete.sql
@@ -0,0 +1 @@
+DROP TABLE {{conn|qtIdent(data.schema, data.name)}}{% if cascade %} CASCADE{% endif %};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/depend.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/depend.sql
new file mode 100644
index 0000000..f5f39e7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/depend.sql
@@ -0,0 +1,9 @@
+SELECT
+    ref.relname AS refname, d2.refclassid, dep.deptype AS deptype
+FROM pg_depend dep
+    LEFT JOIN pg_depend d2 ON dep.objid=d2.objid AND dep.refobjid != d2.refobjid
+    LEFT JOIN pg_class ref ON ref.oid=d2.refobjid
+    LEFT JOIN pg_attribute att ON d2.refclassid=att.attrelid AND d2.refobjsubid=att.attnum
+    {{ where }} AND
+    dep.classid=(SELECT oid FROM pg_class WHERE relname='pg_attrdef') AND
+    dep.refobjid NOT IN (SELECT d3.refobjid FROM pg_depend d3 WHERE d3.objid=d2.refobjid)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/enable_disable_trigger.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/enable_disable_trigger.sql
new file mode 100644
index 0000000..a4ab154
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/enable_disable_trigger.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
+    {% if is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER ALL;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_columns_for_table.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_columns_for_table.sql
new file mode 100644
index 0000000..3d6bbb7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_columns_for_table.sql
@@ -0,0 +1,16 @@
+SELECT
+    a.attname AS name, format_type(a.atttypid, NULL) AS cltype,
+    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
+{% if tid %}
+    c.oid = {{tid}}::OID
+{% else %}
+    c.relname = {{tname|qtLiteral}}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_inherits.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_inherits.sql
new file mode 100644
index 0000000..50f9a1d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_inherits.sql
@@ -0,0 +1,13 @@
+SELECT c.oid, c.relname , nspname,
+CASE WHEN (nspname NOT LIKE E'pg\_%' AND nspname <> 'public') THEN
+ quote_ident(nspname)||'.'||quote_ident(c.relname)
+ELSE quote_ident(c.relname)
+END AS inherits
+FROM pg_class c
+JOIN pg_namespace n
+ON n.oid=c.relnamespace
+WHERE relkind='r'
+{% if not show_system_objects %}
+AND (n.nspname NOT LIKE E'pg\_%' AND n.nspname NOT in ('information_schema'))
+{% endif %}
+ORDER BY relnamespace, c.relname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oftype.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oftype.sql
new file mode 100644
index 0000000..aed42f2
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oftype.sql
@@ -0,0 +1,6 @@
+SELECT t.oid,
+  quote_ident(n.nspname)||'.'||quote_ident(t.typname) AS typname
+  FROM pg_type t, pg_namespace n
+WHERE t.typtype='c' AND t.typnamespace=n.oid
+  AND NOT (n.nspname like 'pg_%' OR n.nspname='information_schema')
+ORDER BY typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oid.sql
new file mode 100644
index 0000000..e9dc772
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_oid.sql
@@ -0,0 +1,5 @@
+SELECT rel.oid as tid
+FROM pg_class rel
+WHERE rel.relkind IN ('r','s','t')
+AND rel.relnamespace = {{ scid }}::oid
+AND rel.relname = {{data.name|qtLiteral}}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_relations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_relations.sql
new file mode 100644
index 0000000..431ee88
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_relations.sql
@@ -0,0 +1,6 @@
+SELECT c.oid, quote_ident(n.nspname)||'.'||quote_ident(c.relname) AS like_relation
+FROM pg_class c, pg_namespace n
+WHERE c.relnamespace=n.oid
+AND
+c.relkind IN ('r', 'v', 'f')
+ORDER BY 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_tables_for_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_tables_for_constraints.sql
new file mode 100644
index 0000000..1362463
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_tables_for_constraints.sql
@@ -0,0 +1,8 @@
+SELECT cl.oid as value, quote_ident(nspname)||'.'||quote_ident(relname) AS label
+FROM pg_namespace nsp, pg_class cl
+WHERE relnamespace=nsp.oid AND relkind='r'
+   AND nsp.nspname NOT LIKE E'pg\_temp\_%'
+   {% if not show_sysobj %}
+   AND (nsp.nspname NOT LIKE E'pg\_%' AND nsp.nspname NOT in ('information_schema'))
+   {% endif %}
+ORDER BY nspname, relname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_types_where_condition.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_types_where_condition.sql
new file mode 100644
index 0000000..fadfc99
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/get_types_where_condition.sql
@@ -0,0 +1,10 @@
+{### Additional where condition for get_types route for column node ###}
+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 not show_system_objects %}
+AND nsp.nspname != 'information_schema'
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/nodes.sql
new file mode 100644
index 0000000..43f14cb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/nodes.sql
@@ -0,0 +1,6 @@
+SELECT rel.oid, rel.relname AS name,
+    (SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE) AS triggercount,
+    (SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE AND tgenabled = 'O') AS has_enable_triggers
+FROM pg_class rel
+    WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ scid }}::oid
+    ORDER BY rel.relname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/properties.sql
new file mode 100644
index 0000000..99727c9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/properties.sql
@@ -0,0 +1,65 @@
+SELECT rel.oid, rel.relname AS name, rel.reltablespace AS spcoid,rel.relacl AS relacl_str,
+  (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+  (select nspname FROM pg_namespace WHERE oid = {{scid}}::oid ) as schema,
+  pg_get_userbyid(rel.relowner) AS relowner, rel.relhasoids,
+  rel.relhassubclass, rel.reltuples, des.description, con.conname, con.conkey,
+	EXISTS(select 1 FROM pg_trigger
+			JOIN pg_proc pt ON pt.oid=tgfoid AND pt.proname='logtrigger'
+			JOIN pg_proc pc ON pc.pronamespace=pt.pronamespace AND pc.proname='slonyversion'
+			WHERE tgrelid=rel.oid) AS isrepl,
+	(SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE) AS triggercount,
+	(SELECT ARRAY(SELECT CASE WHEN (nspname NOT LIKE E'pg\_%' AND nspname <> 'public') THEN
+            quote_ident(nspname)||'.'||quote_ident(c.relname)
+            ELSE quote_ident(c.relname) END AS inherited_tables
+    FROM pg_inherits i
+    JOIN pg_class c ON c.oid = i.inhparent
+    JOIN pg_namespace n ON n.oid=c.relnamespace
+    WHERE i.inhrelid = rel.oid ORDER BY inhseqno)) AS coll_inherits,
+  (SELECT count(*)
+		FROM pg_inherits i
+      JOIN pg_class c ON c.oid = i.inhparent
+      JOIN pg_namespace n ON n.oid=c.relnamespace
+		WHERE i.inhrelid = rel.oid) AS inherited_tables_cnt,
+	(CASE WHEN rel.relpersistence = 'u' THEN true ELSE false END) AS relpersistence,
+	substring(array_to_string(rel.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor,
+	(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') = 'true')
+	  THEN true ELSE false END) AS autovacuum_enabled,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age,
+	substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age,
+	(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') =  'true')
+	  THEN true ELSE false END) AS toast_autovacuum_enabled,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age,
+	substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age,
+	array_to_string(rel.reloptions, ',') AS table_vacuum_settings_str,
+	array_to_string(tst.reloptions, ',') AS toast_table_vacuum_settings_str,
+	rel.reloptions AS reloptions, tst.reloptions AS toast_reloptions, rel.reloftype, typ.typname,
+	(CASE WHEN rel.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable,
+    -- Added for pgAdmin4
+	(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean  THEN true ELSE false END) AS autovacuum_custom,
+	(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean  AND rel.reltoastrelid != 0 THEN true ELSE false END) AS toast_autovacuum,
+
+	(SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=rel.oid AND sl1.objsubid=0) AS seclabels,
+	(CASE WHEN rel.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_table
+FROM pg_class rel
+  LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace
+  LEFT OUTER JOIN pg_description des ON (des.objoid=rel.oid AND des.objsubid=0 AND des.classoid='pg_class'::regclass)
+  LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
+  LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
+  LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
+WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ scid }}::oid
+{% if tid %}  AND rel.oid = {{ tid }}::oid {% endif %}
+ORDER BY rel.relname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/reset_stats.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/reset_stats.sql
new file mode 100644
index 0000000..36eca0d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/reset_stats.sql
@@ -0,0 +1 @@
+SELECT pg_stat_reset_single_table_counters({{tid}})
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/sql.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/sql.sql
new file mode 100644
index 0000000..17b35ae
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/sql.sql
@@ -0,0 +1 @@
+TAKE ASHESH'S HELP ON THIS TASK :-)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/truncate.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/truncate.sql
new file mode 100644
index 0000000..6a276f3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/truncate.sql
@@ -0,0 +1 @@
+TRUNCATE TABLE {{conn|qtIdent(data.schema, data.name)}}{% if cascade %} CASCADE{% endif %};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql
new file mode 100644
index 0000000..a97ac30
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/9.5_plus/update.sql
@@ -0,0 +1,201 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+{#####################################################}
+{## Rename table ##}
+{#####################################################}
+{% if data.name and data.name != o_data.name %}
+ALTER TABLE {{conn|qtIdent(o_data.schema, o_data.name)}}
+    RENAME TO {{conn|qtIdent(data.name)}};
+
+{% endif %}
+{#####################################################}
+{## Change table schema ##}
+{#####################################################}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER TABLE {{conn|qtIdent(o_data.schema, data.name)}}
+  SET SCHEMA {{conn|qtIdent(data.schema)}};
+
+{% endif %}
+{#####################################################}
+{## Change table owner ##}
+{#####################################################}
+{% if data.relowner and data.relowner != o_data.relowner %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    OWNER TO {{conn|qtIdent(data.relowner)}};
+
+{% endif %}
+{#####################################################}
+{## Update Inherits table definition ##}
+{#####################################################}
+{% if data.coll_inherits_added|length > 0 %}
+{% for val in data.coll_inherits_added %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    INHERIT {{val}};
+
+{% endfor %}
+{% endif %}
+{% if data.coll_inherits_removed|length > 0 %}
+{% for val in data.coll_inherits_removed %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    NO INHERIT {{val}};
+
+{% endfor %}
+{% endif %}
+{#####################################################}
+{## Change hasOID attribute of table ##}
+{#####################################################}
+{% if data.relhasoids is defined and data.relhasoids != o_data.relhasoids %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    SET {% if data.relhasoids %}WITH{% else %}WITHOUT{% endif %} OIDS;
+
+{% endif %}
+{#####################################################}
+{## Change tablespace ##}
+{#####################################################}
+{% if data.spcname and data.spcname != o_data.spcname %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    SET TABLESPACE {{conn|qtIdent(data.spcname)}};
+
+{% endif %}
+{#####################################################}
+{## change fillfactore settings ##}
+{#####################################################}
+{% if data.fillfactor and data.fillfactor != o_data.fillfactor %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
+    SET (FILLFACTOR={{data.fillfactor}});
+
+{% endif %}
+{###############################}
+{## Table AutoVacuum settings ##}
+{###############################}
+{% if o_data.autovacuum_custom and data.autovacuum_custom == false %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} RESET (
+    autovacuum_enabled,
+    autovacuum_analyze_scale_factor,
+    autovacuum_analyze_threshold,
+    autovacuum_freeze_max_age,
+    autovacuum_vacuum_cost_delay,
+    autovacuum_vacuum_cost_limit,
+    autovacuum_vacuum_scale_factor,
+    autovacuum_vacuum_threshold,
+    autovacuum_freeze_min_age,
+    autovacuum_freeze_table_age
+);
+{% elif data.autovacuum_enabled != o_data.autovacuum_enabled %}
+{% if data.autovacuum_enabled and o_data.autovacuum_enabled == false %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+    autovacuum_enabled = true{% elif data.autovacuum_enabled == false and o_data.autovacuum_enabled %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+       autovacuum_enabled = false{% endif %}
+{% if (data.autovacuum_enabled or o_data.autovacuum_enabled )and data.vacuum_table and data.vacuum_table.changed|length > 0 %}
+{% for opt in data.vacuum_table.changed %}{% if opt.name and opt.value %}
+{% if flag or (data.autovacuum_enabled and o_data.autovacuum_enabled == false) or (data.autovacuum_enabled == false and o_data.autovacuum_enabled) %}
+,
+{% else %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+{% set flag = true %}
+{% endif %}
+    {{opt.name}} = {{opt.value}}{% endif %}
+{% if loop.index == data.vacuum_table.changed|length and (flag or (data.autovacuum_enabled and o_data.autovacuum_enabled == false) or (data.autovacuum_enabled == false and o_data.autovacuum_enabled))%}
+
+);
+{% endif %}
+{% endfor %}
+{% elif (data.autovacuum_enabled and o_data.autovacuum_enabled == false) or (data.autovacuum_enabled == false and o_data.autovacuum_enabled) %}
+
+);
+{% endif %}
+{% endif %}
+{#####################################}
+{## Toast table AutoVacuum settings ##}
+{#####################################}
+{% if o_data.toast_autovacuum and data.toast_autovacuum == false %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} RESET (
+    toast.autovacuum_enabled,
+    toast.autovacuum_freeze_max_age,
+    toast.autovacuum_vacuum_cost_delay,
+    toast.autovacuum_vacuum_cost_limit,
+    toast.autovacuum_vacuum_scale_factor,
+    toast.autovacuum_vacuum_threshold,
+    toast.autovacuum_freeze_min_age,
+    toast.autovacuum_freeze_table_age,
+    toast.autovacuum_analyze_threshold,
+    toast.autovacuum_analyze_scale_factor
+);
+{% elif data.toast_autovacuum_enabled != o_data.toast_autovacuum_enabled %}
+{% if data.toast_autovacuum_enabled and o_data.toast_autovacuum_enabled == false %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+    toast.autovacuum_enabled = true{% elif data.toast_autovacuum_enabled == false and o_data.toast_autovacuum_enabled %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+       toast.autovacuum_enabled = false{% endif %}
+{% if (data.toast_autovacuum_enabled or o_data.toast_autovacuum_enabled )and data.vacuum_toast and data.vacuum_toast.changed|length > 0 %}
+{% for opt in data.vacuum_toast.changed %}{% if opt.name and opt.value %}
+{% if flag or (data.toast_autovacuum_enabled and o_data.toast_autovacuum_enabled == false) or (data.toast_autovacuum_enabled == false and o_data.toast_autovacuum_enabled) %}
+,
+{% else %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.name)}} SET (
+{% set flag = true %}
+{% endif %}
+    toast.{{opt.name}} = {{opt.value}}{% endif %}
+{% if loop.index == data.vacuum_toast.changed|length and (flag or (data.toast_autovacuum_enabled and o_data.toast_autovacuum_enabled == false) or (data.toast_autovacuum_enabled == false and o_data.toast_autovacuum_enabled))%}
+
+);
+{% endif %}
+{% endfor %}
+{% elif (data.toast_autovacuum_enabled and o_data.toast_autovacuum_enabled == false) or (data.toast_autovacuum_enabled == false and o_data.toast_autovacuum_enabled) %}
+
+);
+{% endif %}
+{% endif %}
+{#####################################################}
+{## Change table comments ##}
+{#####################################################}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TABLE {{conn|qtIdent(data.schema, data.name)}}
+  IS {{data.description|qtLiteral}};
+
+{% endif %}
+{#####################################################}
+{## Update table Privileges ##}
+{#####################################################}
+{% if data.relacl %}
+{% if 'deleted' in data.relacl %}
+{% for priv in data.relacl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.relacl %}
+{% for priv in data.relacl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.relacl %}
+{% for priv in data.relacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{#####################################################}
+{## Update table SecurityLabel ##}
+{#####################################################}
+{% if data.seclabels and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABEL.UNSET(conn, 'TABLE', data.name, r.provider, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.SET(conn, 'TABLE', data.name, r.provider, r.label, data.schema) }}
+{% endfor %}
+{% endif %}
+
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/macros/constraints.macro b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/macros/constraints.macro
new file mode 100644
index 0000000..fe7389a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/table/sql/macros/constraints.macro
@@ -0,0 +1,103 @@
+{##########################}
+{# Macros for Constraints #}
+{##########################}
+{#    CREATE MODE ONLY    #}
+{##########################}
+{% macro PRIMARY_KEY(conn, data) -%}
+{% if data.columns|length > 0 %}
+
+    {% if data.name %}CONSTRAINT {{conn|qtIdent(data.name)}} {% endif %}PRIMARY KEY ({% for c in data.columns%}
+{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(c.column)}}{% endfor %}){% if data.fillfactor %}
+
+        WITH (FILLFACTOR={{data.fillfactor}}){% endif %}
+{% if data.spcname and data.spcname != "pg_default" %}
+
+        USING INDEX TABLESPACE {{ conn|qtIdent(data.spcname) }}{% endif %}
+{% if data.condeferrable %}
+
+        DEFERRABLE{% if data.condeferred %} INITIALLY DEFERRED{% endif%}{% endif%}
+{% endif %}
+{%- endmacro %}
+{% macro UNIQUE(conn, unique_data) -%}
+{% for data in unique_data %}
+{% if data.columns|length > 0 %}{% if loop.index !=1 %},{% endif %}
+
+    {% if data.name %}CONSTRAINT {{conn|qtIdent(data.name)}} {% endif %}UNIQUE ({% for c in data.columns%}
+{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(c.column)}}{% endfor %}){% if data.fillfactor %}
+
+        WITH (FILLFACTOR={{data.fillfactor}}){% endif %}
+{% if data.spcname and data.spcname != "pg_default" %}
+
+        USING INDEX TABLESPACE {{ conn|qtIdent(data.spcname) }}{% endif %}
+{% if data.condeferrable %}
+
+        DEFERRABLE{% if data.condeferred %} INITIALLY DEFERRED{% endif%}{% endif%}
+{% endif %}
+{% endfor %}
+{%- endmacro %}
+{% macro CHECK(conn, check_data) -%}
+{% for data in check_data %}{% if loop.index !=1 %},{% endif %}
+
+    {% if data.name %}CONSTRAINT {{ conn|qtIdent(data.name) }} {% endif%}CHECK ({{ data.consrc }}){% if data.convalidated %}
+ NOT VALID{% endif %}{% if data.connoinherit %} NO INHERIT{% endif %}
+{% endfor %}
+{%- endmacro %}
+{% macro FOREIGN_KEY(conn, foreign_key_data) -%}
+{% for data in foreign_key_data %}{% if loop.index != 1 %},{% endif %}
+
+    {% if data.name %}CONSTRAINT {{conn|qtIdent(data.name)}} {% endif %}FOREIGN KEY ({% for columnobj in data.columns %}{% if loop.index != 1 %}
+, {% endif %}{{ conn|qtIdent(columnobj.local_column)}}{% endfor %})
+        REFERENCES {{ conn|qtIdent(data.remote_schema, data.remote_table) }} ({% for columnobj in data.columns %}{% if loop.index != 1 %}
+, {% endif %}{{ conn|qtIdent(columnobj.referenced)}}{% endfor %}) {% if data.confmatchtype %}MATCH FULL{% else %}MATCH SIMPLE{% endif%}
+
+        ON UPDATE{% if data.confupdtype  == 'a' %}
+ NO ACTION{% elif data.confupdtype  == 'r' %}
+ RESTRICT{% elif data.confupdtype  == 'c' %}
+ CASCADE{% elif data.confupdtype  == 'n' %}
+ SET NULL{% elif data.confupdtype  == 'd' %}
+ SET DEFAULT{% endif %}
+
+        ON DELETE{% if data.confdeltype  == 'a' %}
+ NO ACTION{% elif data.confdeltype  == 'r' %}
+ RESTRICT{% elif data.confdeltype  == 'c' %}
+ CASCADE{% elif data.confdeltype  == 'n' %}
+ SET NULL{% elif data.confdeltype  == 'd' %}
+ SET DEFAULT{% endif %}
+{% if data.condeferrable %}
+
+        DEFERRABLE{% if data.condeferred %}
+ INITIALLY DEFERRED{% endif%}
+{% endif%}
+{% if data.convalidated %}
+
+        NOT VALID{% endif%}
+{% endfor %}
+{%- endmacro %}
+{% macro EXCLUDE(conn, exclude_data) -%}
+{% for data in exclude_data %}{% if loop.index != 1 %},{% endif %}
+
+    {% if data.name %}CONSTRAINT {{ conn|qtIdent(data.name) }} {% endif%}EXCLUDE {% if data.amname and data.amname != '' %}USING {{data.amname}}{% endif %} ({% for col in data.columns %}{% if loop.index != 1 %},
+        {% endif %}{{ conn|qtIdent(col.column)}} {% if col.oper_class and col.oper_class != '' %}{{col.oper_class}} {% endif%}{% if col.order %}ASC{% else %}DESC{% endif %} NULLS {% if col.nulls_order %}FIRST{% else %}LAST{% endif %} WITH {{col.operator}}{% endfor %}){% if data.fillfactor %}
+
+        WITH (FILLFACTOR={{data.fillfactor}}){% endif %}
+        {% if data.spcname and data.spcname != "pg_default" %}
+
+        USING INDEX TABLESPACE {{ conn|qtIdent(data.spcname) }}{% endif %}
+{% if data.condeferrable %}
+
+        DEFERRABLE{% if data.condeferred %}
+ INITIALLY DEFERRED{% endif%}
+{% endif%}{% if data.constraint %} WHERE ({{data.constraint}}){% endif%}
+{% endfor %}
+{%- endmacro %}
+{##########################}
+{#     COMMENTS ONLY      #}
+{##########################}
+{% macro CONSTRAINT_COMMENTS(conn, schema, table, data) -%}
+{% for d in data %}
+{% if d.name and d.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(d.name) }} ON {{ conn|qtIdent(schema, table) }}
+    IS {{ d.comment|qtLiteral }};
+{% endif %}
+{% endfor %}
+{%- endmacro %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/alter.sql
new file mode 100644
index 0000000..93f323e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/alter.sql
@@ -0,0 +1,9 @@
+{## Alter index to use cluster type ##}
+{% if data.indisclustered %}
+ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
+    CLUSTER ON {{conn|qtIdent(data.name)}};
+{% endif %}
+{## Changes description ##}
+{% if data.description %}
+COMMENT ON INDEX {{conn|qtIdent(data.name)}}
+    IS {{data.description|qtLiteral}};{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/backend_support.sql
new file mode 100644
index 0000000..bb5e8d8
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/backend_support.sql
@@ -0,0 +1,9 @@
+{#=============Checks if it is materialized view========#}
+{% if vid %}
+SELECT
+    CASE WHEN c.relkind = 'm' THEN False ELSE True END As m_view
+FROM
+    pg_class c
+WHERE
+    c.oid = {{ vid }}::oid
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..b619f71
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/create.sql
@@ -0,0 +1,27 @@
+{### Set a flag which allows us to put OR between events ###}
+{% set or_flag = False %}
+CREATE{% if data.is_constraint_trigger %} CONSTRAINT{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}
+    {{data.fires}} {% if data.evnt_insert %}INSERT{% set or_flag = True %}
+{% endif %}{% if data.evnt_delete %}
+{% if or_flag %} OR {% endif %}DELETE{% set or_flag = True %}
+{% endif %}{% if data.evnt_turncate %}
+{% if or_flag %} OR {% endif %}TRUNCATE{% set or_flag = True %}
+{% endif %}{% if data.evnt_update %}
+{% if or_flag %} OR {% endif %}UPDATE {% if data.columns|length > 0 %}OF {% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c.column) }}{% endfor %}{% endif %}
+{% endif %}
+
+    ON {{ conn|qtIdent(data.schema, data.table) }}
+{% if data.tgdeferrable %}
+    DEFERRABLE{% if data.tginitdeferred %} INITIALLY DEFERRED{% endif %}
+{% endif %}
+    FOR EACH{% if data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %}
+{% if data.whenclause %}
+
+    WHEN {{ data.whenclause }}{% endif %}
+
+    {% if data.code %}{{ data.code }}{% else %}EXECUTE PROCEDURE {{ data.tfunction }}{% if data.tgargs %}({{ data.tgargs }}){% else %}(){% endif%}{% endif%};
+
+{% if data.description %}
+COMMENT ON TRIGGER {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{data.description|qtLiteral}};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..4c6e82b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+DROP TRIGGER {{conn|qtIdent(data.name)}} ON {{conn|qtIdent(data.nspname, data.relname )}}{% if cascade %} CASCADE{% endif %};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/enable_disable_trigger.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/enable_disable_trigger.sql
new file mode 100644
index 0000000..b700927
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/enable_disable_trigger.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    {% if data.is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER {{ conn|qtIdent(data.name) }};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_columns.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_columns.sql
new file mode 100644
index 0000000..c74c68b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_columns.sql
@@ -0,0 +1,6 @@
+SELECT att.attname as name
+FROM pg_attribute att
+    WHERE att.attrelid = {{tid}}::oid
+    AND att.attnum IN ({{ clist }})
+    AND att.attisdropped IS FALSE
+    ORDER BY att.attnum
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..cf30257
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,5 @@
+SELECT t.oid
+FROM pg_trigger t
+    WHERE NOT tgisinternal
+    AND tgrelid = {{tid}}::OID
+    AND tgname = {{data.name|qtLiteral}};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_parent.sql
new file mode 100644
index 0000000..5dd5d3c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_parent.sql
@@ -0,0 +1,5 @@
+SELECT nsp.nspname AS schema ,rel.relname AS table
+FROM pg_class rel
+    JOIN pg_namespace nsp
+    ON rel.relnamespace = nsp.oid::int
+    WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_triggerfunctions.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_triggerfunctions.sql
new file mode 100644
index 0000000..6134e0e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/get_triggerfunctions.sql
@@ -0,0 +1,11 @@
+SELECT quote_ident(nspname) || '.' || quote_ident(proname) AS tfunctions
+FROM pg_proc p, pg_namespace n, pg_language l
+    WHERE p.pronamespace = n.oid
+    AND p.prolang = l.oid
+    -- PGOID_TYPE_TRIGGER = 2279
+    AND l.lanname != 'edbspl' AND prorettype = 2279
+    -- If Show SystemObjects is not true
+    {% if not show_system_objects %}
+    AND (nspname NOT LIKE E'pg\_%' AND nspname NOT in ('information_schema'))
+    {% endif %}
+    ORDER BY nspname ASC, proname ASC
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..095ada3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/nodes.sql
@@ -0,0 +1,5 @@
+SELECT t.oid, t.tgname as name, (CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger
+FROM pg_trigger t
+    WHERE NOT tgisinternal
+    AND tgrelid = {{tid}}::OID
+    ORDER BY tgname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..535627b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/properties.sql
@@ -0,0 +1,23 @@
+SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,
+    nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction,
+    COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'),
+    substring(pg_get_triggerdef(t.oid), 'WHEN (.*)  \\$trigger')) AS whenclause,
+    -- We need to convert tgargs column bytea datatype to array datatype
+    (string_to_array(encode(tgargs, 'escape'), '\000')::text[])[1:tgnargs] AS tgargs,
+{% if datlastsysoid %}
+    (CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger,
+{% endif %}
+    (CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constarint,
+    (CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger
+FROM pg_trigger t
+    JOIN pg_class cl ON cl.oid=tgrelid
+    JOIN pg_namespace na ON na.oid=relnamespace
+    LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_trigger'::regclass)
+    LEFT OUTER JOIN pg_proc p ON p.oid=t.tgfoid
+    LEFT OUTER JOIN pg_language l ON l.oid=p.prolang
+WHERE NOT tgisinternal
+    AND tgrelid = {{tid}}::OID
+{% if trid %}
+    AND t.oid = {{trid}}::OID
+{% endif %}
+ORDER BY tgname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..fa64809
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/trigger/sql/9.1_plus/update.sql
@@ -0,0 +1,8 @@
+{% if data.name and o_data.name != data.name %}
+ALTER TRIGGER {{ conn|qtIdent(o_data.name) }} ON {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    RENAME TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{% if data.description is defined  and o_data.description != data.description %}
+COMMENT ON TRIGGER {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    IS {{data.description|qtLiteral}};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py
new file mode 100644
index 0000000..634483a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py
@@ -0,0 +1,944 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Trigger Node """
+
+from flask import render_template, make_response, request, jsonify
+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 as database
+from pgadmin.browser.server_groups.servers.databases.schemas.utils import \
+    trigger_definition as _trigger_definition
+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 json
+
+
+class TriggerModule(CollectionNodeModule):
+    """
+     class TriggerModule(CollectionNodeModule)
+
+        A module class for Trigger node derived from CollectionNodeModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the Trigger and it's base module.
+
+    * get_nodes(gid, sid, did, scid, tid)
+      - Method is used to generate the browser collection node.
+
+    * node_inode()
+      - Method is overridden from its base class to make the node as leaf node.
+
+    * script_load()
+      - Load the module script for trigger, when any of the server node is
+        initialized.
+    """
+
+    NODE_TYPE = 'trigger'
+    COLLECTION_LABEL = gettext("Triggers")
+
+    def __init__(self, *args, **kwargs):
+        """
+        Method is used to initialize the TriggerModule and it's base module.
+
+        Args:
+            *args:
+            **kwargs:
+        """
+        self.min_ver = None
+        self.max_ver = None
+        super(TriggerModule, self).__init__(*args, **kwargs)
+
+    def BackendSupported(self, manager, **kwargs):
+        """
+        Load this module if vid is view, we will not load it under
+        material view
+        """
+        if super(TriggerModule, self).BackendSupported(manager, **kwargs):
+            conn = manager.connection(did=kwargs['did'])
+            # If DB is not connected then return error to browser
+            if not conn.connected():
+                return precondition_required(
+                    gettext(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            if 'vid' not in kwargs:
+                return True
+
+            template_path = 'trigger/sql/9.1_plus'
+            SQL = render_template("/".join(
+                  [template_path, 'backend_support.sql']), vid=kwargs['vid'])
+            status, res = conn.execute_scalar(SQL)
+            # check if any errors
+            if not status:
+                return internal_server_error(errormsg=res)
+            # Check vid is view not material view
+            # then true, othewise false
+            return res
+
+    def get_nodes(self, gid, sid, did, scid, **kwargs):
+        """
+        Generate the collection node
+        """
+        assert('tid' in kwargs or 'vid' in kwargs)
+        yield self.generate_browser_collection_node(
+            kwargs['tid'] if 'tid' in kwargs else kwargs['vid']
+        )
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for server, when any of the server-group node is
+        initialized.
+        """
+        return database.DatabaseModule.NODE_TYPE
+
+    @property
+    def node_inode(self):
+        """
+        Load the module node as a leaf node
+        """
+        return True
+
+    @property
+    def csssnippets(self):
+        """
+        Returns a snippet of css to include in the page
+        """
+        snippets = [
+                render_template(
+                    "trigger/css/trigger.css",
+                    node_type=self.node_type
+                    )
+                ]
+
+        for submodule in self.submodules:
+            snippets.extend(submodule.csssnippets)
+
+        return snippets
+
+blueprint = TriggerModule(__name__)
+
+
+class TriggerView(PGChildNodeView):
+    """
+    This class is responsible for generating routes for Trigger node
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the TriggerView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * list()
+      - This function is used to list all the Trigger nodes within that
+      collection.
+
+    * nodes()
+      - This function will used to create all the child node within that
+        collection, Here it will create all the Trigger node.
+
+    * properties(gid, sid, did, scid, tid, trid)
+      - This function will show the properties of the selected Trigger node
+
+    * create(gid, sid, did, scid, tid)
+      - This function will create the new Trigger object
+
+    * update(gid, sid, did, scid, tid, trid)
+      - This function will update the data for the selected Trigger node
+
+    * delete(self, gid, sid, scid, tid, trid):
+      - This function will drop the Trigger object
+
+    * enable(self, gid, sid, scid, tid, trid):
+      - This function will enable/disable Trigger object
+
+    * msql(gid, sid, did, scid, tid, trid)
+      - This function is used to return modified SQL for the selected
+        Trigger node
+
+    * get_sql(data, scid, tid, trid)
+      - This function will generate sql from model data
+
+    * sql(gid, sid, did, scid, tid, trid):
+      - This function will generate sql to show it in sql pane for the
+        selected Trigger node.
+
+    * dependency(gid, sid, did, scid, tid, trid):
+      - This function will generate dependency list show it in dependency
+        pane for the selected Trigger node.
+
+    * dependent(gid, sid, did, scid, tid, trid):
+      - This function will generate dependent list to show it in dependent
+        pane for the selected Trigger node.
+
+    * get_trigger_functions(gid, sid, did, scid, tid, trid):
+      - This function will return list of trigger functions available
+        via AJAX response
+
+    * _column_details(tid, clist)::
+      - This function will fetch the columns for trigger
+
+    * _trigger_definition(data):
+      - This function will set additional trigger definitions in
+        AJAX response
+    """
+
+    node_type = blueprint.node_type
+
+    parent_ids = [
+            {'type': 'int', 'id': 'gid'},
+            {'type': 'int', 'id': 'sid'},
+            {'type': 'int', 'id': 'did'},
+            {'type': 'int', 'id': 'scid'},
+            {'type': 'int', 'id': 'tid'}
+            ]
+    ids = [
+            {'type': 'int', 'id': 'trid'}
+            ]
+
+    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_triggerfunctions': [{'get': 'get_trigger_functions'},
+                               {'get': 'get_trigger_functions'}],
+        'enable': [{'put': 'enable_disable_trigger'}]
+    })
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+                kwargs['sid']
+            )
+            self.conn = self.manager.connection(did=kwargs['did'])
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    gettext(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            # We need datlastsysoid to check if current trigger is system trigger
+            self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+            # we will set template path for sql scripts
+            self.template_path = 'trigger/sql/9.1_plus'
+            # Store server type
+            self.server_type = self.manager.server_type
+            # We need parent's name eg table name and schema name
+            # when we create new trigger in update we can fetch it using
+            # property sql
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=kwargs['tid'])
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            for row in rset['rows']:
+                self.schema = row['schema']
+                self.table = row['table']
+
+            # Here we are storing trigger definition
+            # We will use it to check trigger type definition
+            self.trigger_definition = {
+                'TRIGGER_TYPE_ROW':         (1 << 0),
+                'TRIGGER_TYPE_BEFORE':      (1 << 1),
+                'TRIGGER_TYPE_INSERT':      (1 << 2),
+                'TRIGGER_TYPE_DELETE':      (1 << 3),
+                'TRIGGER_TYPE_UPDATE':      (1 << 4),
+                'TRIGGER_TYPE_TRUNCATE':    (1 << 5),
+                'TRIGGER_TYPE_INSTEAD':     (1 << 6)
+            }
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+    @check_precondition
+    def get_trigger_functions(self, gid, sid, did, scid, tid, trid=None):
+        """
+        This function will return list of trigger functions available
+        via AJAX response
+        """
+        res = [{'label': '', 'value': ''}]
+
+        # TODO: REMOVE True Condition , it's just for testing
+        # If server type is EDB-PPAS then we also need to add
+        # inline edb-spl along with options fetched by below sql
+
+        if self.server_type == 'ppas':
+            res.append({
+                'label': 'Inline EDB-SPL',
+                'value': 'Inline EDB-SPL'
+                })
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'get_triggerfunctions.sql']),
+                                  show_system_objects=self.blueprint.show_system_objects
+                                  )
+            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['tfunctions'],
+                             'value': row['tfunctions']}
+                        )
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def list(self, gid, sid, did, scid, tid):
+        """
+        This function is used to list all the trigger nodes within that collection.
+
+        Args:
+            gid: Server group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+
+        Returns:
+            JSON of available trigger nodes
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']), tid=tid)
+        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, tid):
+        """
+        This function will used to create all the child node within that collection.
+        Here it will create all the trigger node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+
+        Returns:
+            JSON of available trigger child nodes
+        """
+        res = []
+        SQL = render_template("/".join([self.template_path,
+                                        'nodes.sql']), tid=tid)
+        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'],
+                        tid,
+                        row['name'],
+                        icon="icon-trigger" if row['is_enable_trigger']
+                        else "icon-trigger-bad"
+                    ))
+
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    def _column_details(self, tid, clist):
+        """
+        This functional will fetch list of column for trigger
+
+        Args:
+            tid: Table OID
+            clist: List of columns
+
+        Returns:
+            Updated properties data with column
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'get_columns.sql']),
+                              tid=tid, clist=clist)
+        status, rset = self.conn.execute_2darray(SQL)
+        if not status:
+            return internal_server_error(errormsg=rset)
+        # 'tgattr' contains list of columns from table used in trigger
+        columns = []
+
+        for row in rset['rows']:
+            columns.append({'column': row['name']})
+
+        return columns
+
+    def _trigger_definition(self, data):
+        """
+        This functional will set the trigger definition
+
+        Args:
+            data: Properties data
+
+        Returns:
+            Updated properties data with trigger definition
+        """
+
+        # Fires event definition
+        if data['tgtype'] & self.trigger_definition['TRIGGER_TYPE_BEFORE']:
+             data['fires'] = 'BEFORE'
+        elif data['tgtype'] & self.trigger_definition['TRIGGER_TYPE_INSTEAD']:
+            data['fires'] = 'INSTEAD OF'
+        else:
+            data['fires'] = 'AFTER'
+
+        # Trigger of type definition
+        if data['tgtype'] & self.trigger_definition['TRIGGER_TYPE_ROW']:
+            data['is_row_trigger'] = True
+        else:
+            data['is_row_trigger'] = False
+
+        # Event definition
+        if data['tgtype'] & self.trigger_definition['TRIGGER_TYPE_INSERT']:
+            data['evnt_insert'] = True
+        else:
+            data['evnt_insert'] = False
+
+        if data['tgtype'] & self.trigger_definition['TRIGGER_TYPE_DELETE']:
+            data['evnt_delete'] = True
+        else:
+            data['evnt_delete'] = False
+
+        if data['tgtype'] & self.trigger_definition['TRIGGER_TYPE_UPDATE']:
+            data['evnt_update'] = True
+        else:
+            data['evnt_update'] = False
+
+        if data['tgtype'] & self.trigger_definition['TRIGGER_TYPE_TRUNCATE']:
+            data['evnt_turncate'] = True
+        else:
+            data['evnt_turncate'] = False
+
+        return data
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid, trid):
+        """
+        This function will show the properties of the selected trigger node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did:  Database ID
+            scid: Schema ID
+            scid: Schema ID
+            tid: Table ID
+            trid: Trigger ID
+
+        Returns:
+            JSON of selected trigger node
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              tid=tid, trid=trid,
+                              datlastsysoid=self.datlastsysoid)
+
+        status, res = self.conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        # Making copy of output for future use
+        data = dict(res['rows'][0])
+        if data['tgnargs'] > 1:
+            # We know that trigger has more than 1 arguments, let's join them
+            # and convert it as string
+            data['tgargs'] = ', '.join(data['tgargs'])
+
+        if len(data['tgattr']) > 1:
+            columns = ', '.join(data['tgattr'].split(' '))
+            data['columns'] = self._column_details(tid, columns)
+
+        data = self._trigger_definition(data)
+
+        return ajax_response(
+                response=data,
+                status=200
+                )
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, tid):
+        """
+        This function will creates new the trigger object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+        """
+        data = request.form if request.form else json.loads(
+            request.data.decode()
+        )
+
+        for k, v in data.items():
+            try:
+                data[k] = json.loads(v)
+            except (ValueError, TypeError):
+                data[k] = v
+
+        required_args = {
+            'name': 'Name',
+            'tfunction': 'Trigger function'
+        }
+
+        for arg in required_args:
+            if arg not in data:
+                return make_json_response(
+                        status=410,
+                        success=0,
+                        errormsg=gettext("Couldn't find the required parameter (%s)." % \
+                                required_args[arg])
+                        )
+
+        # Adding parent into data dict, will be using it while creating sql
+        data['schema'] = self.schema
+        data['table'] = self.table
+
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'create.sql']),
+                                  data=data, conn=self.conn)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # we need oid to to add object in tree at browser
+            SQL = render_template("/".join([self.template_path,
+                                            'get_oid.sql']),
+                                  tid=tid, data=data)
+            status, trid = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=tid)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    trid,
+                    scid,
+                    data['name'],
+                    icon="icon-trigger"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid, trid):
+        """
+        This function will updates existing the trigger object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           trid: Trigger ID
+        """
+        # Below will decide if it's simple drop or drop with cascade call
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+
+        try:
+            # We will first fetch the trigger name for current request
+            # so that we create template for dropping trigger
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  tid=tid, trid=trid,
+                                  datlastsysoid=self.datlastsysoid)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = dict(res['rows'][0])
+
+            SQL = render_template("/".join([self.template_path,
+                                            'delete.sql']),
+                                  data=data, conn=self.conn, 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("Trigger is dropped"),
+                data={
+                    'id': trid,
+                    'tid': tid
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid, trid):
+        """
+        This function will updates existing the trigger object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           trid: Trigger ID
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        try:
+            SQL = self.get_sql(scid, tid, trid, data)
+            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="Trigger updated",
+                    data={
+                        'id': trid,
+                        'tid': tid,
+                        'scid': scid
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': trid,
+                        'tid': tid,
+                        'scid': scid
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid, trid=None):
+        """
+        This function will generates modified sql for trigger object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           trid: Trigger ID (When working with existing trigger)
+        """
+        data = dict()
+        for k, v in request.args.items():
+            try:
+                data[k] = json.loads(v)
+            except ValueError:
+                data[k] = v
+
+        # Adding parent into data dict, will be using it while creating sql
+        data['schema'] = self.schema
+        data['table'] = self.table
+
+        try:
+            SQL = self.get_sql(scid, tid, trid, data)
+
+            if SQL and SQL.strip('\n') and SQL.strip(' '):
+                return make_json_response(
+                        data=SQL,
+                        status=200
+                        )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def get_sql(self, scid, tid, trid, data):
+        """
+        This function will genrate sql from model data
+        """
+        if trid is not None:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  tid=tid, trid=trid,
+                                  datlastsysoid=self.datlastsysoid)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            old_data = dict(res['rows'][0])
+
+            # If name is not present in data then
+            # we will fetch it from old data, we also need schema & table name
+            if 'name' not in data:
+                data['name'] = old_data['name']
+
+            if old_data['tgnargs'] > 1:
+                # We know that trigger has more than 1 arguments, let's join them
+                old_data['tgargs'] = ', '.join(old_data['tgargs'])
+
+            if len(old_data['tgattr']) > 1:
+                columns = ', '.join(old_data['tgattr'].split(' '))
+                old_data['columns'] = self._column_details(tid, columns)
+
+            old_data = self._trigger_definition(old_data)
+
+            SQL = render_template(
+                "/".join([self.template_path, 'update.sql']),
+                data=data, o_data=old_data, conn=self.conn
+                )
+        else:
+            required_args = {
+                'name': 'Name',
+                'tfunction': 'Trigger function'
+            }
+
+            for arg in required_args:
+                if arg not in data:
+                    return gettext('-- incomplete definition')
+
+            # If the request for new object which do not have did
+            SQL = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=data, conn=self.conn)
+        return SQL
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, tid, trid):
+        """
+        This function will generates reverse engineered sql for trigger object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           trid: Trigger ID
+        """
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  tid=tid, trid=trid,
+                                  datlastsysoid=self.datlastsysoid)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = dict(res['rows'][0])
+            # Adding parent into data dict, will be using it while creating sql
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            if data['tgnargs'] > 1:
+                # We know that trigger has more than 1 arguments, let's join them
+                data['tgargs'] = ', '.join(data['tgargs'])
+
+            if len(data['tgattr']) > 1:
+                columns = ', '.join(data['tgattr'].split(' '))
+                data['columns'] = self._column_details(tid, columns)
+
+            data = self._trigger_definition(data)
+
+            SQL = self.get_sql(scid, tid, None, data)
+
+            sql_header = "-- Trigger: {0}\n\n-- ".format(data['name'])
+            sql_header += render_template("/".join([self.template_path,
+                                                    'delete.sql']),
+                                          data=data, conn=self.conn)
+
+            SQL = sql_header + '\n\n' + SQL.strip('\n')
+
+            # If trigger is disbaled then add sql code for the same
+            if not data['is_enable_trigger']:
+                SQL += '\n\n'
+                SQL += render_template("/".join([self.template_path,
+                                                 'enable_disable_trigger.sql']),
+                                       data=data, conn=self.conn)
+
+            return ajax_response(response=SQL)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def enable_disable_trigger(self, gid, sid, did, scid, tid, trid):
+        """
+        This function will enable OR disable the current trigger object
+
+         Args:
+           gid: Server Group ID
+           sid: Server ID
+           did: Database ID
+           scid: Schema ID
+           tid: Table ID
+           trid: Trigger ID
+        """
+
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        # Convert str 'true' to boolean type
+        is_enable_flag = json.loads(data['enable'])
+
+        try:
+
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  tid=tid, trid=trid,
+                                  datlastsysoid=self.datlastsysoid)
+
+            status, res = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            o_data = dict(res['rows'][0])
+
+            # If enable is set to true means we need SQL to enable
+            # current trigger which is disabled already so we need to
+            # alter the 'is_enable_trigger' flag so that we can render
+            # correct SQL for operation
+            o_data['is_enable_trigger'] = is_enable_flag
+
+            # Adding parent into data dict, will be using it while creating sql
+            o_data['schema'] = self.schema
+            o_data['table'] = self.table
+
+            SQL = render_template("/".join([self.template_path,
+                                            'enable_disable_trigger.sql']),
+                                  data=o_data, conn=self.conn)
+            status, res = self.conn.execute_scalar(SQL)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info="Trigger updated",
+                data={
+                    'id': trid,
+                    'tid': tid,
+                    'scid': scid
+                }
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, tid, trid):
+        """
+        This function get the dependents and return ajax response
+        for the trigger node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+            trid: Trigger ID
+        """
+        dependents_result = self.get_dependents(
+            self.conn, trid
+        )
+
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, tid, trid):
+        """
+        This function get the dependencies and return ajax response
+        for the trigger node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: Table ID
+            trid: Trigger ID
+
+        """
+        dependencies_result = self.get_dependencies(
+            self.conn, trid
+        )
+
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+TriggerView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/img/coll-trigger.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/img/coll-trigger.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c339406fa325dad67f59141a6a08a5334debcda
GIT binary patch
literal 350
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv5AX?b1=6kiPU=rwT=MDBga5xP
z_8ouw|L>&#A2Xj{cR#w-VBI{+?aRZi97(#*xCE$_u_VYZn8D%MjWi&Kv%n*=7)X17
zvD?XPJ0K&^)5S4_<9f0{kWc~xb80dxhx5@;Pr)+*-bQ9j-mAoSvNTT3G+F96CDTtU
zys(goD^vGXfVVgEs_XYmg7@y()6wYo{DM|*UmqVUo8r>Dvy9XIn6#3t7H`cGJ>lb~
z`u6T@g>5h9G)5dcu;fSs3qw<vM2e~mdkWBI)e_f;l9a@fRIB8oR3OD*WMF8ZYiOiv
zU>IU#U}a)#Wn!vrU}|MxFz=n-DijU5`6-!cl@JXEmS7D))h1R3W)KahriZQpYGCkm
L^>bP0l+XkKAR%?3

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/img/trigger-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/img/trigger-bad.png
new file mode 100644
index 0000000000000000000000000000000000000000..6cc2fe96c8f234a73672cc73fd272a15fc7de4f9
GIT binary patch
literal 610
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf&j6ngS0LTG@1*|3#U-B}o%;Xd
z@BjbbfBg9M>-UfEKmPptck0#i>q$vR85k~vM;<wH;@IDBlcy{^>h66vGt;?!=F#Zb
zZ{_7j+`X5s+_2Bq>0@|!L|y;4va)YQMPKvs_E<Z73=4bj@BhxjGp@SlRA9*0+}tl2
z8K0AqK7@w8_XCQ1SoF-Bv2V}k#Ke!$(H}xW-uwE!b9XmezhLo!y|+?RKLi6seBZgb
zt<o{>KeX=}P-AfLdmo>7uC8zF>|a}0_%=`3<>K}sDCoVn_gfd&*XHK03=LmuYd7cA
zd<YDD=jHX**=44)*Gnz!7pkhy6%^h&IUjNJn(5;GTwY=QPpA1n&oh<;`2{mLJiCzw
z<Zu>vL>2>S4={E+nQaGTEbw%343W5;d-f{dp#&b*2lcZ1ns=Wr`2LXZ@Y}!gZH&=I
zLRA-+#NU!T_D{s!WsY3O^U7b0%|TU)i%;fkeASY}^oDQwY2NeVd+dG)a<mDAsWxTk
z^s!7@X<|1)aN^mgfg01U^cJ}~Yz@md*(!e}tNg;-uZo$P&5CZ1PnImpJtH2m?%Iu<
z@>M5p$oE-1tl3!?oFZs`=O(lN+cwV$Ps+oeiATx_obU^~(ExO-YKdz^NlIc#s#S7P
zDv)9@GB7mMH89pSum~|UvNANcGO*M(Ftai+xcqEI5{ic0{FKbJO57S2?H0HP)WG2B
L>gTe~DWM4fI7SDA

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/img/trigger.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/img/trigger.png
new file mode 100644
index 0000000000000000000000000000000000000000..3b413e4a648c999c0dcc005aaaf02a710be4414b
GIT binary patch
literal 324
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPHF3h)VW1=6kiPU=rwT=MDBssBIz
z{{Mgc|Bu?=uTvji_Bye{WYZ$U_4Az$Y>2*ja-Gx8WkA)8B|(0{3=Yq3qyagc1s;*b
zK-vS0-A-oP0U3dwE{-7_*OL<(nB7!eIhs%2;5b!K7}#Lo<lL;#vt^6P2CZeEK7CSR
z6j;p6;jn5(+POKFoI0Dbu4-*CTXuK1IcI}p`Z<TQJF7XfW=>*a4r5?=RWIaqXXEsv
zK*Lo_Tq8<S5=&C8l8aJ-6oZk0p`osUp{|i}h=H*c5E&Y48<<%c7@VBUTY#b=H$Npa
rtrDccK-a($s3*k8*viDj%D_z9z!a$A)b!9bKn)C@u6{1-oD!M<5hib6

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/templates/trigger/css/trigger.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/templates/trigger/css/trigger.css
new file mode 100644
index 0000000..811c838
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/templates/trigger/css/trigger.css
@@ -0,0 +1,20 @@
+.icon-coll-trigger {
+  background-image: url('{{ url_for('NODE-trigger.static', filename='img/coll-trigger.png' )}}') !important;
+  background-repeat: no-repeat;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
+
+.icon-trigger {
+  background-image: url('{{ url_for('NODE-trigger.static', filename='img/trigger.png') }}') !important;
+  background-repeat: no-repeat;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
+
+.icon-trigger-bad {
+  background-image: url('{{ url_for('NODE-trigger.static', filename='img/trigger-bad.png') }}') !important;
+  border-radius: 10px
+}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/templates/trigger/js/trigger.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/templates/trigger/js/trigger.js
new file mode 100644
index 0000000..bd22795
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/templates/trigger/js/trigger.js
@@ -0,0 +1,547 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser',
+        'backform', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
+
+  if (!pgBrowser.Nodes['coll-trigger']) {
+    var triggers = pgAdmin.Browser.Nodes['coll-trigger'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'trigger',
+        label: '{{ _('Triggers') }}',
+        type: 'coll-trigger',
+        columns: ['name', 'description']
+      });
+  };
+
+  if (!pgBrowser.Nodes['trigger']) {
+    pgAdmin.Browser.Nodes['trigger'] = pgAdmin.Browser.Node.extend({
+      parent_type: ['table', 'view'],
+      collection_type: ['coll-table', 'coll-view'],
+      type: 'trigger',
+      label: '{{ _('Trigger') }}',
+      hasSQL:  true,
+      hasDepends: true,
+      sqlAlterHelp: 'sql-altertrigger.html',
+      sqlCreateHelp: 'sql-createtrigger.html',
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_trigger_on_coll', node: 'coll-trigger', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Trigger...') }}',
+          icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_trigger', node: 'trigger', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Trigger...') }}',
+          icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_trigger_onTable', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Trigger...') }}',
+          icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'enable_trigger', node: 'trigger', module: this,
+          applies: ['object', 'context'], callback: 'enable_trigger',
+          category: 'connect', priority: 3, label: '{{ _('Enable trigger') }}',
+          icon: 'fa fa-check', enable : 'canCreate_with_trigger_enable'
+        },{
+          name: 'disable_trigger', node: 'trigger', module: this,
+          applies: ['object', 'context'], callback: 'disable_trigger',
+          category: 'drop', priority: 3, label: '{{ _('Disable trigger') }}',
+          icon: 'fa fa-times', enable : 'canCreate_with_trigger_disable'
+        },{
+          name: 'create_trigger_onView', node: 'view', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Trigger...') }}',
+          icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }
+        ]);
+      },
+      callbacks: {
+        /* Enable trigger */
+        enable_trigger: function(args) {
+          var input = args || {};
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+          if (!d)
+            return false;
+
+          var data = d;
+          $.ajax({
+            url: obj.generate_url(i, 'enable' , d, true),
+            type:'PUT',
+            data: {'enable' : true},
+            dataType: "json",
+            success: function(res) {
+              if (res.success == 1) {
+                alertify.success("{{ _('" + res.info + "') }}");
+                t.removeIcon(i);
+                data.icon = 'icon-trigger';
+                t.addIcon(i, {icon: data.icon});
+                t.unload(i);
+                t.setInode(i);
+                t.deselect(i);
+                // Fetch updated data from server
+                setTimeout(function() {
+                  t.select(i);
+                }, 10);
+              }
+            },
+            error: function(xhr, status, error) {
+              try {
+                var err = $.parseJSON(xhr.responseText);
+                if (err.success == 0) {
+                  msg = S('{{ _(' + err.errormsg + ')}}').value();
+                  alertify.error("{{ _('" + err.errormsg + "') }}");
+                }
+              } catch (e) {}
+              t.unload(i);
+            }
+          })
+        },
+        /* Disable trigger */
+        disable_trigger: function(args) {
+          var input = args || {};
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+          if (!d)
+            return false;
+
+          var data = d;
+          $.ajax({
+            url: obj.generate_url(i, 'enable' , d, true),
+            type:'PUT',
+            data: {'enable' : false},
+            dataType: "json",
+            success: function(res) {
+              if (res.success == 1) {
+                alertify.success("{{ _('" + res.info + "') }}");
+                t.removeIcon(i);
+                data.icon = 'icon-trigger-bad';
+                t.addIcon(i, {icon: data.icon});
+                t.unload(i);
+                t.setInode(i);
+                t.deselect(i);
+                // Fetch updated data from server
+                setTimeout(function() {
+                  t.select(i);
+                }, 10);
+              }
+            },
+            error: function(xhr, status, error) {
+              try {
+                var err = $.parseJSON(xhr.responseText);
+                if (err.success == 0) {
+                  msg = S('{{ _(' + err.errormsg + ')}}').value();
+                  alertify.error("{{ _('" + err.errormsg + "') }}");
+                }
+              } catch (e) {}
+              t.unload(i);
+            }
+          })
+        }
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+					is_row_trigger: true
+        },
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', cell: 'string',
+          type: 'text', disabled: 'inSchema'
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'int', disabled: true, mode: ['properties']
+        },{
+          id: 'is_enable_trigger', label:'{{ _('Enable trigger?') }}',
+          type: 'switch', disabled: 'inSchema', mode: ['properties']
+        },{
+          id: 'is_row_trigger', label:'{{ _('Row trigger') }}',
+          type: 'switch', group: '{{ _('Definition') }}',
+          mode: ['create','edit', 'properties'],
+          deps: ['is_constraint_trigger'],
+          disabled: function(m) {
+            // If contraint trigger is set to True then row trigger will
+            // automatically set to True and becomes disable
+            var is_constraint_trigger = m.get('is_constraint_trigger');
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                if(!_.isUndefined(is_constraint_trigger) &&
+                is_constraint_trigger === true) {
+                // change it's model value
+                    setTimeout(function() { m.set('is_row_trigger', true) }, 10);
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                // Disbale it
+                return true;
+            }
+          }
+        },{
+          id: 'is_constraint_trigger', label:'{{ _('Constraint trigger') }}',
+          type: 'switch', disabled: 'inSchemaWithModelCheck',
+          mode: ['create','edit', 'properties'],
+          group: '{{ _('Definition') }}'
+        },{
+          id: 'tgdeferrable', label:'{{ _('Deferrable') }}',
+          type: 'switch', group: '{{ _('Definition') }}',
+          mode: ['create','edit', 'properties'],
+          deps: ['is_constraint_trigger'],
+          disabled: function(m) {
+            // If contraint trigger is set to True then only enable it
+            var is_constraint_trigger = m.get('is_constraint_trigger');
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                if(!_.isUndefined(is_constraint_trigger) &&
+                is_constraint_trigger === true) {
+                    return false;
+                } else {
+                    setTimeout(function() { m.set('tgdeferrable', false) }, 10);
+                    return true;
+                }
+            } else {
+                // Disbale it
+                return true;
+            }
+          }
+        },{
+          id: 'tginitdeferred', label:'{{ _('Deferred') }}',
+          type: 'switch', group: '{{ _('Definition') }}',
+          mode: ['create','edit', 'properties'],
+          deps: ['tgdeferrable'],
+          disabled: function(m) {
+            // If contraint trigger is set to True then only enable it
+            var is_constraint_trigger = m.get('tgdeferrable');
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                if(!_.isUndefined(is_constraint_trigger) &&
+                is_constraint_trigger === true) {
+                    return false;
+                } else {
+                    setTimeout(function() { m.set('tginitdeferred', false) }, 10);
+                    return true;
+                }
+            } else {
+                // Disbale it
+                return true;
+            }
+          }
+        },{
+          id: 'tfunction', label:'{{ _('Trigger Function') }}',
+          type: 'text', disabled: 'inSchemaWithModelCheck',
+          mode: ['create','edit', 'properties'], group: '{{ _('Definition') }}',
+          control: 'node-ajax-options', url: 'get_triggerfunctions'
+        },{
+          id: 'tgargs', label:'{{ _('Arguments') }}', cell: 'string',
+          group: '{{ _('Definition') }}',
+          type: 'text',mode: ['create','edit', 'properties'], deps: ['tfunction'],
+          disabled: function(m) {
+            // We will disable it when EDB PPAS and trigger function is
+            // set to Inline EDB-SPL
+            var tfunction = m.get('tfunction'),
+                server_type = m.node_info['server']['server_type'];
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                if(server_type === 'ppas' &&
+                    !_.isUndefined(tfunction) &&
+                tfunction === 'Inline EDB-SPL') {
+                    // Disbale and clear its value
+                    m.set('tgargs', undefined)
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                // Disbale it
+                return true;
+            }
+          }
+        },{
+        id: 'fires', label:'{{ _('Fires') }}', deps: ['is_constraint_trigger'],
+        mode: ['create','edit', 'properties'], group: '{{ _('Definition') }}',
+        options: function(control) {
+            var table_options = [
+                {label: "BEFORE", value: "BEFORE"},
+                {label: "AFTER", value: "AFTER"}],
+                view_options = [
+                {label: "BEFORE", value: "BEFORE"},
+                {label: "AFTER", value: "AFTER"},
+                {label: "INSTEAD OF", value: "INSTEAD OF"}];
+            // If we are under table then show table specific options
+            if(_.indexOf(Object.keys(control.model.node_info), 'table') != -1) {
+                return table_options;
+            } else {
+                return view_options;
+            }
+        },
+        // If create mode then by default open composite type
+        control: Backform.Select2Control.extend({
+            render: function(){
+                // Initialize parent's render method
+                Backform.Select2Control.prototype.render.apply(this, arguments);
+                if(this.model.isNew() &&
+                        this.model.get('is_constraint_trigger') !== true ) {
+                    this.model.set({'fires': 'BEFORE'}, {silent: true});
+                }
+                return this;
+            }
+        }),
+        select2: { allowClear: false, width: "100%" },
+        disabled: function(m) {
+        // If contraint trigger is set to True then only enable it
+        var is_constraint_trigger = m.get('is_constraint_trigger');
+        if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+            if(!_.isUndefined(is_constraint_trigger) &&
+            is_constraint_trigger === true) {
+                setTimeout(function() { m.set('fires', 'AFTER', {silent: true}) }, 10);
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            // Disbale it
+            return true;
+        }
+       }
+      },{
+        type: 'nested', control: 'fieldset', mode: ['create','edit', 'properties'],
+        label: '{{ _('Events') }}', group: '{{ _('Definition') }}',
+        schema:[{
+            id: 'evnt_insert', label:'{{ _('INSERT') }}',
+            type: 'switch', mode: ['create','edit', 'properties'],
+            group: '{{ _('Events') }}',
+            disabled: function(m) {
+                return m.inSchemaWithModelCheck.apply(this, [m]);
+            }
+        },{
+            id: 'evnt_update', label:'{{ _('UPDATE') }}',
+            type: 'switch', mode: ['create','edit', 'properties'],
+            group: '{{ _('Events') }}',
+            disabled: function(m) {
+                return m.inSchemaWithModelCheck.apply(this, [m]);
+            }
+        },{
+            id: 'evnt_delete', label:'{{ _('DELETE') }}',
+            type: 'switch', mode: ['create','edit', 'properties'],
+            group: '{{ _('Events') }}',
+            disabled: function(m) {
+                return m.inSchemaWithModelCheck.apply(this, [m]);
+            }
+        },{
+            id: 'evnt_turncate', label:'{{ _('TRUNCATE') }}',
+            type: 'switch', group: '{{ _('Events') }}',
+            disabled: function(m) {
+            var is_constraint_trigger = m.get('is_constraint_trigger'),
+                is_row_trigger = m.get('is_row_trigger'),
+                server_type = m.node_info['server']['server_type'];
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                // We will enabale truncate only for EDB PPAS
+                // and both triggers row & constarint are set to false
+                if(server_type === 'ppas' &&
+                    !_.isUndefined(is_constraint_trigger) &&
+                    !_.isUndefined(is_row_trigger) &&
+                is_constraint_trigger === false &&
+                    is_row_trigger === false) {
+                    return false;
+                } else {
+                    return true;
+                }
+            } else {
+                // Disbale it
+                return true;
+            }
+        }
+        }]
+        },{
+            id: 'whenclause', label:'{{ _('When') }}',
+            type: 'text', disabled: 'inSchemaWithModelCheck',
+            mode: ['create', 'edit', 'properties'],
+            control: 'sql-field', visible: true, group: '{{ _('Definition') }}'
+        },{
+            id: 'columns', label: '{{ _('Columns') }}', url: 'nodes',
+            type: 'collection', control: 'multi-select-ajax',
+            deps: ['evnt_update'], node: 'column', group: '{{ _('Definition') }}',
+            model: pgBrowser.Node.Model.extend({
+                keys: ['column'], defaults: { column: undefined }
+            }),
+            disabled: function(m) {
+                if(this.node_info &&  'catalog' in this.node_info) {
+                    return true;
+                }
+                //Disbale in edit mode
+                if (!m.isNew()) {
+                    return true;
+                }
+                // Enable column only if update event is set true
+                var isUpdate = m.get('evnt_update');
+                if(!_.isUndefined(isUpdate) && isUpdate) {
+                    return false;
+                }
+             return true;
+            }
+        },{
+            id: 'code', label:'{{ _('Code') }}', group: '{{ _('Code') }}',
+            type: 'text', mode: ['create', 'edit'], deps: ['tfunction'],
+            control: 'sql-field', visible: true,
+            disabled: function(m) {
+                // We will enable it only when EDB PPAS and trigger function is
+                // set to Inline EDB-SPL
+                var tfunction = m.get('tfunction'),
+                    server_type = m.node_info['server']['server_type'];
+                if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                    if(server_type === 'ppas' &&
+                        !_.isUndefined(tfunction) &&
+                    tfunction === 'Inline EDB-SPL') {
+                        return false;
+                        // Also clear and disable Argument field
+                    } else {
+                        return true;
+                    }
+                } else {
+                    // Disbale it
+                      return true;
+                }
+            }
+        },{
+          id: 'is_sys_trigger', label:'{{ _('System trigger?') }}', cell: 'string',
+          type: 'switch', disabled: 'inSchemaWithModelCheck', mode: ['properties']
+        },{
+          id: 'is_constarint', label:'{{ _('Constraint?') }}', cell: 'string',
+          type: 'switch', disabled: 'inSchemaWithModelCheck', mode: ['properties']
+        },{
+          id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema'
+    }],
+        validate: function() {
+          var err = {},
+              msg = undefined;
+          this.errorModel.clear();
+
+          if(_.isUndefined(this.get('name'))
+              || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Name can not be empty.') }}';
+            this.errorModel.set('name', msg);
+            return msg;
+            }
+          if(_.isUndefined(this.get('tfunction'))
+              || String(this.get('tfunction')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Trigger function can not be empty.') }}';
+            this.errorModel.set('tfunction', msg);
+            return msg;
+          }
+
+          if(!this.get('evnt_turncate') && !this.get('evnt_delete') && !this.get('evnt_update') && !this.get('evnt_insert')) {
+          msg = '{{ _('Specify atleast one event.') }}';
+          this.errorModel.set('evnt_turncate', " ");
+          this.errorModel.set('evnt_delete', " ");
+          this.errorModel.set('evnt_update', " ");
+          this.errorModel.set('evnt_insert', msg);
+          return msg;
+          }
+          return null;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchema: function() {
+          if(this.node_info &&  'catalog' in this.node_info) {
+            return true;
+          }
+          return false;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchemaWithModelCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info) {
+            // We will disable control if it's in 'edit' mode
+            if (m.isNew()) {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          return true;
+        },
+        // Checks weather to enable/disable control
+        inSchemaWithColumnCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info) {
+            // We will disable control if it's system columns
+            // ie: it's position is less then 1
+            if (m.isNew()) {
+              return false;
+            } else {
+              // if we are in edit mode
+              if (!_.isUndefined(m.get('attnum')) && m.get('attnum') >= 1 ) {
+                return false;
+              } else {
+                return true;
+              }
+           }
+          }
+          return true;
+        }
+      }),
+      // Below function will enable right click menu for creating column
+      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, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to c reate table
+            if (_.indexOf(['schema'], d._type) > -1)
+              return true;
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1) {
+            return false;
+          } else {
+            return true;
+          }
+      },
+      // Check to whether trigger is disable ?
+      canCreate_with_trigger_enable: function(itemData, item, data) {
+        if(this.canCreate.apply(this, [itemData, item, data])) {
+          // We are here means we can create menu, now let's check condition
+          if(itemData.icon === 'icon-trigger-bad') {
+            return true;
+          } else {
+            return false;
+          }
+        }
+      },
+      // Check to whether trigger is enable ?
+      canCreate_with_trigger_disable: function(itemData, item, data) {
+        if(this.canCreate.apply(this, [itemData, item, data])) {
+          // We are here means we can create menu, now let's check condition
+          if(itemData.icon === 'icon-trigger') {
+            return true;
+          } else {
+            return false;
+          }
+        }
+      }
+  });
+ }
+
+  return pgBrowser.Nodes['trigger'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/datatype/sql/9.1_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/datatype/sql/9.1_plus/get_types.sql
index b8952d2..f465a46 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/datatype/sql/9.1_plus/get_types.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/datatype/sql/9.1_plus/get_types.sql
@@ -5,7 +5,8 @@ FROM
         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
+        (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup,
+        CASE WHEN t.typcollation != 0 THEN TRUE ELSE FALSE END AS is_collatable
     FROM
         pg_type t
     JOIN
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/macros/schemas/privilege.macros b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/macros/schemas/privilege.macros
index 183ec2f..dc18a31 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/macros/schemas/privilege.macros
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/macros/schemas/privilege.macros
@@ -6,6 +6,10 @@
 GRANT {{ privs|join(', ') }} ON {{ type }} {{ conn|qtIdent(schema, param) }} TO {{ conn|qtIdent(role) }};
 {% endif %}
 {% if with_grant_privs %}
+{% if privs %}
+{# This empty if is to add new line in between #}
+
+{% endif %}
 GRANT {{ with_grant_privs|join(', ') }} ON {{ type }} {{ conn|qtIdent(schema, param) }} TO {{ conn|qtIdent(role) }} WITH GRANT OPTION;
 {% endif %}
 {%- endmacro %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js
index dc393d5..210f3e6 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js
@@ -4,6 +4,256 @@ define(
         'pgadmin.browser.collection',
         'pgadmin.browser.server.privilege'],
 function($, _, S, pgAdmin, pgBrowser, Backform, alertify) {
+
+
+    // VaccumSettings Collection to display all settings parameters as Grid
+    var VacuumCollectionControl = Backform.VacuumCollectionControl =
+      Backform.Control.extend({
+
+      grid_columns:undefined,
+
+      initialize: function() {
+        Backform.Control.prototype.initialize.apply(this, arguments);
+        var self = this,
+            m = this.model;
+            url = self.field.get('url');
+
+        if (url && m.isNew()) {
+          var node = self.field.get('node'),
+              node_data = self.field.get('node_data'),
+              node_info = self.field.get('node_info'),
+              full_url = node.generate_url.apply(
+                node, [
+                  null, url, node_data, false, node_info
+                ]),
+              data;
+          m.trigger('pgadmin-view:fetching', m, self.field);
+
+          // fetch default values for autovacuum fields
+          $.ajax({
+            async: false,
+            url: full_url,
+            success: function (res) {
+              data = res;
+            },
+            error: function() {
+              m.trigger('pgadmin-view:fetch:error', m, self.field);
+            }
+          });
+          m.trigger('pgadmin-view:fetched', m, self.field);
+
+          // Add fetched models into collection
+          if (data && _.isArray(data)) {
+            m.get(self.field.get('name')).reset(data, {silent: true});
+          }
+        }
+      },
+
+      render: function() {
+        var self = this,
+            m = this.model,
+            attributes = self.field.attributes;
+
+        // remove grid
+        if(self.grid) {
+          self.grid.remove();
+          delete self.grid;
+          self.grid = undefined;
+        }
+
+        self.$el.empty();
+
+        var gridHeader = _.template([
+            '<div class="subnode-header">',
+            '  <label class="control-label col-sm-4"><%-label%></label>',
+            '</div>'].join("\n")),
+            gridBody = $('<div class="pgadmin-control-group backgrid form-group col-xs-12 object subnode"></div>').append(
+                gridHeader(attributes)
+                );
+
+        // Initialize a new Grid instance
+        var grid = self.grid = new Backgrid.Grid({
+          columns: self.grid_columns,
+          collection: self.model.get(self.field.get('name')),
+          className: "backgrid table-bordered"
+        });
+
+        // render grid
+        self.$el.append($(gridBody).append(grid.render().$el));
+
+        return self;
+      }
+    });
+
+  // We will use this function in VacuumSettings Control
+  // to convert data type on the fly
+  var cellFunction = Backform.cellFunction = function(model) {
+    var self = this,
+        m = model,
+        vartype = model.get('column_type');
+
+    switch(vartype) {
+      case "integer":
+        return Backgrid.IntegerCell;
+      break;
+      case "number":
+        return Backgrid.NumberCell;
+      break;
+      case "string":
+        return Backgrid.StringCell;
+      break;
+      default:
+        return Backgrid.Cell;
+      break;
+    }
+  };
+
+  // Define Security Model with fields and validation for VacuumSettings Control
+  var VacuumTableModel =  Backform.VacuumTableModel = pgAdmin.Browser.Node.Model.extend({
+    defaults: {
+      name: undefined,
+      setting: undefined,
+      label:undefined,
+      value: undefined,
+      column_type: undefined
+    },
+
+    toJSON: function(){
+      var d = pgAdmin.Browser.Node.Model.prototype.toJSON.apply(this);
+      delete d.label;
+      delete d.setting;
+      delete d.column_type;
+      return d;
+    }
+  });
+
+   // Extend the browser's collection class for VacuumSettingsModel
+    var VacuumSettingsSchema = Backform.VacuumSettingsSchema =
+     [{
+        id: 'autovacuum_custom', label: '{{ _("Custom auto-vacuum?") }}',
+        group: '{{ _("Table") }}', mode: ['edit', 'create'],
+        type: 'switch',
+        disabled: function(m) {
+          if(!m.top.inSchema.apply(this, [m])) {
+            return false;
+          }
+          return true;
+        }
+      },{
+        id: 'autovacuum_enabled', label: '{{ _("Enabled?") }}',
+        group: '{{ _("Table") }}', mode: ['edit', 'create'],
+        type: 'switch',
+        deps: ['autovacuum_custom'],
+        disabled: function(m) {
+          if(!m.top.inSchema.apply(this, [m]) &&
+            m.get('autovacuum_custom') == true) {
+            return false;
+          }
+
+          // We also need to unset rest of all
+          setTimeout(function() {
+            m.set('autovacuum_enabled', false);
+          }, 10);
+          return true;
+        }
+      },{
+        id: 'vacuum_table', label: '{{ _("Vacuum Table") }}',
+        model: Backform.VacuumTableModel, editable: false, type: 'collection',
+        canEdit: true, group: '{{ _("Table") }}',
+        mode: ['edit', 'create'], url: 'get_table_vacuum',
+        control: Backform.VacuumCollectionControl.extend({
+          grid_columns :[
+            {
+              name: 'label', label: '{{ _("Label") }}',
+              headerCell: Backgrid.Extension.CustomHeaderCell,
+              cell: 'string', editable: false, cellHeaderClasses:'width_percent_40'
+            },
+            {
+              name: 'value', label: '{{ _("Value") }}',
+              cellHeaderClasses:'width_percent_30',
+              cellFunction: Backform.cellFunction, editable: function(m) {
+                return m.handler.get('autovacuum_enabled');
+              }, headerCell: Backgrid.Extension.CustomHeaderCell
+            },
+            {
+              name: 'setting', label: '{{ _("Default value") }}',
+              cellHeaderClasses:'width_percent_30',
+              headerCell: Backgrid.Extension.CustomHeaderCell,
+              cellFunction: Backform.cellFunction, editable: false
+            }
+          ]
+        }),
+        deps: ['autovacuum_enabled']
+      },{
+        id: 'toast_autovacuum', label: '{{ _("Custom auto-vaccum?") }}',
+        group: '{{ _("Toast Table") }}', mode: ['edit', 'create'],
+        type: 'switch',
+        disabled: function(m) {
+          // We need to check additional condition to toggle enable/disable
+          // for table auto-vacuum
+          if(!m.top.inSchema.apply(this, [m]) && m.isNew()) {
+            return false;
+          } else if(!m.top.inSchema.apply(this, [m]) &&
+              (m.get('toast_autovacuum_enabled') === true ||
+              m.top.get('hastoasttable') === true)) {
+            return false;
+          }
+          return true;
+        }
+      },{
+        id: 'toast_autovacuum_enabled', label: '{{ _("Enabled?") }}',
+        group: '{{ _("Toast Table") }}', mode: ['edit', 'create'],
+        type: 'switch',
+        deps:['toast_autovacuum'],
+        disabled: function(m) {
+          // If in schema & in create mode then enable it
+          if(!m.top.inSchema.apply(this, [m]) &&
+              m.get('toast_autovacuum') === true) {
+            return false;
+          }
+
+          if (m.isNew() || m.get('hastoasttable')) {
+            // we also need to unset rest of all
+            setTimeout(function() {
+              m.set('toast_autovacuum_enabled', false);
+            }, 10);
+          }
+        return true;
+        }
+      },{
+        id: 'vacuum_toast', label: '{{ _("Vacuum Toast Table") }}',
+        model: Backform.VacuumTableModel, type: 'collection', editable: function(m) {
+          return m.isNew();
+        },
+        canEdit: true, group: '{{ _("Toast Table") }}',
+        mode: ['properties', 'edit', 'create'], url: 'get_toast_table_vacuum',
+        control: Backform.VacuumCollectionControl.extend({
+          grid_columns :[
+            {
+              name: 'label', label: '{{ _("Label") }}',
+              headerCell: Backgrid.Extension.CustomHeaderCell,
+              cell: 'string', editable: false, cellHeaderClasses:'width_percent_40'
+            },
+            {
+              name: 'value', label: '{{ _("Value") }}',
+              cellHeaderClasses:'width_percent_30',
+              headerCell: Backgrid.Extension.CustomHeaderCell,
+              cellFunction: Backform.cellFunction, editable: function(m) {
+                return m.handler.get('toast_autovacuum_enabled');
+              }
+            },
+            {
+              name: 'setting', label: '{{ _("Default value") }}',
+              cellHeaderClasses:'width_percent_30',
+              headerCell: Backgrid.Extension.CustomHeaderCell,
+              cellFunction: Backform.cellFunction, editable: false
+            }
+          ]
+        }),
+        deps: ['toast_autovacuum_enabled']
+      }
+    ];
+
    // Extend the browser's collection class for SecurityLabel control
     var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
     defaults: {
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/vacuum_settings/sql/vacuum_defaults.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/vacuum_settings/sql/vacuum_defaults.sql
new file mode 100644
index 0000000..e60598c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/vacuum_settings/sql/vacuum_defaults.sql
@@ -0,0 +1,2 @@
+{# ============= Fetch list of default values for autovacuum parameters =============== #}
+SELECT name, setting::numeric AS setting FROM pg_settings WHERE name IN({{ columns }}) ORDER BY name
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/vacuum_settings/vacuum_fields.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/vacuum_settings/vacuum_fields.json
new file mode 100644
index 0000000..83571f4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/vacuum_settings/vacuum_fields.json
@@ -0,0 +1,39 @@
+{# ===== Define name, label and column type of vacuum settings ===== #}
+{# Usage:
+
+  'Type":
+  {
+    "key": ["label", "column_type"
+  }
+
+  Where
+    -  "Type" either table/toast
+    -  "key" refers to the name of columns we fetch in properties.sql
+    -  "label" It is the name of properties to display in grid.
+    -  "column_type" Type of column, we have to provide the grid what type of value to rendered
+#}
+{# ===== Define name, label and column type of vacuum settings ===== #}
+{
+  "table":
+  {
+    "autovacuum_vacuum_threshold": ["autovacuum_vacuum_threshold", "VACCUM base threshold", "integer"],
+    "autovacuum_analyze_threshold": ["autovacuum_analyze_threshold", "ANALYZE base threshold", "integer"],
+    "autovacuum_vacuum_scale_factor": ["autovacuum_vacuum_scale_factor", "VACCUM scale factor", "number"],
+    "autovacuum_analyze_scale_factor": ["autovacuum_analyze_scale_factor", "ANALYZE scale factor", "number"],
+    "autovacuum_vacuum_cost_delay": ["autovacuum_vacuum_cost_delay", "VACCUM cost delay", "integer"],
+    "autovacuum_vacuum_cost_limit": ["autovacuum_vacuum_cost_limit", "VACCUM cost limit", "integer"],
+    "autovacuum_freeze_max_age": ["autovacuum_freeze_max_age", "FREEZE maximum age", "integer"],
+    "vacuum_freeze_min_age": ["autovacuum_freeze_min_age", "FREEZE minimum age", "integer"],
+    "vacuum_freeze_table_age": ["autovacuum_freeze_table_age", "FREEZE table age", "integer"]
+  },
+  "toast":
+  {
+    "autovacuum_vacuum_threshold": ["autovacuum_vacuum_threshold", "VACCUM base threshold", "integer"],
+    "autovacuum_vacuum_scale_factor": ["autovacuum_vacuum_scale_factor", "VACCUM scale factor", "number"],
+    "autovacuum_vacuum_cost_delay": ["autovacuum_vacuum_cost_delay", "VACCUM cost delay", "integer"],
+    "autovacuum_vacuum_cost_limit": ["autovacuum_vacuum_cost_limit", "VACCUM cost limit", "integer"],
+    "autovacuum_freeze_max_age": ["autovacuum_freeze_max_age", "FREEZE maximum age", "integer"],
+    "vacuum_freeze_min_age": ["autovacuum_freeze_min_age", "FREEZE minimum age", "integer"],
+    "vacuum_freeze_table_age": ["autovacuum_freeze_table_age", "FREEZE table age", "integer"]
+  }
+}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py
index 29c7936..17a35b6 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py
@@ -134,7 +134,8 @@ class DataTypeReader:
                 res.append({
                     'label': row['typname'], 'value': row['typname'],
                     'typval': typeval, 'precision': precision,
-                    'length': length, 'min_val': min_val, 'max_val': max_val
+                    'length': length, 'min_val': min_val, 'max_val': max_val,
+                    'is_collatable': row['is_collatable']
                 })
 
         except Exception as e:
@@ -338,4 +339,156 @@ def parse_rule_definition(res):
         res_data['condition'] = condition
     except Exception as e:
         return internal_server_error(errormsg=str(e))
-    return res_data
\ No newline at end of file
+    return res_data
+
+
+class VacuumSettings:
+    """
+    VacuumSettings Class.
+
+    This class includes common utilities to fetch and parse
+    vacuum defaults settings.
+
+    Methods:
+    -------
+    * get_vacuum_table_settings(conn):
+      - Returns vacuum table defaults settings.
+
+    * get_vacuum_toast_settings(conn):
+      - Returns vacuum toast defaults settings.
+
+    * parse_vacuum_data(conn, result, type):
+      - Returns result of an associated array
+        of fields name, label, value and column_type.
+        It adds name, label, column_type properties of table/toast
+        vacuum into the array and returns it.
+        args:
+        * conn - It is db connection object
+        * result - Resultset of vacuum data
+        * type - table/toast vacuum type
+
+    """
+    def __init__(self):
+        pass
+
+    def get_vacuum_table_settings(self, conn):
+        """
+        Fetch the default values for autovacuum
+        fields, return an array of
+          - label
+          - name
+          - setting
+        values
+        """
+
+        # returns an array of name & label values
+        vacuum_fields = render_template("vacuum_settings/vacuum_fields.json")
+
+        vacuum_fields = json.loads(vacuum_fields)
+
+        # returns an array of setting & name values
+        vacuum_fields_keys = "'"+"','".join(
+            vacuum_fields['table'].keys())+"'"
+        SQL = render_template('vacuum_settings/sql/vacuum_defaults.sql',
+                              columns=vacuum_fields_keys)
+        status, res = conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        for row in res['rows']:
+            row_name = row['name']
+            row['name'] = vacuum_fields['table'][row_name][0]
+            row['label'] = vacuum_fields['table'][row_name][1]
+            row['column_type'] = vacuum_fields['table'][row_name][2]
+
+        return res
+
+    def get_vacuum_toast_settings(self, conn):
+        """
+        Fetch the default values for autovacuum
+        fields, return an array of
+          - label
+          - name
+          - setting
+        values
+        """
+
+        # returns an array of name & label values
+        vacuum_fields = render_template("vacuum_settings/vacuum_fields.json")
+
+        vacuum_fields = json.loads(vacuum_fields)
+
+        # returns an array of setting & name values
+        vacuum_fields_keys = "'"+"','".join(
+            vacuum_fields['toast'].keys())+"'"
+        SQL = render_template('vacuum_settings/sql/vacuum_defaults.sql',
+                              columns=vacuum_fields_keys)
+        status, res = conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        for row in res['rows']:
+            row_name = row['name']
+            row['name'] = vacuum_fields['toast'][row_name][0]
+            row['label'] = vacuum_fields['toast'][row_name][1]
+            row['column_type'] = vacuum_fields['table'][row_name][2]
+
+        return res
+
+    def parse_vacuum_data(self, conn, result, type):
+        """
+        This function returns result of an associated array
+        of fields name, label, value and column_type.
+        It adds name, label, column_type properties of table/toast
+        vacuum into the array and returns it.
+        args:
+        * conn - It is db connection object
+        * result - Resultset of vacuum data
+        * type - table/toast vacuum type
+        """
+
+        # returns an array of name & label values
+        vacuum_fields = render_template("vacuum_settings/vacuum_fields.json")
+
+        vacuum_fields = json.loads(vacuum_fields)
+
+        # returns an array of setting & name values
+        vacuum_fields_keys = "'"+"','".join(
+            vacuum_fields[type].keys()) + "'"
+        SQL = render_template('vacuum_settings/sql/vacuum_defaults.sql',
+                              columns=vacuum_fields_keys)
+        status, res = conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        if type is 'table':
+            for row in res['rows']:
+                row_name = row['name']
+                row['name'] = vacuum_fields[type][row_name][0]
+                row['label'] = vacuum_fields[type][row_name][1]
+                row['column_type'] = vacuum_fields[type][row_name][2]
+                if result[row['name']] is not None:
+                    if row['column_type'] == 'number':
+                        value = float(result[row['name']])
+                    else:
+                        value = int(result[row['name']])
+                    row['value'] = row['setting'] = value
+
+        elif type is 'toast':
+            for row in res['rows']:
+                row_old_name = row['name']
+                row_name = 'toast_{0}'.format(vacuum_fields[type][row_old_name][0])
+                row['name'] = vacuum_fields[type][row_old_name][0]
+                row['label'] = vacuum_fields[type][row_old_name][1]
+                row['column_type'] = vacuum_fields[type][row_old_name][2]
+                if result[row_name] and result[row_name] is not None:
+                    if row['column_type'] == 'number':
+                        value = float(result[row_name])
+                    else:
+                        value = int(result[row_name])
+                    row['value'] = row['setting'] = value
+
+        return res['rows']
\ No newline at end of file

---

From: Harshal Dhumal <[email-masked]>
Date: Thu, 19 May 2016 22:47:15 +0530

Hi,

For e.g.

-- 
*Harshal Dhumal*
*Software Engineer *

---

From: Akshay Joshi <[email-masked]>
Date: Fri, 20 May 2016 17:27:01 +0530

Thanks - Committed with minor changes.

On Thu, May 19, 2016 at 10:47 PM, Harshal Dhumal <
[email-masked]> wrote:

> In table create mode user adds column definition with name "col1" then
> adds constraint which includes column "col1". Now user changes column name
> to "col2" then constraint will listen to this change and adapt the column
> name from "col1" to "col2" in constraint definition. Also if column "col2"
> is removed then constraint will also remove the column "col2" from it's
> definition.
>
>
> --
> *Harshal Dhumal*
> *Software Engineer *
>
>
>

-- 
*Akshay Joshi*
*Principal Software Engineer *

*Phone: +91 20-3058-9517Mobile: +91 976-788-8246*

---

From: Dave Page <[email-masked]>
Date: Fri, 20 May 2016 14:31:52 -0400

Hi

I just started to take a look at the table dialogue and friends. Here are a
few issues that we need to address - please take care of them:

1) Move columns to their own tab. Vertical scrolling is bad.

2) Similarly, move constraints to their own tab.

3) Ensure all labels only have a capital letter on the first word, except
if following words are keywords or acronyms, e.g.

  With default values?
  Has OIDs?

4) s/System tabel?/System table?

5) Error messages on fields should not be shown unless the field loses
focus and has an error (see Create Table)

6) The sections on the Properties view are not as they should be. As I've
pointed out before, the "General" section should have a limited subset of
information, e.g. name, oid, owner, tablespace, comment and "is system?"
Other properties should be in other appropriate sections.

7) Variables grids should not be on the Security tab (as also mentioned
previously).

8) Field labels that imply a question (e.g. usually those with a Yes/No
switch for input) should end in a ? - e.g. "Deferrable?"

9) On the Trigger dialogue, "Fires" and following controls should move to a
new tab.

10) On the MV dialogue, VACUUM settings should be on their own tab, as on
the Table dialogue.

11) Privileges controls on the Properties lists should be in a "Security"
group, not "General"

I think there are a couple of basic principles to follow here:

- Make properties lists and dialogues consistent with existing ones, from
control grouping right down to spelling and case of labels.

- Dialogs should never need vertical scrolling by default (e.g. for a new
object with no columns/constraints/whatever yet defined). If you need to
scroll, then things should be moved to a new tab, grouped as appropriate.

Thanks.

On Fri, May 20, 2016 at 7:57 AM, Akshay Joshi <[email-masked]
> wrote:

> *Phone: +91 20-3058-9517 <%2B91%2020-3058-9517>Mobile: +91 976-788-8246*
>

-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

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

---

From: Ashesh Vashi <[email-masked]>
Date: Sat, 21 May 2016 08:42:24 +0530

On Sat, May 21, 2016 at 12:01 AM, Dave Page <[email-masked]> wrote:

> Hi
>
> I just started to take a look at the table dialogue and friends. Here are
> a few issues that we need to address - please take care of them:
>
Should the 'inherit from table' part of columns tab?

> 9) On the Trigger dialogue, "Fires" and following controls should move to
> a new tab.
>
> On Fri, May 20, 2016 at 7:57 AM, Akshay Joshi <
> [email-masked]> wrote:
>

---

From: Dave Page <[email-masked]>
Date: Sat, 21 May 2016 10:15:20 +0100

I think that makes sense, yes.

Sent from my iPad

> On 21 May 2016, at 04:12, Ashesh Vashi <[email-masked]> wrote:
> 
> 
>> I just started to take a look at the table dialogue and friends. Here are a few issues that we need to address - please take care of them:
>> 
>> 3) Ensure all labels only have a capital letter on the first word, except if following words are keywords or acronyms, e.g.
>> 
>>   With default values?
>>   Has OIDs?
>>   
>> 5) Error messages on fields should not be shown unless the field loses focus and has an error (see Create Table)
>> 
>> 6) The sections on the Properties view are not as they should be. As I've pointed out before, the "General" section should have a limited subset of information, e.g. name, oid, owner, tablespace, comment and "is system?" Other properties should be in other appropriate sections.
>> 
>> 7) Variables grids should not be on the Security tab (as also mentioned previously).
>> 
>> 8) Field labels that imply a question (e.g. usually those with a Yes/No switch for input) should end in a ? - e.g. "Deferrable?"
>> 
>> 9) On the Trigger dialogue, "Fires" and following controls should move to a new tab.
>> 
>> 10) On the MV dialogue, VACUUM settings should be on their own tab, as on the Table dialogue.
>> 
>> 11) Privileges controls on the Properties lists should be in a "Security" group, not "General"
>> 
>> - Make properties lists and dialogues consistent with existing ones, from control grouping right down to spelling and case of labels.
>> 
>> - Dialogs should never need vertical scrolling by default (e.g. for a new object with no columns/constraints/whatever yet defined). If you need to scroll, then things should be moved to a new tab, grouped as appropriate.
>> 
>> Thanks.
>> 
>>> On Fri, May 20, 2016 at 7:57 AM, Akshay Joshi <[email-masked]> wrote:
>>>> On Thu, May 19, 2016 at 10:47 PM, Harshal Dhumal <[email-masked]> wrote:
>>>> Hi,
>>>> 
>>>> PFA updated patch for table and all it's child nodes (Version 9). This patch does not depend on any of existing table node patch.
>>>> 
>>>> Major change in this patch: Unlike pgAdmin3 now in table create mode any constraint(s) created (but not saved) will listen to table column changes and adapt themselves accordingly.
>>>> 
>>>> For e.g.
>>>> In table create mode user adds column definition with name "col1" then adds constraint which includes column "col1". Now user changes column name to "col2" then constraint will listen to this change and adapt the column name from "col1" to "col2" in constraint definition. Also if column "col2" is removed then constraint will also remove the column "col2" from it's definition.
>>>> 
>>>> 
>>>> -- 
>>>> Harshal Dhumal
>>>> Software Engineer 
>>>> 
>>>> 
>>>> 
>>>> EenterpriseDB
>>>> 
>>>> 
>>>> 
>>>> --
>>> Akshay Joshi
>>> Principal Software Engineer 
>>> 
>>> 
>>> 
>>> Phone: +91 20-3058-9517
>>> Mobile: +91 976-788-8246
>> 
>> 
>> 
>> -- 
>> Dave Page

---

From: Murtuza Zabuawala <[email-masked]>
Date: Mon, 23 May 2016 18:35:59 +0530

Hi,

PFA patch, which will fixes below mentioned issues,

- Fixed all the review comments given by Dave on tables & its child nodes.

*Additional enhancements*
- In Index node, We have updated the way columns were added,
  earlier it was using subnode control now we can insert/update
  values in-place using DepsCell functionality

- In Type node, We have updated the way Composite types were added,
  earlier it was using subnode control now we can insert/update
  values in-place using DepsCell functionality

- In Constraints nodes, Updated error messages handling earlier it was
  throwing error when we open create dialog and no input has been
  provided by user.

*Affected nodes by this patch:*

   1. Table
   2. Column
   3. Check constraint
   4. Exclusion constraint
   5. Foreign key
   6. Primary key
   7. Unique
   8. Index
   9. Trigger
   10. Type
   11. Materialized view

On Sat, May 21, 2016 at 2:45 PM, Dave Page <[email-masked]> wrote:

>>>> Major change in this patch: Unlike pgAdmin3 now in table create mode
>>>> any constraint(s) created (but not saved) will listen to table column
>>>> changes and adapt themselves accordingly.
>>>>
>>>> For e.g.

---

From: Ashesh Vashi <[email-masked]>
Date: Tue, 24 May 2016 23:39:24 +0530

> PFA patch, which will fixes below mentioned issues,
Committed.

> - Fixed all the review comments given by Dave on tables & its child nodes.
Can you please list down, what has not yet been taken care in the review
comments?

> *Affected nodes by this patch:*

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>

>>> I just started to take a look at the table dialogue and friends. Here
>>> are a few issues that we need to address - please take care of them:
>>>
>>> 3) Ensure all labels only have a capital letter on the first word,
>>> except if following words are keywords or acronyms, e.g.
>>>
>>>   With default values?
>>>   Has OIDs?
>>>
>>> 6) The sections on the Properties view are not as they should be. As
>>> I've pointed out before, the "General" section should have a limited subset
>>> of information, e.g. name, oid, owner, tablespace, comment and "is system?"
>>> - Make properties lists and dialogues consistent with existing ones,
>>> from control grouping right down to spelling and case of labels.
>>>
>>> - Dialogs should never need vertical scrolling by default (e.g. for a
>>> new object with no columns/constraints/whatever yet defined). If you need
>>> to scroll, then things should be moved to a new tab, grouped as appropriate.
>>>
>>> Thanks.
>>>

---

From: Thom Brown <[email-masked]>
Date: Wed, 25 May 2016 00:29:24 +0100

>> PFA patch, which will fixes below mentioned issues,

In the Create Table dialog, on the Advanced tab, the "Of type" drop-down
lists tables and composite types, but those are supposed to just be
composite types.

Also, when using OF in CREATE TABLE, LIKE is no longer valid, so LIKE
should be disabled when using OF.  Also, there should probably be a way of
setting options for the columns taken from the composite type.  For example:

CREATE TYPE inventory AS (product_id bigint, product_name text, weight
numeric);

CREATE TABLE stock OF inventory (
    PRIMARY KEY (product_id),
    weight WITH OPTIONS DEFAULT 0
);

There's currently no way of doing this (neither the primary key, nor the
default value for any columns).  It should probably automatically populate
the columns from the composite type on the columns tab.

Also, could the generated SQL have an empty line between each statement?

Thanks

Thom

---

From: Thom Brown <[email-masked]>
Date: Wed, 25 May 2016 02:00:17 +0100

On 25 May 2016 at 00:29, Thom Brown <[email-masked]> wrote:
>> On Mon, May 23, 2016 at 6:35 PM, Murtuza Zabuawala
>> <[email-masked]> wrote:
>>>
>>> Hi,
>>>
> Also, when using OF in CREATE TABLE, LIKE is no longer valid, so LIKE should
> be disabled when using OF.  Also, there should probably be a way of setting
> options for the columns taken from the composite type.  For example:
>

Another thing I've noticed is that, when adding columns, the "Is
primary key?" column is greyed out.  Why not just make those
modifiable in that view rather than having to go into the details for
each column?

When there's something that hasn't been set up correctly, like adding
variables for a column, but not actually selecting a variable, the SQL
pane, correctly, doesn't show an output, but the error message,
"Please provide input for variable." isn't enough to identify what
needs checking.  Could the relevant tab and field/row be
highlighted/coloured in red/bold?  This is more general, so doesn't
just apply to the create table dialog.

Thom

---

From: Thom Brown <[email-masked]>
Date: Wed, 25 May 2016 02:12:29 +0100

> just apply to the create table dialog.

Last issue today:

When using "Of type", it's not valid to allow additional columns to be
added, so those should be prevented.

Thom

---

From: Ashesh Vashi <[email-masked]>
Date: Wed, 25 May 2016 08:53:07 +0530

Hi Thom,

Can you please create RM cases for the same, it will be easy to track those
issues?
We're using "https://redmine.postgresql.org/projects/pgadmin4/" for the
same.

--

Thanks & Regards,

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

On Wed, May 25, 2016 at 6:42 AM, Thom Brown <[email-masked]> wrote:

> should

---

From: Thom Brown <[email-masked]>
Date: Wed, 25 May 2016 16:31:06 +0100

Thanks.  I've raised all of these in tickets 1231 to 1237.

Thom

On 25 May 2016 at 04:23, Ashesh Vashi <[email-masked]> wrote:

> Hi Thom,
>
> Can you please create RM cases for the same, it will be easy to track
> those issues?

---

From: Ashesh Vashi <[email-masked]>
Date: Wed, 25 May 2016 21:34:34 +0530

Thanks.

---

From: Murtuza Zabuawala <[email-masked]>
Date: Thu, 26 May 2016 15:50:09 +0530

Hi,

PFA patch for RM#1231 & RM#1237.

Additionally I have shifted 'Advanced tab' after columns & constraint tab.

@Thom,

Reading "Of type" drop-down, We borrowed sql query from pgadmin3 to
populate "Of type" combo box as given below.
I was not able to find a way to differentiate between table & composite
type populating from below given query,
I'll put in todo list, may be someone from community can help in future.

SELECT t.oid,
  quote_ident(n.nspname)||'.'||quote_ident(t.typname) AS typname
  FROM pg_type t, pg_namespace n
WHERE t.typtype='c' AND t.typnamespace=n.oid
  AND NOT (n.nspname like 'pg_%' OR n.nspname='information_schema')
ORDER BY typname;

Regards,
Murtuza

On 25-May-2016, at 9:34 pm, Ashesh Vashi <[email-masked]>
wrote:

Thanks.

---

From: Thom Brown <[email-masked]>
Date: Thu, 26 May 2016 11:34:26 +0100

>   AND NOT (n.nspname like 'pg_%' OR n.nspname='information_schema')
This should work:

SELECT c.oid,
  quote_ident(n.nspname)||'.'||quote_ident(c.relname) AS typname
  FROM pg_namespace n, pg_class c
WHERE c.relkind = 'c' AND c.relnamespace=n.oid
  AND NOT (n.nspname like 'pg_%' OR n.nspname='information_schema')
ORDER BY typname;

Although, to avoid confusion, the "typname" alias should probably just be
"relname".

Thom

---

From: Murtuza Zabuawala <[email-masked]>
Date: Thu, 26 May 2016 16:41:20 +0530

Hi,

PFA updated patch which will also include new query given by Thom for "Of
type" drop-down.

Regards,
Murtuza

---

From: Ashesh Vashi <[email-masked]>
Date: Sun, 29 May 2016 16:02:30 +0530

Committed - after rebased the patch.

--

Thanks & Regards,

Ashesh Vashi