Add support for INCLUDE columns on indexes and index constraints with PG 11+. Fixes #3462
@ -48,6 +48,8 @@ Use the fields in the *Columns* tab to to specify the column(s) to which the con
|
||||
* Use the *NULLs order* column to specify the placement of NULL values (when sorted). Specify *FIRST* or *LAST*.
|
||||
* Use the drop-down list next to *Operator* to specify a comparison or conditional operator.
|
||||
|
||||
Use *Include columns* field to specify columns for *INCLUDE* clause of the constraint. This option is available in Postgres 11 and later.
|
||||
|
||||
Click the *SQL* tab to continue.
|
||||
|
||||
Your entries in the *Exclusion Constraint* dialog generate a SQL command (see an example below). Use the *SQL* tab for review; revisit or switch tabs to make any changes to the SQL command.
|
||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 69 KiB |
@ -55,6 +55,8 @@ Use the context-sensitive fields in the *Columns* panel to specify which column(
|
||||
|
||||
* Use the drop-down listbox in the *Collation* field to select a collation to use for the index.
|
||||
|
||||
Use *Include columns* field to specify columns for *INCLUDE* clause of the index. This option is available in Postgres 11 and later.
|
||||
|
||||
Click the *SQL* tab to continue.
|
||||
|
||||
Your entries in the *Index* dialog generate a SQL command (see an example below). Use the *SQL* tab for review; revisit or switch tabs to make any changes to the SQL command.
|
||||
|
@ -23,6 +23,7 @@ Click the *Definition* tab to continue.
|
||||
Use the fields in the *Definition* tab to define the primary key constraint:
|
||||
|
||||
* Click inside the *Columns* field and select one or more column names from the drop-down listbox. To delete a selection, click the *x* to the left of the column name. The primary key constraint should be different from any unique constraint defined for the same table; the selected column(s) for the constraints must be distinct.
|
||||
* Use *Include columns* field to specify columns for *INCLUDE* clause of the index. This option is available in Postgres 11 and later.
|
||||
* Select the name of the tablespace in which the primary key constraint will reside from the drop-down listbox in the *Tablespace* field.
|
||||
* Select the name of an index from the drop-down listbox in the *Index* field. This field is optional. Adding a primary key will automatically create a unique B-tree index on the column or group of columns listed in the primary key, and will force the column(s) to be marked NOT NULL.
|
||||
* Use the *Fill Factor* field to specify a fill factor for the table and index. The fill factor for a table is a percentage between 10 and 100. 100 (complete packing) is the default.
|
||||
|
@ -23,6 +23,7 @@ Click the *Definition* tab to continue.
|
||||
Use the fields in the *Definition* tab to define the unique constraint:
|
||||
|
||||
* Click inside the *Columns* field and select one or more column names from the drop-down listbox. To delete a selection, click the *x* to the left of the column name. The unique constraint should be different from the primary key constraint defined for the same table; the selected column(s) for the constraints must be distinct.
|
||||
* Use *Include columns* field to specify columns for *INCLUDE* clause of the constraint. This option is available in Postgres 11 and later.
|
||||
* Select the name of the tablespace in which the unique constraint will reside from the drop-down listbox in the *Tablespace* field.
|
||||
* Select the name of an index from the drop-down listbox in the *Index* field. This field is optional. Adding a unique constraint will automatically create a unique B-tree index on the column or group of columns listed in the constraint, and will force the column(s) to be marked NOT NULL.
|
||||
* Use the *Fill Factor* field to specify a fill factor for the table and index. The fill factor for a table is a percentage between 10 and 100. 100 (complete packing) is the default.
|
||||
|
@ -301,7 +301,7 @@ class ExclusionConstraintView(PGChildNodeView):
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_constraint_cols.sql']),
|
||||
cid=exid,
|
||||
colcnt=result['indnatts'])
|
||||
colcnt=result['col_count'])
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
@ -326,6 +326,18 @@ class ExclusionConstraintView(PGChildNodeView):
|
||||
|
||||
result['columns'] = columns
|
||||
|
||||
# Add Include details of the index supported for PG-11+
|
||||
if self.manager.version >= 110000:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_constraint_include.sql']),
|
||||
cid=exid)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
result['include'] = [col['colname'] for col in res['rows']]
|
||||
|
||||
return ajax_response(
|
||||
response=result,
|
||||
status=200
|
||||
@ -875,7 +887,7 @@ class ExclusionConstraintView(PGChildNodeView):
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_constraint_cols.sql']),
|
||||
cid=exid,
|
||||
colcnt=data['indnatts'])
|
||||
colcnt=data['col_count'])
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
@ -899,6 +911,20 @@ class ExclusionConstraintView(PGChildNodeView):
|
||||
|
||||
data['columns'] = columns
|
||||
|
||||
# Add Include details of the index supported for PG-11+
|
||||
if self.manager.version >= 110000:
|
||||
sql = render_template(
|
||||
"/".join(
|
||||
[self.template_path, 'get_constraint_include.sql']
|
||||
),
|
||||
cid=exid)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
data['include'] = [col['colname'] for col in res['rows']]
|
||||
|
||||
if not data['amname'] or data['amname'] == '':
|
||||
data['amname'] = 'btree'
|
||||
|
||||
|
@ -650,6 +650,7 @@ define('pgadmin.node.exclusion_constraint', [
|
||||
condeferrable: undefined,
|
||||
condeferred: undefined,
|
||||
columns: [],
|
||||
include: [],
|
||||
},
|
||||
|
||||
// Define the schema for the exclusion constraint node
|
||||
@ -891,6 +892,103 @@ define('pgadmin.node.exclusion_constraint', [
|
||||
Backgrid.StringCell.prototype.remove.apply(this, arguments);
|
||||
},
|
||||
}),
|
||||
},{
|
||||
id: 'include', label: gettext('Include columns'),
|
||||
type: 'array', group: gettext('Columns'),
|
||||
editable: false,
|
||||
canDelete: true, canAdd: true, mode: ['properties', 'create', 'edit'],
|
||||
visible: function(m) {
|
||||
/* In table properties, m.node_info is not available */
|
||||
m = m.top;
|
||||
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||
&& !_.isUndefined(m.node_info.server.version) &&
|
||||
m.node_info.server.version >= 110000)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
control: Backform.MultiSelectAjaxControl.extend({
|
||||
defaults: _.extend(
|
||||
{},
|
||||
Backform.NodeListByNameControl.prototype.defaults,
|
||||
{
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: 'style',
|
||||
multiple: true,
|
||||
placeholder: gettext('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);
|
||||
}
|
||||
},
|
||||
resetColOptions: function() {
|
||||
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);
|
||||
},
|
||||
}),
|
||||
deps: ['index'], node: 'column',
|
||||
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;
|
||||
}
|
||||
},
|
||||
}],
|
||||
validate: function() {
|
||||
this.errorModel.clear();
|
||||
|
@ -0,0 +1,8 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
@ -0,0 +1,80 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
|
||||
import utils as tables_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||
database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as exclusion_utils
|
||||
|
||||
|
||||
class ExclusionConstraintAddTestCase(BaseTestGenerator):
|
||||
"""This class will add new exclusion constraint to existing table"""
|
||||
scenarios = [
|
||||
('Add Exclusion Constraint URL',
|
||||
dict(url='/browser/exclusion_constraint/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
schema_info = parent_node_dict["schema"][-1]
|
||||
self.server_id = schema_info["server_id"]
|
||||
self.db_id = schema_info["db_id"]
|
||||
db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
|
||||
self.server_id, self.db_id)
|
||||
if not db_con['data']["connected"]:
|
||||
raise Exception("Could not connect to database to add a table.")
|
||||
self.schema_id = schema_info["schema_id"]
|
||||
self.schema_name = schema_info["schema_name"]
|
||||
schema_response = schema_utils.verify_schemas(self.server,
|
||||
self.db_name,
|
||||
self.schema_name)
|
||||
if not schema_response:
|
||||
raise Exception("Could not find the schema to add a table.")
|
||||
self.table_name = "table_for_exclusion_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.table_id = tables_utils.create_table(self.server, self.db_name,
|
||||
self.schema_name,
|
||||
self.table_name)
|
||||
|
||||
def runTest(self):
|
||||
"""This function will add exclusion constraint to existing table."""
|
||||
self.index_name = "test_index_add_%s" % (str(uuid.uuid4())[1:8])
|
||||
data = {"name": self.index_name,
|
||||
"spcname": "pg_default",
|
||||
"amname": "btree",
|
||||
"columns": [
|
||||
{"column": "id", "sort_order": False, "nulls": False,
|
||||
"operator": "="}],
|
||||
"include": ["name"]
|
||||
}
|
||||
response = self.tester.post(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(self.server_id) + '/' + str(self.db_id) +
|
||||
'/' + str(self.schema_id) + '/' + str(self.table_id) + '/',
|
||||
data=json.dumps(data),
|
||||
content_type='html/json')
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
index_response = exclusion_utils.verify_exclusion_constraint(
|
||||
self.server, self.db_name, self.index_name)
|
||||
|
||||
if not index_response:
|
||||
raise Exception("Could not find the constraint added.")
|
||||
|
||||
def tearDown(self):
|
||||
# Disconnect the database
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
@ -0,0 +1,79 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
|
||||
import utils as tables_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||
database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as exclusion_utils
|
||||
|
||||
|
||||
class ExclusionConstraintDeleteTestCase(BaseTestGenerator):
|
||||
"""This class will delete the existing exclusion constraint of table."""
|
||||
scenarios = [
|
||||
('Delete Exclusion Constraint Node URL',
|
||||
dict(url='/browser/exclusion_constraint/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
schema_info = parent_node_dict["schema"][-1]
|
||||
self.server_id = schema_info["server_id"]
|
||||
self.db_id = schema_info["db_id"]
|
||||
db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
|
||||
self.server_id, self.db_id)
|
||||
if not db_con['data']["connected"]:
|
||||
raise Exception("Could not connect to database to add a table.")
|
||||
self.schema_id = schema_info["schema_id"]
|
||||
self.schema_name = schema_info["schema_name"]
|
||||
schema_response = schema_utils.verify_schemas(self.server,
|
||||
self.db_name,
|
||||
self.schema_name)
|
||||
if not schema_response:
|
||||
raise Exception("Could not find the schema to add a table.")
|
||||
self.table_name = "table_exclusion_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.table_id = tables_utils.create_table(self.server, self.db_name,
|
||||
self.schema_name,
|
||||
self.table_name)
|
||||
self.index_name = "test_exclusion_delete_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.index_id = exclusion_utils.create_exclusion_constraint(
|
||||
self.server, self.db_name, self.schema_name, self.table_name,
|
||||
self.index_name
|
||||
)
|
||||
|
||||
def runTest(self):
|
||||
"""This function will delete exclusion constraint."""
|
||||
index_response = exclusion_utils.verify_exclusion_constraint(
|
||||
self.server, self.db_name, self.index_name)
|
||||
if not index_response:
|
||||
raise Exception("Could not find the constraint to delete.")
|
||||
response = self.tester.delete(self.url + str(utils.SERVER_GROUP) +
|
||||
'/' + str(self.server_id) + '/' +
|
||||
str(self.db_id) + '/' +
|
||||
str(self.schema_id) + '/' +
|
||||
str(self.table_id) + '/' +
|
||||
str(self.index_id),
|
||||
follow_redirects=True)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
index_response = exclusion_utils.verify_exclusion_constraint(
|
||||
self.server, self.db_name, self.index_name)
|
||||
if index_response:
|
||||
raise Exception("Constraint is not deleted.")
|
||||
|
||||
def tearDown(self):
|
||||
# Disconnect the database
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
@ -0,0 +1,69 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
|
||||
import utils as tables_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||
database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as exclusion_utils
|
||||
|
||||
|
||||
class ExclusionGetTestCase(BaseTestGenerator):
|
||||
"""This class will fetch the existing exclusion constraint"""
|
||||
scenarios = [
|
||||
('Fetch Exclusion Constraint',
|
||||
dict(url='/browser/exclusion_constraint/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
schema_info = parent_node_dict["schema"][-1]
|
||||
self.server_id = schema_info["server_id"]
|
||||
self.db_id = schema_info["db_id"]
|
||||
db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
|
||||
self.server_id, self.db_id)
|
||||
if not db_con['data']["connected"]:
|
||||
raise Exception("Could not connect to database to add a table.")
|
||||
self.schema_id = schema_info["schema_id"]
|
||||
self.schema_name = schema_info["schema_name"]
|
||||
schema_response = schema_utils.verify_schemas(self.server,
|
||||
self.db_name,
|
||||
self.schema_name)
|
||||
if not schema_response:
|
||||
raise Exception("Could not find the schema to add a table.")
|
||||
self.table_name = "table_exclusion_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.table_id = tables_utils.create_table(self.server, self.db_name,
|
||||
self.schema_name,
|
||||
self.table_name)
|
||||
self.index_name = "test_exclusion_get_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.index_id = exclusion_utils.create_exclusion_constraint(
|
||||
self.server, self.db_name, self.schema_name, self.table_name,
|
||||
self.index_name
|
||||
)
|
||||
|
||||
def runTest(self):
|
||||
"""This function will fetch the existing exclusion constraint."""
|
||||
response = self.tester.get(
|
||||
"{0}{1}/{2}/{3}/{4}/{5}/{6}".format(self.url, utils.SERVER_GROUP,
|
||||
self.server_id, self.db_id,
|
||||
self.schema_id, self.table_id,
|
||||
self.index_id),
|
||||
follow_redirects=True)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
# Disconnect the database
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
@ -0,0 +1,77 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
|
||||
import utils as tables_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||
database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as exclusion_utils
|
||||
|
||||
|
||||
class IndexesUpdateTestCase(BaseTestGenerator):
|
||||
"""This class will update the existing exclusion constraint."""
|
||||
scenarios = [
|
||||
('Put exclusion constraint URL',
|
||||
dict(url='/browser/exclusion_constraint/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
schema_info = parent_node_dict["schema"][-1]
|
||||
self.server_id = schema_info["server_id"]
|
||||
self.db_id = schema_info["db_id"]
|
||||
db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
|
||||
self.server_id, self.db_id)
|
||||
if not db_con['data']["connected"]:
|
||||
raise Exception("Could not connect to database to add a table.")
|
||||
self.schema_id = schema_info["schema_id"]
|
||||
self.schema_name = schema_info["schema_name"]
|
||||
schema_response = schema_utils.verify_schemas(self.server,
|
||||
self.db_name,
|
||||
self.schema_name)
|
||||
if not schema_response:
|
||||
raise Exception("Could not find the schema to add a table.")
|
||||
self.table_name = "table_column_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.table_id = tables_utils.create_table(self.server, self.db_name,
|
||||
self.schema_name,
|
||||
self.table_name)
|
||||
self.index_name = "test_exclusion_delete_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.index_id = exclusion_utils.create_exclusion_constraint(
|
||||
self.server, self.db_name, self.schema_name, self.table_name,
|
||||
self.index_name
|
||||
)
|
||||
|
||||
def runTest(self):
|
||||
"""This function will update an existing exclusion constraint"""
|
||||
index_response = exclusion_utils.verify_exclusion_constraint(
|
||||
self.server, self.db_name, self.index_name)
|
||||
if not index_response:
|
||||
raise Exception("Could not find the exclusion constraint.")
|
||||
data = {"oid": self.index_id,
|
||||
"comment": "This is test comment for index"}
|
||||
response = self.tester.put(
|
||||
"{0}{1}/{2}/{3}/{4}/{5}/{6}".format(self.url, utils.SERVER_GROUP,
|
||||
self.server_id, self.db_id,
|
||||
self.schema_id, self.table_id,
|
||||
self.index_id),
|
||||
data=json.dumps(data),
|
||||
follow_redirects=True)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
# Disconnect the database
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
@ -0,0 +1,89 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
|
||||
|
||||
def create_exclusion_constraint(server, db_name, schema_name, table_name,
|
||||
key_name):
|
||||
"""
|
||||
This function creates a exclusion constraint under provided table.
|
||||
:param server: server details
|
||||
:type server: dict
|
||||
:param db_name: database name
|
||||
:type db_name: str
|
||||
:param schema_name: schema name
|
||||
:type schema_name: str
|
||||
:param table_name: table name
|
||||
:type table_name: str
|
||||
:param key_name: test name for key
|
||||
:type key_name: str
|
||||
:return oid: key constraint id
|
||||
:rtype: int
|
||||
"""
|
||||
try:
|
||||
connection = utils.get_db_connection(db_name,
|
||||
server['username'],
|
||||
server['db_password'],
|
||||
server['host'],
|
||||
server['port'],
|
||||
server['sslmode'])
|
||||
old_isolation_level = connection.isolation_level
|
||||
connection.set_isolation_level(0)
|
||||
pg_cursor = connection.cursor()
|
||||
query = "ALTER TABLE %s.%s ADD CONSTRAINT %s EXCLUDE USING btree(" \
|
||||
"id ASC NULLS FIRST WITH =)" % \
|
||||
(schema_name, table_name, key_name)
|
||||
pg_cursor.execute(query)
|
||||
connection.set_isolation_level(old_isolation_level)
|
||||
connection.commit()
|
||||
# Get oid of newly added index constraint
|
||||
pg_cursor.execute(
|
||||
"SELECT conindid FROM pg_constraint where conname='%s'" % key_name)
|
||||
index_constraint = pg_cursor.fetchone()
|
||||
connection.close()
|
||||
oid = index_constraint[0]
|
||||
return oid
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def verify_exclusion_constraint(server, db_name, index_name):
|
||||
"""
|
||||
This function verifies index exist or not.
|
||||
:param server: server details
|
||||
:type server: dict
|
||||
:param db_name: database name
|
||||
:type db_name: str
|
||||
:param index_name: index name
|
||||
:type index_name: str
|
||||
:return table: table record from database
|
||||
:rtype: tuple
|
||||
"""
|
||||
try:
|
||||
connection = utils.get_db_connection(db_name,
|
||||
server['username'],
|
||||
server['db_password'],
|
||||
server['host'],
|
||||
server['port'],
|
||||
server['sslmode'])
|
||||
pg_cursor = connection.cursor()
|
||||
pg_cursor.execute("select * from pg_class where relname='%s'" %
|
||||
index_name)
|
||||
index_record = pg_cursor.fetchone()
|
||||
connection.close()
|
||||
return index_record
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
raise
|
@ -1118,7 +1118,7 @@ class ForeignKeyConstraintView(PGChildNodeView):
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_cols.sql']),
|
||||
cid=costrnt['oid'],
|
||||
colcnt=costrnt['indnatts'])
|
||||
colcnt=costrnt['col_count'])
|
||||
status, rest = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
|
@ -263,7 +263,8 @@ class IndexConstraintView(PGChildNodeView):
|
||||
kwargs['sid']
|
||||
)
|
||||
self.conn = self.manager.connection(did=kwargs['did'])
|
||||
self.template_path = 'index_constraint/sql'
|
||||
self.template_path = 'index_constraint/sql/#{0}#'\
|
||||
.format(self.manager.version)
|
||||
|
||||
# We need parent's name eg table name and schema name
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
@ -323,7 +324,7 @@ class IndexConstraintView(PGChildNodeView):
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_constraint_cols.sql']),
|
||||
cid=cid,
|
||||
colcnt=result['indnatts'])
|
||||
colcnt=result['col_count'])
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
@ -335,6 +336,18 @@ class IndexConstraintView(PGChildNodeView):
|
||||
|
||||
result['columns'] = columns
|
||||
|
||||
# Add Include details of the index supported for PG-11+
|
||||
if self.manager.version >= 110000:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_constraint_include.sql']),
|
||||
cid=cid)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
result['include'] = [col['colname'] for col in res['rows']]
|
||||
|
||||
return ajax_response(
|
||||
response=result,
|
||||
status=200
|
||||
@ -384,7 +397,8 @@ class IndexConstraintView(PGChildNodeView):
|
||||
"""
|
||||
self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
|
||||
self.conn = self.manager.connection(did=did)
|
||||
self.template_path = 'index_constraint/sql'
|
||||
self.template_path = 'index_constraint/sql/#{0}#'\
|
||||
.format(self.manager.version)
|
||||
|
||||
# We need parent's name eg table name and schema name
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
@ -505,7 +519,8 @@ class IndexConstraintView(PGChildNodeView):
|
||||
"""
|
||||
self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
|
||||
self.conn = self.manager.connection(did=did)
|
||||
self.template_path = 'index_constraint/sql'
|
||||
self.template_path = 'index_constraint/sql/#{0}#'\
|
||||
.format(self.manager.version)
|
||||
|
||||
# We need parent's name eg table name and schema name
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
@ -935,7 +950,7 @@ class IndexConstraintView(PGChildNodeView):
|
||||
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_constraint_cols.sql']),
|
||||
cid=cid, colcnt=data['indnatts'])
|
||||
cid=cid, colcnt=data['col_count'])
|
||||
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
@ -948,6 +963,18 @@ class IndexConstraintView(PGChildNodeView):
|
||||
|
||||
data['columns'] = columns
|
||||
|
||||
# Add Include details of the index supported for PG-11+
|
||||
if self.manager.version >= 110000:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_constraint_include.sql']),
|
||||
cid=cid)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
data['include'] = [col['colname'] for col in res['rows']]
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'create.sql']),
|
||||
data=data,
|
||||
|
@ -100,6 +100,7 @@ define('pgadmin.node.primary_key', [
|
||||
condeferrable: undefined,
|
||||
condeferred: undefined,
|
||||
columns: [],
|
||||
include: [],
|
||||
},
|
||||
|
||||
// Define the schema for the index constraint node
|
||||
@ -400,6 +401,103 @@ define('pgadmin.node.primary_key', [
|
||||
}
|
||||
},
|
||||
},{
|
||||
id: 'include', label: gettext('Include columns'),
|
||||
type: 'array', group: gettext('Definition'),
|
||||
editable: false,
|
||||
canDelete: true, canAdd: true, mode: ['properties', 'create', 'edit'],
|
||||
visible: function(m) {
|
||||
/* In table properties, m.node_info is not available */
|
||||
m = m.top;
|
||||
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||
&& !_.isUndefined(m.node_info.server.version) &&
|
||||
m.node_info.server.version >= 110000)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
control: Backform.MultiSelectAjaxControl.extend({
|
||||
defaults: _.extend(
|
||||
{},
|
||||
Backform.NodeListByNameControl.prototype.defaults,
|
||||
{
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: 'style',
|
||||
multiple: true,
|
||||
placeholder: gettext('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);
|
||||
}
|
||||
},
|
||||
resetColOptions: function() {
|
||||
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);
|
||||
},
|
||||
}),
|
||||
deps: ['index'], node: 'column',
|
||||
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: gettext('Tablespace'),
|
||||
type: 'text', group: gettext('Definition'),
|
||||
control: 'node-list-by-name', node: 'tablespace',
|
||||
|
@ -86,6 +86,7 @@ define('pgadmin.node.unique_constraint', [
|
||||
condeferrable: undefined,
|
||||
condeferred: undefined,
|
||||
columns: [],
|
||||
include: [],
|
||||
},
|
||||
|
||||
// Define the schema for the index constraint node
|
||||
@ -386,6 +387,103 @@ define('pgadmin.node.unique_constraint', [
|
||||
}
|
||||
},
|
||||
},{
|
||||
id: 'include', label: gettext('Include columns'),
|
||||
type: 'array', group: gettext('Definition'),
|
||||
editable: false,
|
||||
canDelete: true, canAdd: true, mode: ['properties', 'create', 'edit'],
|
||||
visible: function(m) {
|
||||
/* In table properties, m.node_info is not available */
|
||||
m = m.top;
|
||||
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||
&& !_.isUndefined(m.node_info.server.version) &&
|
||||
m.node_info.server.version >= 110000)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
control: Backform.MultiSelectAjaxControl.extend({
|
||||
defaults: _.extend(
|
||||
{},
|
||||
Backform.NodeListByNameControl.prototype.defaults,
|
||||
{
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: 'style',
|
||||
multiple: true,
|
||||
placeholder: gettext('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);
|
||||
}
|
||||
},
|
||||
resetColOptions: function() {
|
||||
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);
|
||||
},
|
||||
}),
|
||||
deps: ['index'], node: 'column',
|
||||
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: gettext('Tablespace'),
|
||||
type: 'text', group: gettext('Definition'),
|
||||
control: 'node-list-by-name', node: 'tablespace',
|
||||
|
@ -527,10 +527,35 @@ class IndexesView(PGChildNodeView):
|
||||
# Push as collection
|
||||
data['columns'] = columns
|
||||
# Push as string
|
||||
data['cols'] = ', '.join(cols)
|
||||
data['columns_csv'] = ', '.join(cols)
|
||||
|
||||
return data
|
||||
|
||||
def _include_details(self, idx, data, mode='properties'):
|
||||
"""
|
||||
This functional will fetch list of include details for index
|
||||
supported with Postgres 11+
|
||||
|
||||
Args:
|
||||
idx: Index OID
|
||||
data: Properties data
|
||||
|
||||
Returns:
|
||||
Updated properties data with include details
|
||||
"""
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'include_details.sql']), idx=idx
|
||||
)
|
||||
status, rset = self.conn.execute_2darray(SQL)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
# Push as collection
|
||||
data['include'] = [col['colname'] for col in rset['rows']]
|
||||
return data
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, scid, tid, idx):
|
||||
"""
|
||||
@ -568,6 +593,10 @@ class IndexesView(PGChildNodeView):
|
||||
# Add column details for current index
|
||||
data = self._column_details(idx, data)
|
||||
|
||||
# Add Include details of the index
|
||||
if self.manager.version >= 110000:
|
||||
data = self._include_details(idx, data)
|
||||
|
||||
return ajax_response(
|
||||
response=data,
|
||||
status=200
|
||||
@ -905,6 +934,10 @@ class IndexesView(PGChildNodeView):
|
||||
# Add column details for current index
|
||||
data = self._column_details(idx, data, 'create')
|
||||
|
||||
# Add Include details of the index
|
||||
if self.manager.version >= 110000:
|
||||
data = self._include_details(idx, data, 'create')
|
||||
|
||||
SQL, name = self.get_sql(did, scid, tid, None, data)
|
||||
if not isinstance(SQL, (str, unicode)):
|
||||
return SQL
|
||||
|
@ -226,6 +226,7 @@ define('pgadmin.node.index', [
|
||||
hasDepends: true,
|
||||
hasStatistics: true,
|
||||
statsPrettifyFields: ['Size', 'Index size'],
|
||||
width: '45%',
|
||||
Init: function() {
|
||||
/* Avoid mulitple registration of menus */
|
||||
if (this.initialized)
|
||||
@ -331,9 +332,47 @@ define('pgadmin.node.index', [
|
||||
},
|
||||
}),
|
||||
},{
|
||||
id: 'cols', label: gettext('Columns'), cell: 'string',
|
||||
id: 'columns_csv', label: gettext('Columns'), cell: 'string',
|
||||
type: 'text', disabled: 'inSchema', mode: ['properties'],
|
||||
group: gettext('Definition'),
|
||||
},{
|
||||
id: 'include', label: gettext('Include columns'),
|
||||
type: 'array', group: gettext('Definition'),
|
||||
editable: false,
|
||||
canDelete: true, canAdd: true, mode: ['properties'],
|
||||
disabled: 'inSchemaWithModelCheck',
|
||||
visible: function(m) {
|
||||
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||
&& !_.isUndefined(m.node_info.server.version) &&
|
||||
m.node_info.server.version >= 110000)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
control: Backform.MultiSelectAjaxControl.extend({
|
||||
defaults: _.extend(
|
||||
{},
|
||||
Backform.NodeListByNameControl.prototype.defaults,
|
||||
{
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: 'style',
|
||||
multiple: true,
|
||||
placeholder: gettext('Select the column(s)'),
|
||||
},
|
||||
}
|
||||
),
|
||||
}),
|
||||
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;
|
||||
},
|
||||
node:'column',
|
||||
},{
|
||||
id: 'fillfactor', label: gettext('Fill factor'), cell: 'string',
|
||||
type: 'int', disabled: 'inSchema', mode: ['create', 'edit', 'properties'],
|
||||
@ -387,6 +426,44 @@ define('pgadmin.node.index', [
|
||||
},
|
||||
control: 'unique-col-collection', uniqueCol : ['colname'],
|
||||
columns: ['colname', 'op_class', 'sort_order', 'nulls', 'collspcname'],
|
||||
},{
|
||||
id: 'include', label: gettext('Include columns'),
|
||||
type: 'array', group: gettext('Definition'),
|
||||
editable: false,
|
||||
canDelete: true, canAdd: true, mode: ['edit', 'create'],
|
||||
disabled: 'inSchemaWithModelCheck',
|
||||
visible: function(m) {
|
||||
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||
&& !_.isUndefined(m.node_info.server.version) &&
|
||||
m.node_info.server.version >= 110000)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
control: Backform.MultiSelectAjaxControl.extend({
|
||||
defaults: _.extend(
|
||||
{},
|
||||
Backform.NodeListByNameControl.prototype.defaults,
|
||||
{
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: 'style',
|
||||
multiple: true,
|
||||
placeholder: gettext('Select the column(s)'),
|
||||
},
|
||||
}
|
||||
),
|
||||
}),
|
||||
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;
|
||||
},
|
||||
node:'column',
|
||||
},{
|
||||
id: 'description', label: gettext('Comment'), cell: 'string',
|
||||
type: 'multiline', mode: ['properties', 'create', 'edit'],
|
||||
|
@ -55,7 +55,9 @@ class IndexesAddTestCase(BaseTestGenerator):
|
||||
"spcname": "pg_default",
|
||||
"amname": "btree",
|
||||
"columns": [
|
||||
{"colname": "id", "sort_order": False, "nulls": False}]}
|
||||
{"colname": "id", "sort_order": False, "nulls": False}],
|
||||
"include": ["name"]
|
||||
}
|
||||
response = self.tester.post(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(self.server_id) + '/' + str(self.db_id) +
|
||||
|
@ -0,0 +1,21 @@
|
||||
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 is defined and col.is_sort_nulls_applicable %}{% if col.order %} ASC{% else %} DESC{% endif %} NULLS{% endif %} {% if col.nulls_order is defined and col.is_sort_nulls_applicable %}{% if col.nulls_order %}FIRST {% else %}LAST {% endif %}{% endif %}WITH {{col.operator}}{% endfor %})
|
||||
{% if data.include|length > 0 %}
|
||||
INCLUDE({% for col in data.include %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(col)}}{% endfor %}){% endif %}
|
||||
{% 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%};
|
||||
{% if data.comment and data.name %}
|
||||
|
||||
COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
|
||||
IS {{ data.comment|qtLiteral }};
|
||||
{% endif %}
|
@ -0,0 +1,16 @@
|
||||
-- pg_get_indexdef did not support INCLUDE columns
|
||||
|
||||
SELECT a.attname as colname
|
||||
FROM (
|
||||
SELECT
|
||||
i.indnkeyatts,
|
||||
i.indrelid,
|
||||
unnest(indkey) AS table_colnum,
|
||||
unnest(ARRAY(SELECT generate_series(1, i.indnatts) AS n)) attnum
|
||||
FROM
|
||||
pg_index i
|
||||
WHERE i.indexrelid = {{cid}}::OID
|
||||
) i JOIN pg_attribute a
|
||||
ON (a.attrelid = i.indrelid AND i.table_colnum = a.attnum)
|
||||
WHERE i.attnum > i.indnkeyatts
|
||||
ORDER BY i.attnum
|
@ -0,0 +1,34 @@
|
||||
SELECT cls.oid,
|
||||
cls.relname as name,
|
||||
indnkeyatts as col_count,
|
||||
amname,
|
||||
CASE WHEN length(spcname) > 0 THEN spcname ELSE
|
||||
(SELECT sp.spcname FROM pg_database dtb
|
||||
JOIN pg_tablespace sp ON dtb.dattablespace=sp.oid
|
||||
WHERE dtb.oid = {{ did }}::oid)
|
||||
END 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
|
@ -1,6 +1,6 @@
|
||||
SELECT cls.oid,
|
||||
cls.relname as name,
|
||||
indnatts,
|
||||
indnatts as col_count,
|
||||
amname,
|
||||
CASE WHEN length(spcname) > 0 THEN spcname ELSE
|
||||
(SELECT sp.spcname FROM pg_database dtb
|
||||
@ -31,4 +31,4 @@ WHERE indrelid = {{tid}}::oid
|
||||
AND cls.oid = {{cid}}::oid
|
||||
{% endif %}
|
||||
AND contype='x'
|
||||
ORDER BY cls.relname
|
||||
ORDER BY cls.relname
|
||||
|
@ -1,4 +1,4 @@
|
||||
SELECT cls.oid, cls.relname as idxname, indnatts
|
||||
SELECT cls.oid, cls.relname as idxname, indnatts as col_count
|
||||
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')
|
||||
@ -34,4 +34,4 @@ SELECT cls.oid, cls.relname as idxname, indnatts
|
||||
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
|
||||
AND conname IS NULL
|
||||
|
@ -0,0 +1,31 @@
|
||||
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.indnkeyatts) 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
|
||||
ORDER BY i.attnum;
|
@ -0,0 +1,25 @@
|
||||
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 %}
|
||||
|
||||
{% if mode == 'create' %}
|
||||
({% 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 data.amname is defined %}{% if c.sort_order is defined and c.is_sort_nulls_applicable %}{% if c.sort_order %} DESC{% else %} ASC{% endif %}{% endif %}{% if c.nulls is defined and c.is_sort_nulls_applicable %} NULLS {% if c.nulls %}
|
||||
FIRST{% else %}LAST{% endif %}{% endif %}{% endif %}{% endfor %})
|
||||
{% if data.include|length > 0 %}
|
||||
INCLUDE({% for col in data.include %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(col)}}{% endfor %})
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{## We will get indented data from postgres for column ##}
|
||||
({% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{{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.include|length > 0 %}
|
||||
INCLUDE({% for col in data.include %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(col)}}{% endfor %})
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if data.fillfactor %}
|
||||
WITH (FILLFACTOR={{data.fillfactor}})
|
||||
{% endif %}{% if data.spcname %}
|
||||
TABLESPACE {{conn|qtIdent(data.spcname)}}{% endif %}{% if data.indconstraint %}
|
||||
WHERE {{data.indconstraint}}
|
||||
{% endif %};
|
@ -0,0 +1,16 @@
|
||||
-- pg_get_indexdef did not support INCLUDE columns
|
||||
|
||||
SELECT a.attname as colname
|
||||
FROM (
|
||||
SELECT
|
||||
i.indnkeyatts,
|
||||
i.indrelid,
|
||||
unnest(indkey) AS table_colnum,
|
||||
unnest(ARRAY(SELECT generate_series(1, i.indnatts) AS n)) attnum
|
||||
FROM
|
||||
pg_index i
|
||||
WHERE i.indexrelid = {{idx}}::OID
|
||||
) i JOIN pg_attribute a
|
||||
ON (a.attrelid = i.indrelid AND i.table_colnum = a.attnum)
|
||||
WHERE i.attnum > i.indnkeyatts
|
||||
ORDER BY i.attnum
|
@ -0,0 +1,20 @@
|
||||
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.include|length > 0 %}
|
||||
INCLUDE({% for col in data.include %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(col)}}{% endfor %}){% endif %}
|
||||
{% 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%};
|
||||
{% if data.comment and data.name %}
|
||||
|
||||
COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
|
||||
IS {{ data.comment|qtLiteral }};
|
||||
{% endif %}
|
@ -0,0 +1,16 @@
|
||||
-- pg_get_indexdef did not support INCLUDE columns
|
||||
|
||||
SELECT a.attname as colname
|
||||
FROM (
|
||||
SELECT
|
||||
i.indnkeyatts,
|
||||
i.indrelid,
|
||||
unnest(indkey) AS table_colnum,
|
||||
unnest(ARRAY(SELECT generate_series(1, i.indnatts) AS n)) attnum
|
||||
FROM
|
||||
pg_index i
|
||||
WHERE i.indexrelid = {{cid}}::OID
|
||||
) i JOIN pg_attribute a
|
||||
ON (a.attrelid = i.indrelid AND i.table_colnum = a.attnum)
|
||||
WHERE i.attnum > i.indnkeyatts
|
||||
ORDER BY i.attnum
|
@ -0,0 +1,33 @@
|
||||
SELECT cls.oid,
|
||||
cls.relname as name,
|
||||
indnkeyatts as col_count,
|
||||
CASE WHEN length(spcname) > 0 THEN spcname ELSE
|
||||
(SELECT sp.spcname FROM pg_database dtb
|
||||
JOIN pg_tablespace sp ON dtb.dattablespace=sp.oid
|
||||
WHERE dtb.oid = {{ did }}::oid)
|
||||
END 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
|
@ -1,6 +1,6 @@
|
||||
SELECT cls.oid,
|
||||
cls.relname as name,
|
||||
indnatts,
|
||||
indnatts as col_count,
|
||||
CASE WHEN length(spcname) > 0 THEN spcname ELSE
|
||||
(SELECT sp.spcname FROM pg_database dtb
|
||||
JOIN pg_tablespace sp ON dtb.dattablespace=sp.oid
|
||||
@ -30,4 +30,4 @@ WHERE indrelid = {{tid}}::oid
|
||||
AND cls.oid = {{cid}}::oid
|
||||
{% endif %}
|
||||
AND contype='{{constraint_type}}'
|
||||
ORDER BY cls.relname
|
||||
ORDER BY cls.relname
|
@ -7,7 +7,10 @@
|
||||
{% 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 %}
|
||||
{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(c.column)}}{% endfor %}){% if data.include|length > 0 %}
|
||||
|
||||
INCLUDE({% for col in data.include %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(col)}}{% endfor %}){% endif %}
|
||||
{% if data.fillfactor %}
|
||||
|
||||
WITH (FILLFACTOR={{data.fillfactor}}){% endif %}
|
||||
{% if data.spcname and data.spcname != "pg_default" %}
|
||||
@ -23,7 +26,10 @@
|
||||
{% 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 %}
|
||||
{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(c.column)}}{% endfor %})
|
||||
{% if data.include|length > 0 %}
|
||||
INCLUDE({% for col in data.include %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(col)}}{% endfor %}){% endif %}
|
||||
{% if data.fillfactor %}
|
||||
|
||||
WITH (FILLFACTOR={{data.fillfactor}}){% endif %}
|
||||
{% if data.spcname and data.spcname != "pg_default" %}
|
||||
@ -78,9 +84,11 @@
|
||||
|
||||
{% 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 is defined and col.is_sort_nulls_applicable %}{% if col.order %} ASC{% else %} DESC{% endif %} NULLS{% endif %} {% if col.nulls_order is defined and col.is_sort_nulls_applicable %}{% if col.nulls_order %}FIRST {% else %}LAST {% endif %}{% endif %}WITH {{col.operator}}{% endfor %}){% if data.fillfactor %}
|
||||
WITH (FILLFACTOR={{data.fillfactor}}){% endif %}{% if data.spcname and data.spcname != "pg_default" %}
|
||||
|
||||
{% endif %}{{ conn|qtIdent(col.column)}}{% if col.oper_class and col.oper_class != '' %} {{col.oper_class}}{% endif%}{% if col.order is defined and col.is_sort_nulls_applicable %}{% if col.order %} ASC{% else %} DESC{% endif %} NULLS{% endif %} {% if col.nulls_order is defined and col.is_sort_nulls_applicable %}{% if col.nulls_order %}FIRST {% else %}LAST {% endif %}{% endif %}WITH {{col.operator}}{% endfor %})
|
||||
{% if data.include|length > 0 %}
|
||||
INCLUDE({% for col in data.include %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(col)}}{% endfor %})
|
||||
{% endif %}{% 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 %}
|
||||
|
||||
@ -99,4 +107,4 @@ COMMENT ON CONSTRAINT {{ conn|qtIdent(d.name) }} ON {{ conn|qtIdent(schema, tabl
|
||||
IS {{ d.comment|qtLiteral }};
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{%- endmacro %}
|
||||
{%- endmacro %}
|
||||
|
@ -127,7 +127,8 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
'exclusion_constraint/sql', server_type, ver)
|
||||
|
||||
# Template for PK & Unique constraint node
|
||||
self.index_constraint_template_path = 'index_constraint/sql'
|
||||
self.index_constraint_template_path = 'index_constraint/sql/#{0}#'\
|
||||
.format(ver)
|
||||
|
||||
# Template for foreign key constraint node
|
||||
self.foreign_key_template_path = compile_template_path(
|
||||
@ -368,7 +369,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
"/".join([self.index_constraint_template_path,
|
||||
'get_constraint_cols.sql']),
|
||||
cid=row['oid'],
|
||||
colcnt=row['indnatts'])
|
||||
colcnt=row['col_count'])
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
@ -380,6 +381,19 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
|
||||
result['columns'] = columns
|
||||
|
||||
# INCLUDE clause in index is supported from PG-11+
|
||||
if self.manager.version >= 110000:
|
||||
sql = render_template(
|
||||
"/".join([self.index_constraint_template_path,
|
||||
'get_constraint_include.sql']),
|
||||
cid=row['oid'])
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
result['include'] = [col['colname'] for col in res['rows']]
|
||||
|
||||
# If not exists then create list and/or append into
|
||||
# existing list [ Adding into main data dict]
|
||||
data.setdefault(index_constraints[ctype], []).append(result)
|
||||
@ -513,7 +527,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
[self.exclusion_constraint_template_path,
|
||||
'get_constraint_cols.sql']),
|
||||
cid=ex['oid'],
|
||||
colcnt=ex['indnatts'])
|
||||
colcnt=ex['col_count'])
|
||||
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
@ -538,6 +552,20 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
})
|
||||
|
||||
ex['columns'] = columns
|
||||
|
||||
# INCLUDE clause in index is supported from PG-11+
|
||||
if self.manager.version >= 110000:
|
||||
sql = render_template(
|
||||
"/".join([self.exclusion_constraint_template_path,
|
||||
'get_constraint_include.sql']),
|
||||
cid=ex['oid'])
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
ex['include'] = [col['colname'] for col in res['rows']]
|
||||
|
||||
# If not exists then create list and/or append into
|
||||
# existing list [ Adding into main data dict]
|
||||
data.setdefault('exclude_constraint', []).append(ex)
|
||||
@ -962,6 +990,18 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
# Push as string
|
||||
data['cols'] = ', '.join(cols)
|
||||
|
||||
if self.manager.version >= 110000:
|
||||
SQL = render_template(
|
||||
"/".join([self.index_template_path,
|
||||
'include_details.sql']),
|
||||
idx=row['oid'])
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
data['include'] = [col['colname'] for col in res['rows']]
|
||||
|
||||
sql_header = u"\n-- Index: {0}\n\n-- ".format(data['name'])
|
||||
|
||||
sql_header += render_template("/".join([self.index_template_path,
|
||||
@ -2389,7 +2429,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
sql = render_template(
|
||||
"/".join([self.foreign_key_template_path, 'get_cols.sql']),
|
||||
cid=costrnt['oid'],
|
||||
colcnt=costrnt['indnatts'])
|
||||
colcnt=costrnt['col_count'])
|
||||
status, rest = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
|
@ -12,6 +12,7 @@ import codecs
|
||||
import os
|
||||
import pickle
|
||||
import random
|
||||
import sys
|
||||
|
||||
import simplejson as json
|
||||
from flask import Response, url_for, render_template, session, request, \
|
||||
@ -50,6 +51,11 @@ try:
|
||||
except ImportError:
|
||||
from urllib.parse import unquote
|
||||
|
||||
if sys.version_info[0:2] <= (2, 7):
|
||||
IS_PY2 = True
|
||||
else:
|
||||
IS_PY2 = False
|
||||
|
||||
|
||||
class SqlEditorModule(PgAdminModule):
|
||||
"""
|
||||
@ -310,8 +316,10 @@ def extract_sql_from_network_parameters(request_data, request_arguments,
|
||||
request_form_data):
|
||||
if request_data:
|
||||
sql_parameters = json.loads(request_data, encoding='utf-8')
|
||||
if type(sql_parameters) is str:
|
||||
return dict(sql=sql_parameters, explain_plan=None)
|
||||
|
||||
if (IS_PY2 and type(sql_parameters) is unicode) \
|
||||
or type(sql_parameters) is str:
|
||||
return dict(sql=str(sql_parameters), explain_plan=None)
|
||||
return sql_parameters
|
||||
else:
|
||||
return request_arguments or request_form_data
|
||||
|