mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-09 23:15:58 -06:00
Added option to create unique constraint with nulls not distinct. #5855
This commit is contained in:
parent
3de2e625b5
commit
89c8a7f907
@ -151,10 +151,7 @@ export default class PrimaryKeySchema extends BaseUISchema {
|
||||
editable: false,
|
||||
canDelete: true, canAdd: true,
|
||||
mode: ['properties', 'create', 'edit'],
|
||||
visible: function() {
|
||||
/* In table properties, nodeInfo is not available */
|
||||
return this.getServerVersion() >= 110000;
|
||||
},
|
||||
min_version: 110000,
|
||||
deps: ['index'],
|
||||
readonly: function(state) {
|
||||
return obj.isReadOnly(state);
|
||||
|
@ -25,6 +25,7 @@ export default class UniqueConstraintSchema extends BaseUISchema {
|
||||
fillfactor: undefined,
|
||||
condeferrable: undefined,
|
||||
condeferred: undefined,
|
||||
indnullsnotdistinct: undefined,
|
||||
columns: [],
|
||||
include: [],
|
||||
});
|
||||
@ -150,12 +151,7 @@ export default class UniqueConstraintSchema extends BaseUISchema {
|
||||
editable: false,
|
||||
canDelete: true, canAdd: true,
|
||||
mode: ['properties', 'create', 'edit'],
|
||||
visible: function() {
|
||||
/* In table properties, nodeInfo is not available */
|
||||
return (!_.isUndefined(this.nodeInfo) && !_.isUndefined(this.nodeInfo.server)
|
||||
&& !_.isUndefined(this.nodeInfo.server.version) &&
|
||||
this.nodeInfo.server.version >= 110000);
|
||||
},
|
||||
min_version: 110000,
|
||||
deps: ['index'],
|
||||
readonly: function(state) {
|
||||
return obj.isReadOnly(state);
|
||||
@ -256,6 +252,13 @@ export default class UniqueConstraintSchema extends BaseUISchema {
|
||||
return {condeferred: false};
|
||||
}
|
||||
}
|
||||
},{
|
||||
id: 'indnullsnotdistinct', label: gettext('NULLs not distinct?'),
|
||||
type: 'switch', group: gettext('Definition'),
|
||||
readonly: function(state) {
|
||||
return obj.isReadOnly(state);
|
||||
},
|
||||
min_version: 150000
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
-- Constraint: UC_$%{}[]()&*^!@"'`\/#
|
||||
|
||||
-- ALTER TABLE IF EXISTS testschema.tablefor_unique_cons DROP CONSTRAINT IF EXISTS "UC_$%{}[]()&*^!@""'`\/#";
|
||||
|
||||
ALTER TABLE IF EXISTS testschema.tablefor_unique_cons
|
||||
ADD CONSTRAINT "UC_$%{}[]()&*^!@""'`\/#" UNIQUE NULLS NOT DISTINCT (col1)
|
||||
INCLUDE (col2)
|
||||
WITH (FILLFACTOR=20)
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
COMMENT ON CONSTRAINT "UC_$%{}[]()&*^!@""'`\/#" ON testschema.tablefor_unique_cons
|
||||
IS 'Comment for create';
|
@ -0,0 +1,8 @@
|
||||
ALTER TABLE IF EXISTS testschema.tablefor_unique_cons
|
||||
ADD CONSTRAINT "UC_$%{}[]()&*^!@""'`\/#" UNIQUE NULLS NOT DISTINCT (col1)
|
||||
INCLUDE (col2)
|
||||
WITH (FILLFACTOR=20)
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
COMMENT ON CONSTRAINT "UC_$%{}[]()&*^!@""'`\/#" ON testschema.tablefor_unique_cons
|
||||
IS 'Comment for create';
|
@ -0,0 +1,77 @@
|
||||
{
|
||||
"scenarios": [
|
||||
{
|
||||
"type": "create",
|
||||
"name": "Create Table",
|
||||
"endpoint": "NODE-table.obj",
|
||||
"sql_endpoint": "NODE-table.sql_id",
|
||||
"data": {
|
||||
"name": "tablefor_unique_cons",
|
||||
"columns": [{
|
||||
"name": "col1",
|
||||
"cltype": "integer",
|
||||
"is_primary_key": false
|
||||
}, {
|
||||
"name": "col2",
|
||||
"cltype": "integer",
|
||||
"is_primary_key": false
|
||||
}],
|
||||
"is_partitioned": false,
|
||||
"schema": "testschema",
|
||||
"spcname": "pg_default"
|
||||
},
|
||||
"store_object_id": true
|
||||
}, {
|
||||
"type": "create",
|
||||
"name": "Create Index",
|
||||
"endpoint": "NODE-index.obj",
|
||||
"sql_endpoint": "NODE-index.sql_id",
|
||||
"data": {
|
||||
"name": "uindex",
|
||||
"spcname": "pg_default",
|
||||
"amname": "btree",
|
||||
"columns": [{
|
||||
"colname": "col1",
|
||||
"sort_order": false,
|
||||
"nulls": false,
|
||||
"is_sort_nulls_applicable": true
|
||||
}],
|
||||
"indisunique": true,
|
||||
"fillfactor": 20
|
||||
}
|
||||
}, {
|
||||
"type": "create",
|
||||
"name": "Create Unique Constraint -- 15 Plus",
|
||||
"endpoint": "NODE-unique_constraint.obj",
|
||||
"sql_endpoint": "NODE-unique_constraint.sql_id",
|
||||
"msql_endpoint": "NODE-unique_constraint.msql",
|
||||
"data": {
|
||||
"name": "UC_$%{}[]()&*^!@\"'`\\/#",
|
||||
"comment": "Comment for create",
|
||||
"fillfactor": 20,
|
||||
"columns": [{"column":"col1"}],
|
||||
"include": ["col2"],
|
||||
"condeferrable": true,
|
||||
"condeferred": true,
|
||||
"indnullsnotdistinct": true
|
||||
},
|
||||
"expected_sql_file": "create_unique_constraint.sql",
|
||||
"expected_msql_file": "create_unique_constraint_msql.sql"
|
||||
}, {
|
||||
"type": "delete",
|
||||
"name": "Drop Unique Constraint -- 15 Plus",
|
||||
"endpoint": "NODE-unique_constraint.delete_id",
|
||||
"data": {
|
||||
"name": "UC_$%{}[]()&*^!@\"'`\\/#a"
|
||||
}
|
||||
}, {
|
||||
"type": "delete",
|
||||
"name": "Drop Unique Constraint Table -- 15 Plus",
|
||||
"endpoint": "NODE-table.delete_id",
|
||||
"data": {
|
||||
"name": "tablefor_unique_cons"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -173,6 +173,28 @@
|
||||
"error_msg": "Mocked Internal Server Error",
|
||||
"test_result_data": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Create: Add unique key constraint to table with nulls not distinct.",
|
||||
"url": "/browser/unique_constraint/obj/",
|
||||
"is_positive_test": true,
|
||||
"inventory_data": {
|
||||
"server_min_version": 150000,
|
||||
"skip_msg": "Nulls not distinct is not supported by PPAS/PG 15.0 and below."
|
||||
},
|
||||
"test_data": {
|
||||
"constraint_name": "test_uniquekey_add_",
|
||||
"spcname": "pg_default",
|
||||
"columns": [{"column": "id"}],
|
||||
"indnullsnotdistinct": true
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"index_constraint_delete": [
|
||||
|
@ -21,6 +21,7 @@ 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 index_constraint_utils
|
||||
from pgadmin.utils import server_utils
|
||||
|
||||
|
||||
class IndexConstraintAddTestCase(BaseTestGenerator):
|
||||
@ -35,10 +36,21 @@ class IndexConstraintAddTestCase(BaseTestGenerator):
|
||||
# Load test data
|
||||
self.data = self.test_data
|
||||
|
||||
# Create db connection
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
# Check server version
|
||||
schema_info = parent_node_dict["schema"][-1]
|
||||
self.server_id = schema_info["server_id"]
|
||||
|
||||
if "server_min_version" in self.inventory_data:
|
||||
server_con = server_utils.connect_server(self, self.server_id)
|
||||
if not server_con["info"] == "Server connected.":
|
||||
raise Exception("Could not connect to server to add "
|
||||
"partitioned table.")
|
||||
if server_con["data"]["version"] < \
|
||||
self.inventory_data["server_min_version"]:
|
||||
self.skipTest(self.inventory_data["skip_msg"])
|
||||
|
||||
# Create db connection
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
self.db_id = schema_info["db_id"]
|
||||
db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
|
||||
self.server_id, self.db_id)
|
||||
|
@ -0,0 +1,20 @@
|
||||
ALTER TABLE IF EXISTS {{ conn|qtIdent(data.schema, data.table) }}
|
||||
ADD{% if data.name %} CONSTRAINT {{ conn|qtIdent(data.name) }}{% endif%} {{constraint_name}}{% if data.indnullsnotdistinct %} NULLS NOT DISTINCT{% endif %} {% 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(conn) }};
|
||||
{% endif %}
|
@ -0,0 +1,32 @@
|
||||
SELECT cls.oid,
|
||||
cls.relname as name,
|
||||
indnkeyatts as col_count,
|
||||
indnullsnotdistinct,
|
||||
CASE WHEN length(spcname::text) > 0 THEN spcname ELSE
|
||||
(SELECT sp.spcname FROM pg_catalog.pg_database dtb
|
||||
JOIN pg_catalog.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,
|
||||
conislocal,
|
||||
substring(pg_catalog.array_to_string(cls.reloptions, ',') from 'fillfactor=([0-9]*)') AS fillfactor
|
||||
FROM pg_catalog.pg_index idx
|
||||
JOIN pg_catalog.pg_class cls ON cls.oid=indexrelid
|
||||
LEFT OUTER JOIN pg_catalog.pg_tablespace ta on ta.oid=cls.reltablespace
|
||||
LEFT JOIN pg_catalog.pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
|
||||
LEFT OUTER JOIN pg_catalog.pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
|
||||
LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=cls.oid AND des.classoid='pg_class'::regclass)
|
||||
LEFT OUTER JOIN pg_catalog.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
|
@ -25,7 +25,7 @@
|
||||
{% 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 data.name %}CONSTRAINT {{conn|qtIdent(data.name)}} {% endif %}UNIQUE {% if data.indnullsnotdistinct %}NULLS NOT DISTINCT {% endif %}({% for c in data.columns%}
|
||||
{% 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 %}
|
||||
|
@ -101,11 +101,7 @@ export default class SubscriptionSchema extends BaseUISchema{
|
||||
return [{
|
||||
id: 'name', label: gettext('Name'), type: 'text',
|
||||
mode: ['properties', 'create', 'edit'], noEmpty: true,
|
||||
visible: function() {
|
||||
return (!_.isUndefined(this.node_info['node_info'])
|
||||
&& !_.isUndefined(this.node_info['node_info'].version)
|
||||
&& this.node_info['node_info'].version >= 100000);
|
||||
},
|
||||
min_version: 100000
|
||||
},{
|
||||
id: 'oid', label: gettext('OID'), cell: 'string', mode: ['properties'],
|
||||
type: 'text',
|
||||
|
@ -149,7 +149,6 @@ export class SaveOptSchema extends BaseUISchema {
|
||||
|
||||
|
||||
get baseFields() {
|
||||
let obj = this;
|
||||
return [{
|
||||
id: 'dns_owner',
|
||||
label: gettext('Owner'),
|
||||
@ -180,11 +179,7 @@ export class SaveOptSchema extends BaseUISchema {
|
||||
type: 'switch',
|
||||
disabled: false,
|
||||
group: gettext('Do not save'),
|
||||
visible: function() {
|
||||
let serverInfo = _.isUndefined(obj.fieldOptions.nodeInfo) ? undefined : obj.fieldOptions.nodeInfo.server;
|
||||
|
||||
return _.isUndefined(serverInfo) ? false : serverInfo.version >= 110000;
|
||||
},
|
||||
min_version: 110000
|
||||
}];
|
||||
}
|
||||
}
|
||||
@ -218,7 +213,6 @@ export class QueryOptionSchema extends BaseUISchema {
|
||||
|
||||
|
||||
get baseFields() {
|
||||
let obj = this;
|
||||
return [{
|
||||
id: 'use_column_inserts',
|
||||
label: gettext('Use Column Inserts'),
|
||||
@ -237,9 +231,7 @@ export class QueryOptionSchema extends BaseUISchema {
|
||||
type: 'switch',
|
||||
disabled: false,
|
||||
group: gettext('Queries'),
|
||||
visible: function() {
|
||||
return !(!_.isUndefined(obj.backupType) && obj.backupType === 'server');
|
||||
},
|
||||
visible: isVisible,
|
||||
}, {
|
||||
id: 'include_drop_database',
|
||||
label: gettext('Include DROP DATABASE statement'),
|
||||
@ -259,14 +251,8 @@ export class QueryOptionSchema extends BaseUISchema {
|
||||
type: 'switch',
|
||||
disabled: false,
|
||||
group: gettext('Queries'),
|
||||
visible: function() {
|
||||
if (!_.isUndefined(obj.backupType) && obj.backupType === 'server')
|
||||
return false;
|
||||
|
||||
let serverInfo = _.isUndefined(obj.fieldOptions.nodeInfo) ? undefined : obj.fieldOptions.nodeInfo.server;
|
||||
|
||||
return _.isUndefined(serverInfo) ? false : serverInfo.version >= 110000;
|
||||
},
|
||||
min_version: 110000,
|
||||
visible: isVisible,
|
||||
}];
|
||||
}
|
||||
}
|
||||
@ -474,13 +460,8 @@ export default class BackupSchema extends BaseUISchema {
|
||||
type: 'select',
|
||||
disabled: false,
|
||||
options: obj.fieldOptions.encoding,
|
||||
visible: function() {
|
||||
if (!_.isUndefined(obj.backupType) && obj.backupType === 'server') {
|
||||
let dbNode = obj.pgBrowser.serverInfo[obj.treeNodeInfo.server._id];
|
||||
return _.isUndefined(dbNode) ? false : dbNode.version >= 110000;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
min_version: 110000,
|
||||
visible: isVisible
|
||||
}, {
|
||||
id: 'no_of_jobs',
|
||||
label: gettext('Number of jobs'),
|
||||
|
@ -151,7 +151,6 @@ export class RestoreSaveOptSchema extends BaseUISchema {
|
||||
|
||||
|
||||
get baseFields() {
|
||||
let obj = this;
|
||||
return [{
|
||||
id: 'dns_owner',
|
||||
label: gettext('Owner'),
|
||||
@ -176,10 +175,7 @@ export class RestoreSaveOptSchema extends BaseUISchema {
|
||||
type: 'switch',
|
||||
disabled: false,
|
||||
group: gettext('Do not save'),
|
||||
visible: function() {
|
||||
let serverInfo = obj.fieldOptions.nodeInfo.server;
|
||||
return !_.isUndefined(serverInfo) && serverInfo.version >= 110000 ? true : false;
|
||||
},
|
||||
min_version: 110000
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user