Added support for OWNED BY clause for sequences. Fixes #4211

This commit is contained in:
Akshay Joshi 2021-08-25 18:45:11 +05:30
parent c02705c53c
commit 7ffe71d12f
21 changed files with 398 additions and 8 deletions

View File

@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////
import { getNodeListByName } from '../../../../../../../static/js/node_ajax';
import { getNodeAjaxOptions, getNodeListByName } from '../../../../../../../static/js/node_ajax';
import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui';
import SequenceSchema from './sequence.ui';
@ -89,6 +89,16 @@ define('pgadmin.node.sequence', [
}
return true;
}),
allTables: ()=>getNodeListByName('table', treeNodeInfo, itemNodeData, {includeItemKeys: ['_id']}),
getColumns: (params)=>{
return getNodeAjaxOptions('get_columns', pgBrowser.Nodes['table'], treeNodeInfo, itemNodeData, {urlParams: params, useCache:false}, (rows)=>{
return rows.map((r)=>({
'value': r.name,
'image': 'icon-column',
'label': r.name,
}));
});
}
},
{
seqowner: pgBrowser.serverInfo[treeNodeInfo.server._id].user.name,

View File

@ -12,6 +12,61 @@ import BaseUISchema from 'sources/SchemaView/base_schema.ui';
import SecLabelSchema from '../../../../../static/js/sec_label.ui';
import { emptyValidator, isEmptyString } from '../../../../../../../../static/js/validators';
export class OwnedBySchema extends BaseUISchema {
constructor(allTables, getColumns) {
super({
owned_table: undefined,
owned_column: undefined,
});
this.allTables = allTables;
this.allTablesOptions = [];
this.getColumns = getColumns;
}
getTableOid(tabName) {
// Here we will fetch the table oid from table name
// iterate over list to find table oid
for(const t of this.allTablesOptions) {
if(t.label === tabName) {
return t._id;
}
}
return;
}
get baseFields() {
let obj = this;
return [
{
id: 'owned_table', label: gettext('Table'), type: 'select', editable: false,
options: obj.allTables,
optionsLoaded: (res)=>obj.allTablesOptions=res,
},{
id: 'owned_column', label: gettext('Column'), editable: false, deps: ['owned_table'],
type: (state)=>{
let tid = obj.getTableOid(state.owned_table);
return {
type: 'select',
options: state.owned_table ? ()=>obj.getColumns({tid: tid}) : [],
optionsReloadBasis: state.owned_table,
};
},
}
];
}
validate(state, setError) {
if (!isEmptyString(state.owned_table) && isEmptyString(state.owned_column)) {
setError('owned_column', gettext('Column cannot be empty.'));
return true;
} else {
setError('owned_column', null);
}
}
}
export default class SequenceSchema extends BaseUISchema {
constructor(getPrivilegeRoleSchema, fieldOptions={}, initValues) {
super({
@ -36,8 +91,10 @@ export default class SequenceSchema extends BaseUISchema {
this.fieldOptions = {
role: [],
schema: [],
allTables: [],
...fieldOptions,
};
this.ownedSchemaObj = new OwnedBySchema(this.fieldOptions.allTables, this.fieldOptions.getColumns);
}
get idAttribute() {
@ -91,6 +148,13 @@ export default class SequenceSchema extends BaseUISchema {
}, {
id: 'cycled', label: gettext('Cycled'), type: 'switch',
mode: ['properties', 'create', 'edit'], group: gettext('Definition'),
}, {
type: 'nested-fieldset', label: gettext('Owned By'), group: gettext('Definition'),
schema: this.ownedSchemaObj,
}, {
id: 'owned_by_note', type: 'note', group: gettext('Definition'),
mode: ['create', 'edit'],
text: gettext('The OWNED BY option causes the sequence to be associated with a specific table column, such that if that column (or its whole table) is dropped, the sequence will be automatically dropped as well. The specified table must have the same owner and be in the same schema as the sequence.'),
}, {
id: 'acl', label: gettext('Privileges'), type: 'text',
group: gettext('Security'), mode: ['properties'],

View File

@ -12,5 +12,7 @@ CREATE SEQUENCE IF NOT EXISTS {{ conn|qtIdent(data.schema, data.name) }}{% if da
MAXVALUE {{data.maximum|int}}{% endif %}{% if data.cache is defined and data.cache|int(-1) > -1%}
CACHE {{data.cache|int}}{% endif %};
CACHE {{data.cache|int}}{% endif %}{% if data.owned_table is defined and data.owned_table != None and data.owned_column is defined and data.owned_column != None %}
OWNED BY {{ conn|qtIdent(data.owned_table) }}.{{ conn|qtIdent(data.owned_column) }}{% endif %};

View File

@ -1,17 +1,22 @@
{% if scid %}
SELECT
cl.oid as oid,
relname as name,
cl.relname as name,
nsp.nspname as schema,
pg_catalog.pg_get_userbyid(relowner) AS seqowner,
pg_catalog.pg_get_userbyid(cl.relowner) AS seqowner,
description as comment,
pg_catalog.array_to_string(relacl::text[], ', ') as acl,
(SELECT pg_catalog.array_agg(provider || '=' || label) FROM pg_catalog.pg_seclabels sl1 WHERE sl1.objoid=cl.oid) AS securities
pg_catalog.array_to_string(cl.relacl::text[], ', ') as acl,
(SELECT pg_catalog.array_agg(provider || '=' || label) FROM pg_catalog.pg_seclabels sl1 WHERE sl1.objoid=cl.oid) AS securities,
depcl.relname AS owned_table,
att.attname AS owned_column
FROM pg_catalog.pg_class cl
LEFT OUTER JOIN pg_catalog.pg_namespace nsp ON cl.relnamespace = nsp.oid
LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=cl.oid
AND des.classoid='pg_class'::regclass)
WHERE relkind = 'S' AND relnamespace = {{scid}}::oid
LEFT OUTER JOIN pg_catalog.pg_depend dep ON (dep.objid=cl.oid and deptype = 'a')
LEFT JOIN pg_catalog.pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum
LEFT JOIN pg_catalog.pg_class depcl ON depcl.oid = att.attrelid
WHERE cl.relkind = 'S' AND cl.relnamespace = {{scid}}::oid
{% if seid %}AND cl.oid = {{seid}}::oid {% endif %}
ORDER BY relname
ORDER BY cl.relname
{% endif %}

View File

@ -11,6 +11,13 @@ ALTER SEQUENCE IF EXISTS {{ conn|qtIdent(o_data.schema, data.name) }}
OWNER TO {{ conn|qtIdent(data.seqowner) }};
{% endif %}
{% if (data.owned_table == None or data.owned_table is not defined) and (o_data.owned_table is defined and o_data.owned_table != None) and (data.owned_column == None or data.owned_column is not defined) %}
ALTER SEQUENCE IF EXISTS {{ conn|qtIdent(o_data.schema, data.name) }}
OWNED BY NONE;
{% elif (data.owned_table is defined or data.owned_column is defined) and (data.owned_table != o_data.owned_table or data.owned_column != o_data.owned_column) %}
ALTER SEQUENCE IF EXISTS {{ conn|qtIdent(o_data.schema, data.name) }}
OWNED BY {% if data.owned_table is defined %}{{ conn|qtIdent(data.owned_table) }}{% else %}{{ conn|qtIdent(o_data.owned_table) }}{% endif %}.{% if data.owned_column is defined %}{{ conn|qtIdent(data.owned_column) }}{% else %}{{ conn|qtIdent(o_data.owned_column) }}{% endif %};
{% endif %}
{% if data.current_value is defined %}
{% set seqname = conn|qtIdent(o_data.schema, data.name) %}
SELECT setval({{ seqname|qtLiteral }}, {{ data.current_value }}, true);

View File

@ -0,0 +1,14 @@
-- SEQUENCE: public.Seq1_$%{}[]()&*^!@"'`\/#
-- DROP SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#";
CREATE SEQUENCE IF NOT EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
INCREMENT 5
START 5
MINVALUE 5
MAXVALUE 999
CACHE 1
OWNED BY tableforownedby.col2;
ALTER SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNER TO postgres;

View File

@ -0,0 +1,2 @@
ALTER SEQUENCE IF EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNED BY tableforownedby.col2;

View File

@ -0,0 +1,13 @@
-- SEQUENCE: public.Seq1_$%{}[]()&*^!@"'`\/#
-- DROP SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#";
CREATE SEQUENCE IF NOT EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
INCREMENT 5
START 5
MINVALUE 5
MAXVALUE 900
CACHE 1;
ALTER SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNER TO postgres;

View File

@ -0,0 +1,4 @@
ALTER SEQUENCE IF EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNED BY NONE;
ALTER SEQUENCE IF EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
MAXVALUE 900;

View File

@ -0,0 +1,14 @@
-- SEQUENCE: public.Seq1_$%{}[]()&*^!@"'`\/#
-- DROP SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#";
CREATE SEQUENCE IF NOT EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
INCREMENT 5
START 5
MINVALUE 5
MAXVALUE 999
CACHE 1
OWNED BY tableforownedby.col1;
ALTER SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNER TO postgres;

View File

@ -0,0 +1,10 @@
CREATE SEQUENCE IF NOT EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
INCREMENT 5
START 5
MINVALUE 5
MAXVALUE 999
CACHE 1
OWNED BY tableforownedby.col1;
ALTER SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNER TO postgres;

View File

@ -0,0 +1,13 @@
-- SEQUENCE: public.Seq1_$%{}[]()&*^!@"'`\/#
-- DROP SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#";
CREATE SEQUENCE IF NOT EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
INCREMENT 5
START 5
MINVALUE 5
MAXVALUE 999
CACHE 1;
ALTER SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNER TO postgres;

View File

@ -1,5 +1,33 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Table for Owned By",
"endpoint": "NODE-table.obj",
"sql_endpoint": "NODE-table.sql_id",
"data": {
"name": "tableforownedby",
"columns": [
{
"name": "col1",
"cltype": "integer",
"is_primary_key": true
},
{
"name": "col2",
"cltype": "text"
},
{
"name": "col3",
"cltype": "integer"
}
],
"is_partitioned": false,
"schema": "public",
"spcname": "pg_default"
},
"store_object_id": true
},
{
"type": "create",
"name": "Create Sequence",
@ -212,6 +240,59 @@
"data": {
"name": "Seq1_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create Sequence with Owned By",
"endpoint": "NODE-sequence.obj",
"sql_endpoint": "NODE-sequence.sql_id",
"msql_endpoint": "NODE-sequence.msql",
"data": {
"name": "Seq1_$%{}[]()&*^!@\"'`\\/#",
"seqowner": "postgres",
"schema": "public",
"increment": "5",
"start": "5",
"maximum": "999",
"minimum": "5",
"cache": "1",
"cycled": false,
"owned_table": "tableforownedby",
"owned_column": "col1",
"relacl": [],
"securities": []
},
"expected_sql_file": "create_sequence_ownedby.sql",
"expected_msql_file": "create_sequence_ownedby_msql.sql"
}, {
"type": "alter",
"name": "Alter Sequence owned by column",
"endpoint": "NODE-sequence.obj_id",
"sql_endpoint": "NODE-sequence.sql_id",
"msql_endpoint": "NODE-sequence.msql_id",
"data": {
"owned_column": "col2"
},
"expected_sql_file": "alter_ownedby_column.sql",
"expected_msql_file": "alter_ownedby_column_msql.sql"
}, {
"type": "alter",
"name": "Alter Sequence remove owned by",
"endpoint": "NODE-sequence.obj_id",
"sql_endpoint": "NODE-sequence.sql_id",
"msql_endpoint": "NODE-sequence.msql_id",
"data": {
"maximum": "900"
},
"expected_sql_file": "alter_ownedby_remove.sql",
"expected_msql_file": "alter_ownedby_remove_msql.sql"
}, {
"type": "delete",
"name": "Drop owned by sequence",
"endpoint": "NODE-sequence.delete_id",
"data": {
"name": "Seq1_$%{}[]()&*^!@\"'`\\/#"
}
}
]
}

View File

@ -0,0 +1,14 @@
-- SEQUENCE: public.Seq1_$%{}[]()&*^!@"'`\/#
-- DROP SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#";
CREATE SEQUENCE IF NOT EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
INCREMENT 5
START 5
MINVALUE 5
MAXVALUE 999
CACHE 1
OWNED BY tableforownedby.col2;
ALTER SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNER TO enterprisedb;

View File

@ -0,0 +1,2 @@
ALTER SEQUENCE IF EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNED BY tableforownedby.col2;

View File

@ -0,0 +1,13 @@
-- SEQUENCE: public.Seq1_$%{}[]()&*^!@"'`\/#
-- DROP SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#";
CREATE SEQUENCE IF NOT EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
INCREMENT 5
START 5
MINVALUE 5
MAXVALUE 900
CACHE 1;
ALTER SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNER TO enterprisedb;

View File

@ -0,0 +1,4 @@
ALTER SEQUENCE IF EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNED BY NONE;
ALTER SEQUENCE IF EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
MAXVALUE 900;

View File

@ -0,0 +1,14 @@
-- SEQUENCE: public.Seq1_$%{}[]()&*^!@"'`\/#
-- DROP SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#";
CREATE SEQUENCE IF NOT EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
INCREMENT 5
START 5
MINVALUE 5
MAXVALUE 999
CACHE 1
OWNED BY tableforownedby.col1;
ALTER SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNER TO enterprisedb;

View File

@ -0,0 +1,10 @@
CREATE SEQUENCE IF NOT EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
INCREMENT 5
START 5
MINVALUE 5
MAXVALUE 999
CACHE 1
OWNED BY tableforownedby.col1;
ALTER SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNER TO enterprisedb;

View File

@ -0,0 +1,13 @@
-- SEQUENCE: public.Seq1_$%{}[]()&*^!@"'`\/#
-- DROP SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#";
CREATE SEQUENCE IF NOT EXISTS public."Seq1_$%{}[]()&*^!@""'`\/#"
INCREMENT 5
START 5
MINVALUE 5
MAXVALUE 999
CACHE 1;
ALTER SEQUENCE public."Seq1_$%{}[]()&*^!@""'`\/#"
OWNER TO enterprisedb;

View File

@ -1,5 +1,33 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Table for Owned By",
"endpoint": "NODE-table.obj",
"sql_endpoint": "NODE-table.sql_id",
"data": {
"name": "tableforownedby",
"columns": [
{
"name": "col1",
"cltype": "integer",
"is_primary_key": true
},
{
"name": "col2",
"cltype": "text"
},
{
"name": "col3",
"cltype": "integer"
}
],
"is_partitioned": false,
"schema": "public",
"spcname": "pg_default"
},
"store_object_id": true
},
{
"type": "create",
"name": "Create Sequence",
@ -212,6 +240,59 @@
"data": {
"name": "Seq1_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create Sequence with Owned By",
"endpoint": "NODE-sequence.obj",
"sql_endpoint": "NODE-sequence.sql_id",
"msql_endpoint": "NODE-sequence.msql",
"data": {
"name": "Seq1_$%{}[]()&*^!@\"'`\\/#",
"seqowner": "enterprisedb",
"schema": "public",
"increment": "5",
"start": "5",
"maximum": "999",
"minimum": "5",
"cache": "1",
"cycled": false,
"owned_table": "tableforownedby",
"owned_column": "col1",
"relacl": [],
"securities": []
},
"expected_sql_file": "create_sequence_ownedby.sql",
"expected_msql_file": "create_sequence_ownedby_msql.sql"
}, {
"type": "alter",
"name": "Alter Sequence owned by column",
"endpoint": "NODE-sequence.obj_id",
"sql_endpoint": "NODE-sequence.sql_id",
"msql_endpoint": "NODE-sequence.msql_id",
"data": {
"owned_column": "col2"
},
"expected_sql_file": "alter_ownedby_column.sql",
"expected_msql_file": "alter_ownedby_column_msql.sql"
}, {
"type": "alter",
"name": "Alter Sequence remove owned by",
"endpoint": "NODE-sequence.obj_id",
"sql_endpoint": "NODE-sequence.sql_id",
"msql_endpoint": "NODE-sequence.msql_id",
"data": {
"maximum": "900"
},
"expected_sql_file": "alter_ownedby_remove.sql",
"expected_msql_file": "alter_ownedby_remove_msql.sql"
}, {
"type": "delete",
"name": "Drop owned by sequence",
"endpoint": "NODE-sequence.delete_id",
"data": {
"name": "Seq1_$%{}[]()&*^!@\"'`\\/#"
}
}
]
}