Implement column node and its features for foreign table. #640, #6373, #6674

This commit is contained in:
Yogesh Mahajan 2023-09-26 15:03:03 +05:30 committed by GitHub
parent 5736e173ea
commit 3cd547f879
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 2037 additions and 159 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -10,11 +10,10 @@
"""Implements the Foreign Table Module.""" """Implements the Foreign Table Module."""
import sys import sys
import traceback
from functools import wraps from functools import wraps
import json import json
from flask import render_template, make_response, request, jsonify, \ from flask import render_template, request, jsonify, \
current_app current_app
from flask_babel import gettext from flask_babel import gettext
@ -29,10 +28,11 @@ from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
from pgadmin.browser.utils import PGChildNodeView from pgadmin.browser.utils import PGChildNodeView
from pgadmin.utils.ajax import make_json_response, internal_server_error, \ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
make_response as ajax_response, gone make_response as ajax_response, gone
from pgadmin.utils.compile_template_name import compile_template_path
from pgadmin.utils.driver import get_driver from pgadmin.utils.driver import get_driver
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
columns import utils as column_utils
class ForeignTableModule(SchemaChildModule): class ForeignTableModule(SchemaChildModule):
@ -88,6 +88,10 @@ class ForeignTableModule(SchemaChildModule):
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\ from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
constraints import blueprint as module constraints import blueprint as module
self.submodules.append(module) self.submodules.append(module)
from pgadmin.browser.server_groups.servers.databases.schemas. \
foreign_tables.foreign_table_columns import \
foreign_table_column_blueprint as module
self.submodules.append(module)
super().register(app, options) super().register(app, options)
@ -216,6 +220,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
'get_foreign_servers': [{'get': 'get_foreign_servers'}, 'get_foreign_servers': [{'get': 'get_foreign_servers'},
{'get': 'get_foreign_servers'}], {'get': 'get_foreign_servers'}],
'get_tables': [{'get': 'get_tables'}, {'get': 'get_tables'}], 'get_tables': [{'get': 'get_tables'}, {'get': 'get_tables'}],
'set_trigger': [{'put': 'enable_disable_triggers'}],
'get_columns': [{'get': 'get_columns'}, {'get': 'get_columns'}], 'get_columns': [{'get': 'get_columns'}, {'get': 'get_columns'}],
'select_sql': [{'get': 'select_sql'}], 'select_sql': [{'get': 'select_sql'}],
'insert_sql': [{'get': 'insert_sql'}], 'insert_sql': [{'get': 'insert_sql'}],
@ -451,7 +456,9 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
scid, scid,
row['name'], row['name'],
icon="icon-foreign_table", icon="icon-foreign_table",
description=row['description'] description=row['description'],
tigger_count=row['triggercount'],
has_enable_triggers=row['has_enable_triggers'],
)) ))
return make_json_response( return make_json_response(
@ -1160,15 +1167,14 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
""" """
cols = [] cols = []
for c in columns: for c in columns:
if len(c) > 0: if len(c) > 0 and 'cltype' in c:
if '[]' in c['datatype']: if '[]' in c['cltype']:
c['datatype'] = c['datatype'].replace('[]', '') c['cltype'] = c['cltype'].replace('[]', '')
c['isArrayType'] = True c['isArrayType'] = True
else: else:
c['isArrayType'] = False c['isArrayType'] = False
cols.append(c) cols.append(c)
return cols if cols else columns
return cols
def _fetch_properties(self, gid, sid, did, scid, foid, inherits=False, ): def _fetch_properties(self, gid, sid, did, scid, foid, inherits=False, ):
""" """
@ -1238,18 +1244,44 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
if not status: if not status:
return False, internal_server_error(errormsg=cols) return False, internal_server_error(errormsg=cols)
self._get_datatype_precision(cols) # Fetch length and precision data
for col in cols['rows']:
column_utils.fetch_length_precision(col)
self._get_edit_types(cols['rows'])
if cols and 'rows' in cols: if cols and 'rows' in cols:
data['columns'] = cols['rows'] data['columns'] = cols['rows']
# Get Inherited table names from their OID # Get Inherited table names from their OID
is_error, errmsg = self._get_inherited_table_name(data, inherits) is_error, errmsg = self._get_inherited_table_name(data, inherits)
if is_error: if is_error:
return False, internal_server_error(errormsg=errmsg) return False, internal_server_error(errormsg=errmsg)
return True, data return True, data
def _get_edit_types(self, cols):
edit_types = {}
for col in cols:
edit_types[col['atttypid']] = []
if len(cols) > 0:
SQL = render_template("/".join([self.template_path,
'edit_mode_types_multi.sql']),
type_ids=",".join(map(lambda x: str(x),
edit_types.keys())))
status, res = self.conn.execute_2darray(SQL)
for row in res['rows']:
edit_types[row['main_oid']] = sorted(row['edit_types'])
for column in cols:
edit_type_list = edit_types[column['atttypid']]
edit_type_list.append(column['fulltype'])
column['edit_types'] = sorted(edit_type_list)
column['cltype'] = \
DataTypeReader.parse_type_name(column['cltype'])
def _get_datatype_precision(self, cols): def _get_datatype_precision(self, cols):
""" """
The Length and the precision of the Datatype should be separated. The Length and the precision of the Datatype should be separated.
@ -1262,11 +1294,11 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
substr = self.extract_type_length_precision(c) substr = self.extract_type_length_precision(c)
typlen = substr.split(",") typlen = substr.split(",")
if len(typlen) > 1: if len(typlen) > 1:
c['typlen'] = self.convert_typlen_to_int(typlen) c['attlen'] = self.convert_typlen_to_int(typlen)
c['precision'] = self.convert_precision_to_int(typlen) c['attprecision'] = self.convert_precision_to_int(typlen)
else: else:
c['typlen'] = self.convert_typlen_to_int(typlen) c['attlen'] = self.convert_typlen_to_int(typlen)
c['precision'] = None c['attprecision'] = None
# Get formatted Column Options # Get formatted Column Options
if 'attfdwoptions' in c and c['attfdwoptions'] != '': if 'attfdwoptions' in c and c['attfdwoptions'] != '':
@ -1393,7 +1425,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
columns = [] columns = []
for c in data['columns']: for c in data['columns']:
columns.append(self.qtIdent(self.conn, c['attname'])) columns.append(self.qtIdent(self.conn, c['name']))
if len(columns) > 0: if len(columns) > 0:
columns = ", ".join(columns) columns = ", ".join(columns)
@ -1434,7 +1466,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
# Now we have all list of columns which we need # Now we have all list of columns which we need
if 'columns' in data: if 'columns' in data:
for c in data['columns']: for c in data['columns']:
columns.append(self.qtIdent(self.conn, c['attname'])) columns.append(self.qtIdent(self.conn, c['name']))
values.append('?') values.append('?')
if len(columns) > 0: if len(columns) > 0:
@ -1475,7 +1507,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
# Now we have all list of columns which we need # Now we have all list of columns which we need
if 'columns' in data: if 'columns' in data:
for c in data['columns']: for c in data['columns']:
columns.append(self.qtIdent(self.conn, c['attname'])) columns.append(self.qtIdent(self.conn, c['name']))
if len(columns) > 0: if len(columns) > 0:
if len(columns) == 1: if len(columns) == 1:
@ -1676,6 +1708,66 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
data['ftoptions'] = tmp_ftoptions data['ftoptions'] = tmp_ftoptions
@check_precondition
def enable_disable_triggers(self, gid, sid, did, scid, foid):
"""
This function will enable/disable trigger(s) on the table object
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
tid: Table ID
"""
# Below will decide if it's simple drop or drop with cascade call
data = request.form if request.form else json.loads(
request.data
)
# Convert str 'true' to boolean type
is_enable_trigger = data['is_enable_trigger']
try:
if foid is not None:
status, data = self._fetch_properties(
gid, sid, did, scid, foid, inherits=True)
if not status:
return data
elif not data:
return gone(self.not_found_error_msg())
SQL = render_template(
"/".join([self.template_path, 'enable_disable_trigger.sql']),
data=data, is_enable_trigger=is_enable_trigger
)
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
SQL = render_template(
"/".join([self.template_path, 'get_enabled_triggers.sql']),
tid=foid
)
status, trigger_res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
return make_json_response(
success=1,
info=gettext("Trigger(s) have been disabled")
if is_enable_trigger == 'D'
else gettext("Trigger(s) have been enabled"),
data={
'id': foid,
'scid': scid,
'has_enable_triggers': trigger_res
}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
SchemaDiffRegistry(blueprint.node_type, ForeignTableView) SchemaDiffRegistry(blueprint.node_type, ForeignTableView)
ForeignTableView.register_node_view(blueprint) ForeignTableView.register_node_view(blueprint)

View File

@ -5,9 +5,10 @@ foreign tables.
Do not remove these imports as they will be automatically imported by the view Do not remove these imports as they will be automatically imported by the view
module as its children module as its children
""" """
from pgadmin.browser.server_groups.servers.databases.schemas.tables.columns \
import blueprint as columns_module
from pgadmin.browser.server_groups.servers.databases.schemas.tables.triggers \ from pgadmin.browser.server_groups.servers.databases.schemas.tables.triggers \
import blueprint as triggers_modules import blueprint as triggers_modules
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\ from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
constraints import blueprint as constraints_modules constraints import blueprint as constraints_modules
from pgadmin.browser.server_groups.servers.databases.schemas.\
foreign_tables.foreign_table_columns import foreign_table_column_blueprint\
as foreign_tables_columns_modules

View File

@ -0,0 +1,97 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
""" Implements Column Node for foreign table """
import pgadmin.browser.server_groups.servers.databases as database
from flask_babel import gettext
from pgadmin.browser.collection import CollectionNodeModule
from pgadmin.browser.server_groups.servers.databases.schemas.tables.columns \
import ColumnsView
class ForeignTableColumnsModule(CollectionNodeModule):
"""
class ColumnsModule(CollectionNodeModule)
A module class for Column node derived from CollectionNodeModule.
Methods:
-------
* __init__(*args, **kwargs)
- Method is used to initialize the Column and it's base module.
* get_nodes(gid, sid, did, scid, tid)
- Method is used to generate the browser collection node.
* node_inode()
- Method is overridden from its base class to make the node as leaf node.
* script_load()
- Load the module script for schema, when any of the server node is
initialized.
"""
_NODE_TYPE = 'foreign_table_column'
_COLLECTION_LABEL = gettext("Columns")
def __init__(self, *args, **kwargs):
"""
Method is used to initialize the ColumnModule and it's base module.
Args:
*args:
**kwargs:
"""
self.min_ver = None
self.max_ver = None
super().__init__(*args, **kwargs)
def get_nodes(self, gid, sid, did, scid, foid, **kwargs):
"""
Generate the collection node
"""
if self.has_nodes(
sid, did, scid=scid, tid=foid,
base_template_path=ForeignTableColumnsView.BASE_TEMPLATE_PATH):
yield self.generate_browser_collection_node(foid)
@property
def script_load(self):
"""
Load the module script for server, when any of the server-group node is
initialized.
"""
return database.DatabaseModule.node_type
@property
def node_inode(self):
"""
Load the module node as a leaf node
"""
return False
@property
def module_use_template_javascript(self):
"""
Returns whether Jinja2 template is used for generating the javascript
module.
"""
return False
foreign_table_column_blueprint = ForeignTableColumnsModule(__name__)
class ForeignTableColumnsView(ColumnsView):
node_type = foreign_table_column_blueprint.node_type
node_label = "Column"
ForeignTableColumnsView.register_node_view(foreign_table_column_blueprint)

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#e1fff9;}.cls-2{fill:none;stroke:#aecec8;stroke-linejoin:round;stroke-width:0.75px;}.cls-3{fill:#16a085;}</style></defs><title>coll-column</title><g id="_2" data-name="2"><rect class="cls-1" x="4.15" y="1.95" width="5.5" height="9.9" rx="1" ry="1"/><line class="cls-2" x1="4.7" y1="6.9" x2="9.1" y2="6.9"/><line class="cls-2" x1="4.7" y1="9.1" x2="9.1" y2="9.1"/><path class="cls-3" d="M4.9,5.18l4,0v5.55a.35.35,0,0,1-.35.35H5.25a.35.35,0,0,1-.35-.35V5.18m-.75-.75v6.32a1.1,1.1,0,0,0,1.1,1.1h3.3a1.1,1.1,0,0,0,1.1-1.1V4.46l-5.5,0Z"/><path class="cls-3" d="M8.55,2.7A.35.35,0,0,1,8.9,3V4.5h-4V3a.35.35,0,0,1,.35-.35h3.3m0-.75H5.25A1.1,1.1,0,0,0,4.15,3v2.2h5.5V3A1.1,1.1,0,0,0,8.55,2Z"/><rect class="cls-1" x="6.35" y="4.15" width="5.5" height="9.9" rx="1" ry="1"/><line class="cls-2" x1="6.9" y1="9.1" x2="11.3" y2="9.1"/><line class="cls-2" x1="6.9" y1="11.3" x2="11.3" y2="11.3"/><path class="cls-3" d="M7.1,7.38l4,0V13a.35.35,0,0,1-.35.35H7.45A.35.35,0,0,1,7.1,13V7.38m-.75-.75V13A1.1,1.1,0,0,0,7.45,14h3.3a1.1,1.1,0,0,0,1.1-1.1V6.66l-5.5,0Z"/><path class="cls-3" d="M10.75,4.9a.35.35,0,0,1,.35.35V6.7h-4V5.25a.35.35,0,0,1,.35-.35h3.3m0-.75H7.45a1.1,1.1,0,0,0-1.1,1.1v2.2h5.5V5.25a1.1,1.1,0,0,0-1.1-1.1Z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="20" height="20"><defs><style>.cls-1{fill:#e1fff9;}.cls-2{fill:none;stroke:#aecec8;stroke-linejoin:round;stroke-width:0.75px;}.cls-3{fill:#16a085;}</style></defs><title>column</title><g id="_2" data-name="2"><rect class="cls-1" x="5.3" y="2.15" width="5.4" height="11.7" rx="1" ry="1"/><line class="cls-2" x1="5.75" y1="8" x2="10.25" y2="8"/><line class="cls-2" x1="5.75" y1="10.7" x2="10.25" y2="10.7"/><path class="cls-3" d="M6,5.83l3.9,0V13a.15.15,0,0,1-.15.15H6.2A.15.15,0,0,1,6,13V5.83M5.3,5.08V13a.9.9,0,0,0,.9.9H9.8a.9.9,0,0,0,.9-.9V5.1l-5.4,0Z"/><path class="cls-3" d="M9.8,2.9A.15.15,0,0,1,10,3V5H6V3A.15.15,0,0,1,6.2,2.9H9.8m0-.75H6.2a.9.9,0,0,0-.9.9v2.7h5.4V3a.9.9,0,0,0-.9-.9Z"/></g></svg>

After

Width:  |  Height:  |  Size: 768 B

View File

@ -0,0 +1,70 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { getNodeColumnSchema } from '../../../static/js/foreign_table.ui';
define('pgadmin.node.foreign_table_column', [
'sources/gettext', 'sources/url_for', 'pgadmin.browser',
'pgadmin.node.schema.dir/schema_child_tree_node', 'pgadmin.browser.collection',
], function(
gettext, url_for, pgBrowser
) {
if (!pgBrowser.Nodes['coll-foreign_table_column']) {
pgBrowser.Nodes['coll-foreign_table_column'] =
pgBrowser.Collection.extend({
node: 'foreign_table_column',
label: gettext('Columns'),
type: 'coll-foreign_table_column',
columns: ['name', 'cltype', 'description']
});
}
if (!pgBrowser.Nodes['foreign_table_column']) {
pgBrowser.Nodes['foreign_table_column'] = pgBrowser.Node.extend({
// Foreign table is added in parent_type to support triggers on
// foreign table where we need column information.
parent_type: ['foreign_table'],
collection_type: ['coll-foreign_table'],
type: 'foreign_table_column',
label: gettext('Column'),
hasSQL: true,
sqlAlterHelp: 'sql-altertable.html',
sqlCreateHelp: 'sql-altertable.html',
dialogHelp: url_for('help.static', {'filename': 'column_dialog.html'}),
canDrop: true,
canDropCascade: false,
hasDepends: true,
hasStatistics: true,
Init: function() {
/* Avoid multiple registration of menus */
if (this.initialized)
return;
this.initialized = true;
pgBrowser.add_menus([{
name: 'create_column_on_coll', node: 'coll-foreign_table_column', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('Column...'),
data: {action: 'create'}, enable: 'canCreate',
},{
name: 'create_column_onTable', node: 'foreign_table', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('Column...'),
data: {action: 'create'}, enable: 'canCreate',
},
]);
},
getSchema: function(treeNodeInfo, itemNodeData) {
return getNodeColumnSchema(treeNodeInfo, itemNodeData, pgBrowser);
},
});
}
return pgBrowser.Nodes['foreign_table_column'];
});

View File

@ -0,0 +1,8 @@
ALTER FOREIGN TABLE public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_3_$%{}[]()&*^!@""'`\/#" TYPE time(10) with time zone ;
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_3_$%{}[]()&*^!@""'`\/#" DROP NOT NULL;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment modification';

View File

@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_3_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_3_$%{}[]()&*^!@""'`\/#" time(6) with time zone DEFAULT now();
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment modification';

View File

@ -0,0 +1,5 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#" OPTIONS (SET column_name 'test_options_update');
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=111);

View File

@ -0,0 +1,13 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_4_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_4_$%{}[]()&*^!@""'`\/#" bigint OPTIONS (column_name 'test_options_update') NOT NULL GENERATED ALWAYS AS ((1000 + 1)) STORED;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=111);

View File

@ -0,0 +1,5 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_3_$%{}[]()&*^!@""'`\/#" time with time zone NOT NULL DEFAULT now();
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment';

View File

@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_3_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_3_$%{}[]()&*^!@""'`\/#" time with time zone NOT NULL DEFAULT now();
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment';

View File

@ -0,0 +1,9 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_4_$%{}[]()&*^!@""'`\/#" bigint OPTIONS (column_name 'test_options') NOT NULL GENERATED ALWAYS AS (1000+1) STORED;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=1);

View File

@ -0,0 +1,13 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_4_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_4_$%{}[]()&*^!@""'`\/#" bigint OPTIONS (column_name 'test_options') NOT NULL GENERATED ALWAYS AS ((1000 + 1)) STORED;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=1);

View File

@ -0,0 +1,213 @@
{
"scenarios": [
{
"type": "create",
"name": "Create FDW for foreign table",
"endpoint": "NODE-foreign_data_wrapper.obj",
"sql_endpoint": "NODE-foreign_data_wrapper.sql_id",
"data": {
"name": "test_fdw_for_foreign_table_columns",
"fdwacl": [],
"fdwoptions": []
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create foreign server for foreign table",
"endpoint": "NODE-foreign_server.obj",
"sql_endpoint": "NODE-foreign_server.sql_id",
"data": {
"name":"test_fs_for_foreign_table_column"
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create Foreign Table for testing column node.",
"endpoint": "NODE-foreign_table.obj",
"sql_endpoint": "NODE-foreign_table.sql_id",
"data": {
"name": "foreign_table_2_$%{}[]()&*^!@\"'`\\/#",
"columns": [],
"schema": "public",
"basensp": "public",
"ftsrvname": "test_fs_for_foreign_table_column",
"owner": "postgres",
"ftoptions": [],
"inherits": [],
"relacl": [],
"seclabels": []
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_1_$%{}[]()&*^!@\"'`\\/#",
"description": "Comment for create",
"cltype": "numeric",
"attlen":"10",
"attprecision":"5",
"colconstype":"n",
"attnotnull": true,
"attoptions": [],
"seclabels": [],
"defval": "1"
},
"expected_sql_file": "create_column_numeric.sql",
"expected_msql_file": "create_column_numeric.msql"
},
{
"type": "alter",
"name": "Alter FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attnum": 1,
"name": "new_col_1_$%{}[]()&*^!@\"'`\\/#",
"description": "Comment for alter",
"cltype": "real",
"coloptions":{"added":[{"option":"column_name","value":"test"}]}
},
"expected_sql_file": "alter_column_numeric.sql",
"expected_msql_file": "alter_column_numeric.msql"
},
{
"type": "delete",
"name": "Drop FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "new_col_1_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create FT Column with text & default value",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_2_$%{}[]()&*^!@\"'`\\/#",
"cltype": "text",
"description": "test comment",
"attnotnull": true,
"colconstype": "n",
"defval": "'xyz'"
},
"expected_sql_file": "create_column_text_with_default_value.sql",
"expected_msql_file": "create_column_text_with_default_value.msql"
},
{
"type": "alter",
"name": "Alter FT Column with text & update default value",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attnum": 2,
"defval": "'changed default value'"
},
"expected_sql_file": "alter_column_text_with_default_value.sql",
"expected_msql_file": "alter_column_text_with_default_value.msql"
},
{
"type": "delete",
"name": "Drop FT column with text & update default value",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "col_2_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create FT Column with time with time zone & default value using function",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_3_$%{}[]()&*^!@\"'`\\/#",
"cltype": "time with time zone",
"attacl": [],
"description": "test comment",
"attnotnull": true,
"colconstype": "n",
"defval": "now()"
},
"expected_sql_file": "create_column_timestamp_with_default_value_using_function.sql",
"expected_msql_file": "create_column_timestamp_with_default_value_using_function.msql"
},
{
"type": "alter",
"name": "Alter FT Column with time with time zone & update length",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attlen": "10",
"attnotnull": false,
"attnum":3,
"description": "test comment modification"
},
"expected_sql_file": "alter_column_timestamp_with_default_value_using_function.sql",
"expected_msql_file": "alter_column_timestamp_with_default_value_using_function.msql"
},
{
"type": "delete",
"name": "Drop FT Column with time with time zone",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "col_3_$%{}[]()&*^!@\"'`\\/"
}
},
{
"type": "create",
"name": "Create FT Column with integer & generated always with expression, column option, variables",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {"name":"col_4_$%{}[]()&*^!@\"'`\\/#",
"description":"test comment",
"cltype":"bigint",
"attnotnull":true,
"coloptions":[{"option":"column_name","value":"test_options"}],
"colconstype":"g",
"is_tlength":false,
"is_precision":false,
"genexpr":"1000+1",
"attoptions":[{"name":"n_distinct","value":"1"}]},
"expected_sql_file": "create_column_with_integer_generated_always_column_option_variables.sql",
"expected_msql_file": "create_column_with_integer_generated_always_column_option_variables.msql"
},
{
"type": "alter",
"name": "Alter FT Column with integer & generated always with expression, column option, variables",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data":{
"attoptions":{
"changed":[{"name":"n_distinct","value":"111"}]},
"coloptions":{"changed":[{"option":"column_name","value":"test_options_update"}]},
"attnum":4},
"expected_sql_file": "alter_column_with_integer_generated_always_column_option_variables.sql",
"expected_msql_file": "alter_column_with_integer_generated_always_column_option_variables.msql"
},
{
"type": "delete",
"name": "Drop FT Column with time with time zone",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_columnn.sql_id",
"data": {
"name":"col_4_$%{}[]()&*^!@\"'`\\/#"
}
}
]}

View File

@ -0,0 +1,16 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from pgadmin.utils.route import BaseTestGenerator
class ForeignTableGeneratorTestCase(BaseTestGenerator):
def runTest(self):
return []

View File

@ -0,0 +1,154 @@
{
"column_create": [
{
"name": "Create: Add FT column with valid data",
"is_positive_test": true,
"inventory_data": {},
"test_data": {
"name": "test_ft_column_add_",
"description": "Comment for create",
"cltype": "numeric",
"attlen":"10",
"attprecision":"5",
"coloptions":[],
"colconstype":"n",
"attnotnull": true,
"attoptions": [],
"seclabels": [],
"defval": "1"
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
},
{
"name": "Create: Add FT column with valid data while server down",
"is_positive_test": false,
"inventory_data": {},
"test_data": {
"name": "test_column_add_",
"cltype": "\"char\"",
"attacl": [],
"is_primary_key": false,
"attnotnull": false,
"attlen": null,
"attprecision": null,
"attoptions": [],
"seclabels": [],
"description": {
"comment": "jsoncomment"
}
},
"mocking_required": true,
"mock_data": {
"function_name": "pgadmin.utils.driver.psycopg3.connection.Connection.execute_scalar",
"return_value": "[(False, 'Mocked Internal Server Error'),(True,True)]"
},
"expected_data": {
"status_code": 500,
"error_msg": "Mocked Internal Server Error",
"test_result_data": {}
}
}
],
"column_delete": [
{
"name": "Delete: Existing FT column",
"is_positive_test": true,
"inventory_data": {},
"test_data": {},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
},
"is_list": false
},
{
"name": "Delete: Multiple existing FT column",
"is_positive_test": true,
"inventory_data": {},
"test_data": {},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
},
"is_list": true
},
{
"name": "Delete: Non-existing FT column",
"is_positive_test": false,
"inventory_data": {},
"test_data": {
"column_id": 9999999
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": "Error: Object not found.",
"test_result_data": {}
},
"is_list": false
},
{
"name": "Delete: Existing FT column while server down",
"is_positive_test": false,
"inventory_data": {},
"test_data": {},
"mocking_required": true,
"mock_data": {
"function_name": "pgadmin.utils.driver.psycopg3.connection.Connection.execute_dict",
"return_value": "[(False,'Mocked Internal Server Error')]"
},
"expected_data": {
"status_code": 500,
"error_msg": "Mocked Internal Server Error",
"test_result_data": {}
},
"is_list": false
}
],
"column_get": [
{
"name": "Get details: For existing FT column",
"is_positive_test": true,
"inventory_data": {},
"test_data": {},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
},
"is_list": false
},
{
"name": "Get details: For existing FT column while server down",
"is_positive_test": false,
"inventory_data": {},
"test_data": {},
"mocking_required": true,
"mock_data": {
"function_name": "pgadmin.utils.driver.psycopg3.connection.Connection.execute_dict",
"return_value": "[(False,'Mocked Internal Server Error')]"
},
"expected_data": {
"status_code": 500,
"error_msg": "Mocked Internal Server Error",
"test_result_data": {}
},
"is_list": false
}
]
}

View File

@ -0,0 +1,10 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
RENAME "col_1_$%{}[]()&*^!@""'`\/#" TO "new_col_1_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "new_col_1_$%{}[]()&*^!@""'`\/#" OPTIONS (ADD column_name 'test');
ALTER FOREIGN TABLE public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "new_col_1_$%{}[]()&*^!@""'`\/#" TYPE real;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."new_col_1_$%{}[]()&*^!@""'`\/#"
IS 'Comment for alter';

View File

@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."new_col_1_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "new_col_1_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "new_col_1_$%{}[]()&*^!@""'`\/#" real OPTIONS (column_name 'test') NOT NULL DEFAULT 1;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."new_col_1_$%{}[]()&*^!@""'`\/#"
IS 'Comment for alter';

View File

@ -0,0 +1,2 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_2_$%{}[]()&*^!@""'`\/#" SET DEFAULT 'changed default value';

View File

@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_2_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_2_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_2_$%{}[]()&*^!@""'`\/#" text COLLATE pg_catalog."default" NOT NULL DEFAULT 'changed default value'::text;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_2_$%{}[]()&*^!@""'`\/#"
IS 'test comment';

View File

@ -0,0 +1,5 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_1_$%{}[]()&*^!@""'`\/#" numeric(10, 5) NOT NULL DEFAULT 1;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_1_$%{}[]()&*^!@""'`\/#"
IS 'Comment for create';

View File

@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_1_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_1_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_1_$%{}[]()&*^!@""'`\/#" numeric(10,5) NOT NULL DEFAULT 1;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_1_$%{}[]()&*^!@""'`\/#"
IS 'Comment for create';

View File

@ -0,0 +1,5 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_2_$%{}[]()&*^!@""'`\/#" text NOT NULL DEFAULT 'xyz';
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_2_$%{}[]()&*^!@""'`\/#"
IS 'test comment';

View File

@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_2_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_2_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_2_$%{}[]()&*^!@""'`\/#" text COLLATE pg_catalog."default" NOT NULL DEFAULT 'xyz'::text;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_2_$%{}[]()&*^!@""'`\/#"
IS 'test comment';

View File

@ -0,0 +1,130 @@
{
"scenarios": [
{
"type": "create",
"name": "Create FDW for foreign table",
"endpoint": "NODE-foreign_data_wrapper.obj",
"sql_endpoint": "NODE-foreign_data_wrapper.sql_id",
"data": {
"name": "test_fdw_for_foreign_table_columns",
"fdwacl": [],
"fdwoptions": []
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create foreign server for foreign table",
"endpoint": "NODE-foreign_server.obj",
"sql_endpoint": "NODE-foreign_server.sql_id",
"data": {
"name":"test_fs_for_foreign_table_column"
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create Foreign Table for testing column node.",
"endpoint": "NODE-foreign_table.obj",
"sql_endpoint": "NODE-foreign_table.sql_id",
"data": {
"name": "foreign_table_2_$%{}[]()&*^!@\"'`\\/#",
"columns": [],
"schema": "public",
"basensp": "public",
"ftsrvname": "test_fs_for_foreign_table_column",
"owner": "postgres",
"ftoptions": [],
"inherits": [],
"relacl": [],
"seclabels": []
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_1_$%{}[]()&*^!@\"'`\\/#",
"description": "Comment for create",
"cltype": "numeric",
"attlen":"10",
"attprecision":"5",
"coloptions":[],
"colconstype":"n",
"attnotnull": true,
"attoptions": [],
"seclabels": [],
"defval": "1"
},
"expected_sql_file": "create_column_numeric.sql",
"expected_msql_file": "create_column_numeric.msql"
},
{
"type": "alter",
"name": "Alter FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attnum": 1,
"name": "new_col_1_$%{}[]()&*^!@\"'`\\/#",
"description": "Comment for alter",
"cltype": "real",
"coloptions":{"added":[{"option":"column_name","value":"test"}]}
},
"expected_sql_file": "alter_column_numeric.sql",
"expected_msql_file": "alter_column_numeric.msql"
},
{
"type": "delete",
"name": "Drop FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "new_col_1_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create FT Column with text & default value",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_2_$%{}[]()&*^!@\"'`\\/#",
"cltype": "text",
"description": "test comment",
"attnotnull": true,
"colconstype": "n",
"defval": "'xyz'"
},
"expected_sql_file": "create_column_text_with_default_value.sql",
"expected_msql_file": "create_column_text_with_default_value.msql"
},
{
"type": "alter",
"name": "Alter FT Column with text & update default value",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attnum": 2,
"defval": "'changed default value'"
},
"expected_sql_file": "alter_column_text_with_default_value.sql",
"expected_msql_file": "alter_column_text_with_default_value.msql"
},
{
"type": "delete",
"name": "Drop FT column with text & update default value",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "col_2_$%{}[]()&*^!@\"'`\\/#"
}
}
]}

View File

@ -0,0 +1,111 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
from unittest.mock import patch
from pgadmin.browser.server_groups.servers.databases.schemas.foreign_tables.\
tests import utils as ft_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 pgadmin.browser.server_groups.servers.databases.schemas.foreign_tables.\
foreign_table_columns. tests import utils as columns_utils
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers. \
foreign_servers.tests import utils as fsrv_utils
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers. \
tests import utils as fdw_utils
class ColumnAddTestCase(BaseTestGenerator):
"""This class will add new column under table node."""
url = '/browser/foreign_table_column/obj/'
# Generates scenarios
scenarios = utils.generate_scenarios("column_create",
columns_utils.test_cases)
def setUp(self):
super().setUp()
# Load test data
self.data = self.test_data
# Get parent schema info
self.db_name = parent_node_dict["database"][-1]["db_name"]
self.schema_info = parent_node_dict['schema'][-1]
self.server_id = self.schema_info['server_id']
self.db_id = self.schema_info['db_id']
# Create db connection
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.")
# Create schema
self.schema_name = self.schema_info['schema_name']
self.schema_id = self.schema_info['schema_id']
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.")
# Create FDW, server & table
self.fdw_name = "fdw_%s" % (str(uuid.uuid4())[1:8])
self.fsrv_name = "fsrv_%s" % (str(uuid.uuid4())[1:8])
self.ft_name = "ft_%s" % (str(uuid.uuid4())[1:8])
self.fdw_id = fdw_utils.create_fdw(self.server, self.db_name,
self.fdw_name)
self.fsrv_id = fsrv_utils.create_fsrv(self.server, self.db_name,
self.fsrv_name, self.fdw_name)
self.ft_id = ft_utils.create_foreign_table(self.server, self.db_name,
self.schema_name,
self.fsrv_name,
self.ft_name)
def runTest(self):
"""This function will add column under table node."""
if "name" in self.data:
self.data["name"] = self.data["name"] + (str(uuid.uuid4())[1:8])
if self.is_positive_test:
response = columns_utils.api_create(self)
# Assert response
utils.assert_status_code(self, response)
# Verify in backend
self.assertIsNotNone(columns_utils.verify_column
(self.server, self.db_name,
self.data["name"]),
"Column not found")
else:
if self.mocking_required:
with patch(self.mock_data["function_name"],
side_effect=eval(self.mock_data["return_value"])):
response = columns_utils.api_create(self)
else:
if 'table_id' in self.data:
self.table_id = self.data['table_id']
response = columns_utils.api_create(self)
# Assert response
utils.assert_status_code(self, response)
utils.assert_error_message(self, response)
def tearDown(self):
# Disconnect the database
database_utils.disconnect_database(self, self.server_id, self.db_id)

View File

@ -0,0 +1,132 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
from unittest.mock import patch
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 pgadmin.browser.server_groups.servers.databases.schemas.foreign_tables.\
foreign_table_columns. tests import utils as columns_utils
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers. \
foreign_servers.tests import utils as fsrv_utils
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers. \
tests import utils as fdw_utils
from pgadmin.browser.server_groups.servers.databases.schemas.foreign_tables.\
tests import utils as ft_utils
class ColumnDeleteTestCase(BaseTestGenerator):
"""This class will delete column under table node."""
url = '/browser/foreign_table_column/obj/'
# Generates scenarios
scenarios = utils.generate_scenarios("column_delete",
columns_utils.test_cases)
def setUp(self):
super().setUp()
# Load test data
self.data = self.test_data
# Get parent schema info
self.db_name = parent_node_dict["database"][-1]["db_name"]
self.schema_info = parent_node_dict['schema'][-1]
self.server_id = self.schema_info['server_id']
self.db_id = self.schema_info['db_id']
# Create db connection
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.")
# Create schema
self.schema_name = self.schema_info['schema_name']
self.schema_id = self.schema_info['schema_id']
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.")
# Create FDW, server & table
self.fdw_name = "fdw_%s" % (str(uuid.uuid4())[1:8])
self.fsrv_name = "fsrv_%s" % (str(uuid.uuid4())[1:8])
self.ft_name = "ft_%s" % (str(uuid.uuid4())[1:8])
self.fdw_id = fdw_utils.create_fdw(self.server, self.db_name,
self.fdw_name)
self.fsrv_id = fsrv_utils.create_fsrv(self.server, self.db_name,
self.fsrv_name, self.fdw_name)
self.ft_id = ft_utils.create_foreign_table(self.server, self.db_name,
self.schema_name,
self.fsrv_name,
self.ft_name)
# Create column
self.column_name = "test_column_delete_%s" % (str(uuid.uuid4())[1:8])
self.column_id = columns_utils.create_column(self.server,
self.db_name,
self.schema_name,
self.ft_name,
self.column_name)
self.column_name_1 = "test_column_delete_%s" % (str(uuid.uuid4())[1:8])
self.column_id_1 = columns_utils.create_column(self.server,
self.db_name,
self.schema_name,
self.ft_name,
self.column_name_1)
# Verify column creation
col_response = columns_utils.verify_column(self.server, self.db_name,
self.column_name)
if not col_response:
raise Exception("Could not find the column to drop.")
col_response = columns_utils.verify_column(self.server, self.db_name,
self.column_name_1)
if not col_response:
raise Exception("Could not find the column to drop.")
def runTest(self):
"""This function will drop column under table node."""
if self.is_positive_test:
if self.is_list:
self.data["ids"] = [self.column_id, self.column_id_1]
response = columns_utils.api_delete(self, "")
else:
response = columns_utils.api_delete(self)
# Assert response
utils.assert_status_code(self, response)
else:
if self.mocking_required:
with patch(self.mock_data["function_name"],
side_effect=eval(self.mock_data["return_value"])):
response = columns_utils.api_delete(self)
else:
if 'column_id' in self.data:
self.column_id = self.data['column_id']
response = columns_utils.api_delete(self)
# Assert response
utils.assert_status_code(self, response)
utils.assert_error_message(self, response)
def tearDown(self):
# Disconnect the database
database_utils.disconnect_database(self, self.server_id, self.db_id)

View File

@ -0,0 +1,125 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
from unittest.mock import patch
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 columns_utils
from pgadmin.browser.server_groups.servers.databases.schemas.foreign_tables.\
foreign_table_columns. tests import utils as columns_utils
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers. \
foreign_servers.tests import utils as fsrv_utils
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers. \
tests import utils as fdw_utils
from pgadmin.browser.server_groups.servers.databases.schemas.foreign_tables.\
tests import utils as ft_utils
class ColumnGetTestCase(BaseTestGenerator):
"""This class will get column under table node."""
url = '/browser/foreign_table_column/obj/'
# Generates scenarios
scenarios = utils.generate_scenarios("column_get",
columns_utils.test_cases)
def setUp(self):
super().setUp()
# Load test data
self.data = self.test_data
# Get parent schema info
self.db_name = parent_node_dict["database"][-1]["db_name"]
self.schema_info = parent_node_dict['schema'][-1]
self.server_id = self.schema_info['server_id']
self.db_id = self.schema_info['db_id']
# Create db connection
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.")
# Create schema
self.schema_name = self.schema_info['schema_name']
self.schema_id = self.schema_info['schema_id']
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.")
# Create FDW, server & table
self.fdw_name = "fdw_%s" % (str(uuid.uuid4())[1:8])
self.fsrv_name = "fsrv_%s" % (str(uuid.uuid4())[1:8])
self.ft_name = "ft_%s" % (str(uuid.uuid4())[1:8])
self.fdw_id = fdw_utils.create_fdw(self.server, self.db_name,
self.fdw_name)
self.fsrv_id = fsrv_utils.create_fsrv(self.server, self.db_name,
self.fsrv_name, self.fdw_name)
self.ft_id = ft_utils.create_foreign_table(self.server, self.db_name,
self.schema_name,
self.fsrv_name,
self.ft_name)
# Create column
self.column_name = "test_column_delete_%s" % (str(uuid.uuid4())[1:8])
self.column_id = columns_utils.create_column(self.server,
self.db_name,
self.schema_name,
self.ft_name,
self.column_name)
if self.is_list:
# Create column
self.column_name_1 = "test_column_delete_%s" % \
(str(uuid.uuid4())[1:8])
self.column_id_1 = columns_utils.create_column(self.server,
self.db_name,
self.schema_name,
self.ft_name,
self.column_name_1)
def runTest(self):
"""This function will fetch the column under table node."""
if self.is_positive_test:
if self.is_list:
response = columns_utils.api_get(self, "")
else:
response = columns_utils.api_get(self)
# Assert response
utils.assert_status_code(self, response)
else:
if self.mocking_required:
with patch(self.mock_data["function_name"],
side_effect=eval(self.mock_data["return_value"])):
if self.is_list:
response = columns_utils.api_get(self, "")
else:
response = columns_utils.api_get(self)
else:
if 'column_id' in self.data:
self.column_id = self.data['column_id']
response = columns_utils.api_get(self)
# Assert response
utils.assert_status_code(self, response)
utils.assert_error_message(self, response)
def tearDown(self):
# Disconnect the database
database_utils.disconnect_database(self, self.server_id, self.db_id)

View File

@ -0,0 +1,203 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import sys
import traceback
import os
import json
from urllib.parse import urlencode
from regression.python_test_utils import test_utils as utils
# Load test data from json file.
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
with open(CURRENT_PATH + "/column_test_data.json") as data_file:
test_cases = json.load(data_file)
# api method calls
def api_create(self):
return self.tester.post("{0}{1}/{2}/{3}/{4}/{5}/".
format(self.url, utils.SERVER_GROUP,
self.server_id, self.db_id,
self.schema_id, self.ft_id),
data=json.dumps(self.data),
content_type='html/json'
)
def api_delete(self, column_id=None):
if column_id is None:
column_id = self.column_id
return self.tester.delete("{0}{1}/{2}/{3}/{4}/{5}/{6}".
format(self.url, utils.SERVER_GROUP,
self.server_id, self.db_id,
self.schema_id, self.ft_id, column_id),
data=json.dumps(self.data),
follow_redirects=True
)
def api_get(self, column_id=None):
if column_id is None:
column_id = self.column_id
return 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.ft_id, column_id),
data=json.dumps(self.data),
follow_redirects=True
)
def api_get_msql(self, url_encode_data):
return self.tester.get("{0}{1}/{2}/{3}/{4}/{5}/{6}?{7}".
format(self.url, utils.SERVER_GROUP, self.server_id,
self.db_id,
self.schema_id, self.ft_id,
self.column_id,
urlencode(url_encode_data)),
follow_redirects=True
)
def api_put(self):
return 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.ft_id,
self.column_id),
data=json.dumps(self.data),
follow_redirects=True)
def create_column(server, db_name, schema_name, table_name, col_name,
col_data_type='char'):
"""
This function creates a column 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 col_name: column name
:type col_name: str
:param col_data_type: column data type
:type col_data_type: str
:return ft_id: table 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
utils.set_isolation_level(connection, 0)
pg_cursor = connection.cursor()
query = "ALTER FOREIGN TABLE %s.%s ADD COLUMN %s %s" % \
(schema_name, table_name, col_name, col_data_type)
pg_cursor.execute(query)
utils.set_isolation_level(connection, old_isolation_level)
connection.commit()
# Get column position of newly added column
pg_cursor.execute("select attnum from pg_attribute where"
" attname='%s'" % col_name)
col = pg_cursor.fetchone()
col_pos = ''
if col:
col_pos = col[0]
connection.close()
return col_pos
except Exception:
traceback.print_exc(file=sys.stderr)
raise
def create_identity_column(server, db_name, schema_name, table_name,
col_name, col_data_type='bigint'):
"""
This function creates a column 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 col_name: column name
:type col_name: str
:param col_data_type: column data type
:type col_data_type: str
:return table_id: table 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
utils.set_isolation_level(connection, 0)
pg_cursor = connection.cursor()
query = "ALTER TABLE %s.%s ADD COLUMN %s %s " \
"GENERATED ALWAYS AS IDENTITY" % \
(schema_name, table_name, col_name, col_data_type)
pg_cursor.execute(query)
utils.set_isolation_level(connection, old_isolation_level)
connection.commit()
# Get column position of newly added column
pg_cursor.execute("select attnum from pg_attribute where"
" attname='%s'" % col_name)
col = pg_cursor.fetchone()
col_pos = ''
if col:
col_pos = col[0]
connection.close()
return col_pos
except Exception:
traceback.print_exc(file=sys.stderr)
raise
def verify_column(server, db_name, col_name):
"""
This function verifies table exist in database or not.
:param server: server details
:type server: dict
:param db_name: database name
:type db_name: str
:param col_name: column name
:type col_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_attribute where attname='%s'" %
col_name)
col = pg_cursor.fetchone()
connection.close()
return col
except Exception:
traceback.print_exc(file=sys.stderr)
raise

View File

@ -11,15 +11,16 @@ import { getNodeVariableSchema } from '../../../../../static/js/variable.ui';
import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui'; import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui';
import ForeignTableSchema from './foreign_table.ui'; import ForeignTableSchema from './foreign_table.ui';
import _ from 'lodash'; import _ from 'lodash';
import Notify from '../../../../../../../../static/js/helpers/Notifier';
/* Create and Register Foreign Table Collection and Node. */ /* Create and Register Foreign Table Collection and Node. */
define('pgadmin.node.foreign_table', [ define('pgadmin.node.foreign_table', ['pgadmin.tables.js/enable_disable_triggers',
'sources/gettext', 'sources/url_for', 'pgadmin.browser', 'sources/gettext', 'sources/url_for', 'pgadmin.browser',
'pgadmin.node.schema.dir/child', 'pgadmin.node.schema.dir/schema_child_tree_node', 'pgadmin.node.schema.dir/child', 'pgadmin.node.schema.dir/schema_child_tree_node',
'pgadmin.browser.collection','pgadmin.node.column', 'pgadmin.browser.collection','pgadmin.node.column',
'pgadmin.node.constraints' 'pgadmin.node.constraints'
], function( ], function(
gettext, url_for, pgBrowser, schemaChild, schemaChildTreeNode tableFunctions, gettext, url_for, pgBrowser, schemaChild, schemaChildTreeNode,
) { ) {
if (!pgBrowser.Nodes['coll-foreign_table']) { if (!pgBrowser.Nodes['coll-foreign_table']) {
@ -69,9 +70,54 @@ define('pgadmin.node.foreign_table', [
applies: ['object', 'context'], callback: 'show_obj_properties', applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('Foreign Table...'), category: 'create', priority: 4, label: gettext('Foreign Table...'),
data: {action: 'create', check: false}, enable: 'canCreate', data: {action: 'create', check: false}, enable: 'canCreate',
}, },{
// To enable/disable all triggers for the table
name: 'enable_all_triggers', node: 'foreign_table', module: this,
applies: ['object', 'context'], callback: 'enable_triggers_on_table',
category: gettext('Trigger(s)'), priority: 4, label: gettext('Enable All'),
enable : 'canCreate_with_trigger_enable',
data: {
data_disabled: gettext('The selected tree node does not support this option.'),
action: 'create'
},
},{
name: 'disable_all_triggers', node: 'foreign_table', module: this,
applies: ['object', 'context'], callback: 'disable_triggers_on_table',
category: gettext('Trigger(s)'), priority: 4, label: gettext('Disable All'),
enable : 'canCreate_with_trigger_disable',
data: {
data_disabled: gettext('The selected tree node does not support this option.'),
action: 'create'
}}
]); ]);
},
callbacks: {
/* Enable trigger(s) on table */
enable_triggers_on_table: function(args) {
tableFunctions.enableTriggers(
pgBrowser.tree,
Notify,
this.generate_url.bind(this),
args
);
},
/* Disable trigger(s) on table */
disable_triggers_on_table: function(args) {
tableFunctions.disableTriggers(
pgBrowser.tree,
Notify,
this.generate_url.bind(this),
args
);
},
},
// Check to whether table has disable trigger(s)
canCreate_with_trigger_enable: function(itemData) {
return itemData.tigger_count > 0 && (itemData.has_enable_triggers == 0 || itemData.has_enable_triggers < itemData.tigger_count);
},
// Check to whether table has enable trigger(s)
canCreate_with_trigger_disable: function(itemData) {
return itemData.tigger_count > 0 && itemData.has_enable_triggers > 0;
}, },
getSchema: function(treeNodeInfo, itemNodeData) { getSchema: function(treeNodeInfo, itemNodeData) {
return new ForeignTableSchema( return new ForeignTableSchema(

View File

@ -12,6 +12,7 @@ import SecLabelSchema from '../../../../../static/js/sec_label.ui';
import BaseUISchema from 'sources/SchemaView/base_schema.ui'; import BaseUISchema from 'sources/SchemaView/base_schema.ui';
import OptionsSchema from '../../../../../static/js/options.ui'; import OptionsSchema from '../../../../../static/js/options.ui';
import { isEmptyString } from 'sources/validators'; import { isEmptyString } from 'sources/validators';
import VariableSchema from 'top/browser/server_groups/servers/static/js/variable.ui';
import _ from 'lodash'; import _ from 'lodash';
import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui'; import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui';
@ -167,7 +168,7 @@ export default class ForeignTableSchema extends BaseUISchema {
id: 'columns', label: gettext('Columns'), cell: 'text', id: 'columns', label: gettext('Columns'), cell: 'text',
type: 'collection', group: gettext('Columns'), mode: ['edit', 'create'], type: 'collection', group: gettext('Columns'), mode: ['edit', 'create'],
schema: this.columnsObj, schema: this.columnsObj,
canAdd: true, canDelete: true, canEdit: true, columns: ['attname', 'datatype', 'inheritedfrom'], canAdd: true, canDelete: true, canEdit: true, columns: ['name', 'cltype', 'attprecision', 'attlen', 'inheritedfrom'],
// For each row edit/delete button enable/disable // For each row edit/delete button enable/disable
canEditRow: this.canEditDeleteRowColumns, canEditRow: this.canEditDeleteRowColumns,
canDeleteRow: this.canEditDeleteRowColumns, canDeleteRow: this.canEditDeleteRowColumns,
@ -256,27 +257,28 @@ export function getNodeColumnSchema(treeNodeInfo, itemNodeData, pgBrowser) {
export class ColumnSchema extends BaseUISchema { export class ColumnSchema extends BaseUISchema {
constructor(initValues, getPrivilegeRoleSchema, nodeInfo, datatypeOptions, collspcnameOptions) { constructor(initValues, getPrivilegeRoleSchema, nodeInfo, datatypeOptions, collspcnameOptions) {
super({ super({
attname: undefined, name: undefined,
datatype: undefined, description: undefined,
typlen: undefined, atttypid: undefined,
precision: undefined, cltype: undefined,
typdefault: undefined, edit_types: undefined,
attlen: undefined,
attprecision: undefined,
defval: undefined,
attnotnull: undefined, attnotnull: undefined,
collname: undefined, collspcname: undefined,
attstattarget:undefined,
attnum: undefined, attnum: undefined,
inheritedfrom: undefined, inheritedfrom: undefined,
inheritedid: undefined, inheritedid: undefined,
attstattarget: undefined,
coloptions: [], coloptions: [],
colconstype: 'n',
}); });
this.getPrivilegeRoleSchema = getPrivilegeRoleSchema; this.getPrivilegeRoleSchema = getPrivilegeRoleSchema;
this.nodeInfo = nodeInfo; this.nodeInfo = nodeInfo;
this.datatypeOptions = datatypeOptions; this.cltypeOptions = datatypeOptions;
this.collspcnameOptions = collspcnameOptions; this.collspcnameOptions = collspcnameOptions;
this.datatypes = [];
} }
get idAttribute() { get idAttribute() {
@ -287,43 +289,93 @@ export class ColumnSchema extends BaseUISchema {
return (_.isUndefined(state.inheritedid) || _.isNull(state.inheritedid) || _.isUndefined(state.inheritedfrom) || _.isNull(state.inheritedfrom)) ? true : false; return (_.isUndefined(state.inheritedid) || _.isNull(state.inheritedid) || _.isUndefined(state.inheritedfrom) || _.isNull(state.inheritedfrom)) ? true : false;
} }
// Check whether the column is a generated column
isTypeGenerated(state) {
let colconstype = state.colconstype;
return (!_.isUndefined(colconstype) && !_.isNull(colconstype) && colconstype == 'g');
}
get baseFields() { get baseFields() {
let obj = this; let obj = this;
return [ return [
{ {
id: 'attname', label: gettext('Name'), cell: 'text', id: 'name', label: gettext('Name'), cell: 'text',
type: 'text', editable: obj.editable_check_for_column, noEmpty: true, type: 'text', editable: obj.editable_check_for_column, noEmpty: true,
minWidth: 115, minWidth: 115,
disabled: (state)=>{
return state.is_inherited;
},
}, },
{ {
id: 'datatype', label: gettext('Data type'), minWidth: 150, id: 'description', label: gettext('Comment'), cell: 'text',
group: gettext('Definition'), noEmpty: true, type: 'multiline', mode: ['properties', 'create', 'edit'],
},
{
id: 'cltype',
label: gettext('Data type'),
minWidth: 150,
group: gettext('Definition'),
noEmpty: true,
editable: obj.editable_check_for_column, editable: obj.editable_check_for_column,
options: obj.datatypeOptions, disabled: (state)=>{
return state.is_inherited;
},
options: obj.cltypeOptions,
optionsLoaded: (options)=>{ optionsLoaded: (options)=>{
obj.datatypes = options;
obj.type_options = options; obj.type_options = options;
}, },
cell: 'select', cell: (row)=>{
return {
cell: 'select',
options: this.cltypeOptions,
controlProps: {
allowClear: false,
filter: (options)=>{
let result = options;
let edit_types = row?.edit_types || [];
if(!obj.isNew(row) && !this.inErd) {
result = _.filter(options, (o)=>edit_types.indexOf(o.value) > -1);
}
return result;
},
}
};
},
type: (state)=>{
return {
type: 'select',
options: this.cltypeOptions,
controlProps: {
allowClear: false,
filter: (options)=>{
let result = options;
let edit_types = state?.edit_types || [];
if(!obj.isNew(state) && !this.inErd) {
result = _.filter(options, (o)=>edit_types.indexOf(o.value) > -1);
}
return result;
},
}
};
},
controlProps: { controlProps: {
allowClear: false, allowClear: false,
}, }
type: 'select'
}, },
{ {
id: 'inheritedfrom', label: gettext('Inherited From'), cell: 'label', id: 'inheritedfrom', label: gettext('Inherited From'), cell: 'label',
type: 'label', readonly: true, editable: false, mode: ['properties', 'edit'], type: 'label', readonly: true, editable: false, mode: ['create','properties', 'edit'],
}, },
{ {
id: 'attnum', label: gettext('Position'), cell: 'text', id: 'attnum', label: gettext('Position'), cell: 'text',
type: 'text', disabled: obj.inCatalog(), mode: ['properties'], type: 'text', disabled: obj.inCatalog(), mode: ['properties'],
}, },
{ {
id: 'typlen', label: gettext('Length'), cell: 'int', id: 'attlen', label: gettext('Length'), cell: 'int',
deps: ['datatype'], type: 'int', group: gettext('Definition'), width: 120, minWidth: 120, deps: ['cltype'], type: 'int', group: gettext('Definition'), minWidth: 60,
disabled: (state) => { disabled: (state) => {
let val = state.typlen; let val = state.attlen;
// We will store type from selected from combobox // We will store type from selected from combobox
if(!(_.isUndefined(state.inheritedid) if(!(_.isUndefined(state.inheritedid)
|| _.isNull(state.inheritedid) || _.isNull(state.inheritedid)
@ -331,12 +383,12 @@ export class ColumnSchema extends BaseUISchema {
|| _.isNull(state.inheritedfrom))) { || _.isNull(state.inheritedfrom))) {
if (!_.isUndefined(val)) { if (!_.isUndefined(val)) {
state.typlen = undefined; state.attlen = undefined;
} }
return true; return true;
} }
let of_type = state.datatype, let of_type = state.cltype,
has_length = false; has_length = false;
if(obj.type_options) { if(obj.type_options) {
state.is_tlength = false; state.is_tlength = false;
@ -358,34 +410,34 @@ export class ColumnSchema extends BaseUISchema {
}); });
if (!has_length && !_.isUndefined(val)) { if (!has_length && !_.isUndefined(val)) {
state.typlen = undefined; state.attlen = undefined;
} }
return !(state.is_tlength); return !(state.is_tlength);
} }
if (!has_length && !_.isUndefined(val)) { if (!has_length && !_.isUndefined(val)) {
state.typlen = undefined; state.attlen = undefined;
} }
return true; return true;
}, },
}, },
{ {
id: 'precision', label: gettext('Precision'), cell: 'int', minWidth: 60, id: 'attprecision', label: gettext('Scale'), cell: 'int', minWidth: 60,
deps: ['datatype'], type: 'int', group: gettext('Definition'), deps: ['cltype'], type: 'int', group: gettext('Definition'),
disabled: (state) => { disabled: (state) => {
let val = state.precision; let val = state.attprecision;
if(!(_.isUndefined(state.inheritedid) if(!(_.isUndefined(state.inheritedid)
|| _.isNull(state.inheritedid) || _.isNull(state.inheritedid)
|| _.isUndefined(state.inheritedfrom) || _.isUndefined(state.inheritedfrom)
|| _.isNull(state.inheritedfrom))) { || _.isNull(state.inheritedfrom))) {
if (!_.isUndefined(val)) { if (!_.isUndefined(val)) {
state.precision = undefined; state.attprecision = undefined;
} }
return true; return true;
} }
let of_type = state.datatype, let of_type = state.cltype,
has_precision = false; has_precision = false;
if(obj.type_options) { if(obj.type_options) {
@ -406,34 +458,16 @@ export class ColumnSchema extends BaseUISchema {
} }
}); });
if (!has_precision && !_.isUndefined(val)) { if (!has_precision && !_.isUndefined(val)) {
state.precision = undefined; state.attprecision = undefined;
} }
return !(state.is_precision); return !(state.is_precision);
} }
if (!has_precision && !_.isUndefined(val)) { if (!has_precision && !_.isUndefined(val)) {
state.precision = undefined; state.attprecision = undefined;
} }
return true; return true;
}, },
}, },
{
id: 'typdefault', label: gettext('Default'), cell: 'text',
type: 'text', group: gettext('Definition'),
controlProps: {placeholder: gettext('Enter an expression or a value.')},
editable: (state) => {
if(!(_.isUndefined(state.inheritedid)
|| _.isNull(state.inheritedid)
|| _.isUndefined(state.inheritedfrom)
|| _.isNull(state.inheritedfrom))) { return false; }
return obj.nodeInfo.server.version >= 90300;
},
},
{
id: 'attnotnull', label: gettext('Not NULL?'), cell: 'switch',
type: 'switch', minWidth: 80,
group: gettext('Definition'), editable: obj.editable_check_for_column,
},
{ {
id: 'attstattarget', label: gettext('Statistics'), cell: 'text', id: 'attstattarget', label: gettext('Statistics'), cell: 'text',
type: 'text', disabled: (state) => { type: 'text', disabled: (state) => {
@ -451,9 +485,103 @@ export class ColumnSchema extends BaseUISchema {
group: gettext('Definition'), group: gettext('Definition'),
}, },
{ {
id: 'collname', label: gettext('Collation'), cell: 'select', id: 'attstorage', label: gettext('Storage'), group: gettext('Definition'),
type: 'select', mode: ['properties', 'edit'],
cell: 'select', readonly: obj.inSchemaWithColumnCheck,
controlProps: { placeholder: gettext('Select storage'),
allowClear: false,
},
options: [
{label: 'PLAIN', value: 'p'},
{label: 'MAIN', value: 'm'},
{label: 'EXTERNAL', value: 'e'},
{label: 'EXTENDED', value: 'x'},
],
},
{
id: 'defval',
label: gettext('Default'),
cell: 'text',
type: 'text',
group: gettext('Constraints'),
editable: (state) => {
if(!(_.isUndefined(state.inheritedid)
|| _.isNull(state.inheritedid)
|| _.isUndefined(state.inheritedfrom)
|| _.isNull(state.inheritedfrom))) { return false; }
return obj.nodeInfo.server.version >= 90300;
},
},
{
id: 'attnotnull',
label: gettext('Not NULL?'),
cell: 'switch',
type: 'switch',
minWidth: 80,
group: gettext('Constraints'),
editable: obj.editable_check_for_column,
},
{
id: 'colconstype',
label: gettext('Type'),
cell: 'text',
group: gettext('Constraints'),
type: (state)=>{
let options = [
{
'label': gettext('NONE'),
'value': 'n'},
]; // You can't change the existing column to Generated column.
if (this.isNew(state)) {
options.push({
'label': gettext('GENERATED'),
'value': 'g',
});
} else {
options.push({
'label': gettext('GENERATED'),
'value': 'g',
'disabled': true,
});
}
return {
type: 'toggle',
options: options,
};
},
disabled: function(state) {
return (!this.isNew(state) && state.colconstype == 'g');
},
min_version: 120000,
},
{
id: 'genexpr',
label: gettext('Expression'),
type: 'text',
mode: ['properties', 'create', 'edit'],
group: gettext('Constraints'),
min_version: 120000,
deps: ['colconstype'],
visible: this.isTypeGenerated,
readonly: function(state) {
return !this.isNew(state);
},
},
{
id: 'attoptions', label: gettext('Variables'), type: 'collection',
group: gettext('Variables'),
schema: new VariableSchema([
{label: 'n_distinct', value: 'n_distinct', vartype: 'string'},
{label: 'n_distinct_inherited', value: 'n_distinct_inherited', vartype: 'string'}
], null, null, ['name', 'value']),
uniqueCol : ['name'], mode: ['edit', 'create'],
canAdd: true, canEdit: false, canDelete: true,
},
{
id: 'collspcname', label: gettext('Collation'), cell: 'select',
type: 'select', group: gettext('Definition'), type: 'select', group: gettext('Definition'),
deps: ['datatype'], options: obj.collspcnameOptions, deps: ['cltype'], options: obj.collspcnameOptions,
disabled: (state)=>{ disabled: (state)=>{
if (!(_.isUndefined(obj.isNew)) && !obj.isNew(state)) { return false; } if (!(_.isUndefined(obj.isNew)) && !obj.isNew(state)) { return false; }

View File

@ -0,0 +1,39 @@
{% import 'macros/variable.macros' as VARIABLE %}
{% import 'types/macros/get_full_type_sql_format.macros' as GET_TYPE %}
{### Add column ###}
{% if data.name and data.cltype %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ADD COLUMN {{conn|qtIdent(data.name)}} {% if is_sql %}{{data.displaytypname}}{% else %}{{ GET_TYPE.CREATE_TYPE_SQL(conn, data.cltype, data.attlen, data.attprecision, data.hasSqrBracket) }}{% endif %}
{### Add coloptions to column ###}
{% if data.coloptions %}{% for o in data.coloptions %}{% if o.option is defined and o.value is defined %}{% if loop.first %}
OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral(conn)}}{% if loop.last %}){% endif %}{% endif %}{% endfor %}{% endif %}{% if data.collspcname %}
COLLATE {{data.collspcname}}{% endif %}{% if data.attnotnull %}
NOT NULL{% endif %}{% if data.defval is defined and data.defval is not none and data.defval != '' and data.colconstype != 'g' %}
DEFAULT {{data.defval}}{% endif %}{% if data.colconstype == 'g' and data.genexpr and data.genexpr != '' %}
GENERATED ALWAYS AS ({{data.genexpr}}) STORED{% endif %}{% endif %};
{### Add comments ###}
{% if data and data.description and data.description != None %}
COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
IS {{data.description|qtLiteral(conn)}};
{% endif %}
{### Add variables to column ###}
{% if data.attoptions %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
{{ VARIABLE.SET(conn, 'COLUMN', data.name, data.attoptions) }}
{% endif %}
{### Alter column statistics value ###}
{% if data.attstattarget is defined and data.attstattarget > -1 %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {{conn|qtTypeIdent(data.name)}} SET STATISTICS {{data.attstattarget}};
{% endif %}
{### Alter column storage value ###}
{% if data.attstorage is defined and data.attstorage != data.defaultstorage %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {{conn|qtTypeIdent(data.name)}} SET STORAGE {%if data.attstorage == 'p' %}
PLAIN{% elif data.attstorage == 'm'%}MAIN{% elif data.attstorage == 'e'%}
EXTERNAL{% elif data.attstorage == 'x'%}EXTENDED{% endif %};
{% endif %}

View File

@ -0,0 +1 @@
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}} DROP COLUMN IF EXISTS {{conn|qtIdent(data.name)}};

View File

@ -0,0 +1,110 @@
{% import 'macros/variable.macros' as VARIABLE %}
{% import 'types/macros/get_full_type_sql_format.macros' as GET_TYPE %}
{### Rename column name ###}
{% if data.name and data.name != o_data.name %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
RENAME {{conn|qtIdent(o_data.name)}} TO {{conn|qtIdent(data.name)}};
{% endif %}
{### Add/Update column options ###}
{% if 'coloptions' in data and data.coloptions != None and data.coloptions|length > 0 %}
{% set coloptions = data.coloptions %}
{% if data.name %}
{% if 'added' in coloptions and coloptions.added|length > 0 %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {{conn|qtIdent(data.name)}} OPTIONS (ADD {% for opt in coloptions.added %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(opt.option) }} {{ opt.value|qtLiteral(conn) }}{% endfor %});
{% endif %}
{% if 'changed' in coloptions and coloptions.changed|length > 0 %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {{conn|qtIdent(data.name)}} OPTIONS (SET {% for opt in coloptions.changed %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(opt.option) }} {{ opt.value|qtLiteral(conn) }}{% endfor %});
{% endif %}
{% if 'deleted' in coloptions and coloptions.deleted|length > 0 %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {{conn|qtIdent(data.name)}} OPTIONS (DROP {% for opt in coloptions.deleted %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(opt.option) }}{% endfor %});
{% endif %}
{% endif %}
{% endif %}
{### Alter column type and collation ###}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname) or data.col_type_conversion is defined %}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %}
-- WARNING:
-- The SQL statement below would normally be used to alter the cltype for the {{o_data.name}} column, however,
-- the current datatype cannot be cast to the target cltype so this conversion cannot be made automatically.
{% endif %}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %} -- {% endif %}ALTER FOREIGN TABLE {{conn|qtIdent(data.schema, data.table)}}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %} -- {% endif %} ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} TYPE {{ GET_TYPE.UPDATE_TYPE_SQL(conn, data, o_data) }}{% if data.collspcname and data.collspcname != o_data.collspcname %}
COLLATE {{data.collspcname}}{% elif o_data.collspcname %} COLLATE {{o_data.collspcname}}{% endif %};
{% endif %}
{### Alter column default value ###}
{% if is_view_only and data.defval is defined and data.defval is not none and data.defval != '' and data.defval != o_data.defval %}
ALTER VIEW {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} SET DEFAULT {{data.defval}};
{% elif data.defval is defined and data.defval is not none and data.defval != '' and data.defval != o_data.defval %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} SET DEFAULT {{data.defval}};
{% endif %}
{### Drop column default value ###}
{% if data.defval is defined and (data.defval == '' or data.defval is none) and data.defval != o_data.defval %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} DROP DEFAULT;
{% endif %}
{### Alter column not null value ###}
{% if 'attnotnull' in data and data.attnotnull != o_data.attnotnull %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} {% if data.attnotnull %}SET{% else %}DROP{% endif %} NOT NULL;
{% endif %}
{### Alter column statistics value ###}
{% if data.attstattarget is defined and data.attstattarget != o_data.attstattarget %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} SET STATISTICS {{data.attstattarget}};
{% endif %}
{### Alter column storage value ###}
{% if data.attstorage is defined and data.attstorage != o_data.attstorage %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} SET STORAGE {%if data.attstorage == 'p' %}
PLAIN{% elif data.attstorage == 'm'%}MAIN{% elif data.attstorage == 'e'%}
EXTERNAL{% elif data.attstorage == 'x'%}EXTENDED{% endif %};
{% endif %}
{% if data.description is defined and data.description != None %}
{% if data.name %}
COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
{% else %}
COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, o_data.name)}}
{% endif %}
IS {{data.description|qtLiteral(conn)}};
{% endif %}
{### Update column variables ###}
{% if 'attoptions' in data and data.attoptions != None and data.attoptions|length > 0 %}
{% set variables = data.attoptions %}
{% if 'deleted' in variables and variables.deleted|length > 0 %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
{% if data.name %}
{{ VARIABLE.UNSET(conn, 'COLUMN', data.name, variables.deleted) }}
{% else %}
{{ VARIABLE.UNSET(conn, 'COLUMN', o_data.name, variables.deleted) }}
{% endif %}
{% endif %}
{% if 'added' in variables and variables.added|length > 0 %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
{% if data.name %}
{{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.added) }}
{% else %}
{{ VARIABLE.SET(conn, 'COLUMN', o_data.name, variables.added) }}
{% endif %}
{% endif %}
{% if 'changed' in variables and variables.changed|length > 0 %}
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
{% if data.name %}
{{ VARIABLE.SET(conn, 'COLUMN', data.name, variables.changed) }}
{% else %}
{{ VARIABLE.SET(conn, 'COLUMN', o_data.name, variables.changed) }}
{% endif %}
{% endif %}
{% endif %}

View File

@ -7,12 +7,12 @@ CREATE FOREIGN TABLE{% if add_not_exists_clause %} IF NOT EXISTS{% endif %} {{ c
{% for c in data.columns %} {% for c in data.columns %}
{% if (not c.inheritedfrom or c.inheritedfrom =='' or c.inheritedfrom == None or c.inheritedfrom == 'None' ) %} {% if (not c.inheritedfrom or c.inheritedfrom =='' or c.inheritedfrom == None or c.inheritedfrom == 'None' ) %}
{% if is_columns.append('1') %}{% endif %} {% if is_columns.append('1') %}{% endif %}
{{conn|qtIdent(c.attname)}} {% if is_sql %}{{ c.fulltype }}{% else %}{{c.datatype }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% endif %}{% if c.coloptions %} {{conn|qtIdent(c.name)}} {% if is_sql %}{{ c.fulltype }}{% else %}{{c.cltype }}{% if c.attlen %}({{c.attlen}}{% if c.attprecision %}, {{c.attprecision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% endif %}{% if c.coloptions %}
{% for o in c.coloptions %}{% if o.option is defined and o.value is defined %} {% for o in c.coloptions %}{% if o.option is defined and o.value is defined %}
{% if loop.first %} OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral(conn)}}{% if loop.last %}){% endif %}{% endif %} {% if loop.first %} OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral(conn)}}{% if loop.last %}){% endif %}{% endif %}
{% endfor %}{% endif %} {% endfor %}{% endif %}
{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %} {% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
{% if c.typdefault is defined and c.typdefault is not none %} DEFAULT {{c.typdefault}}{% endif %} {% if c.defval is defined and c.defval is not none %} DEFAULT {{c.defval}}{% endif %}
{% if c.collname %} COLLATE {{c.collname}}{% endif %} {% if c.collname %} COLLATE {{c.collname}}{% endif %}
{% if not loop.last %}, {% if not loop.last %},
{% endif %} {% endif %}
@ -51,7 +51,7 @@ COMMENT ON FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
{% for c in data.columns %} {% for c in data.columns %}
{% if c.description %} {% if c.description %}
COMMENT ON COLUMN {{conn|qtIdent(data.basensp, data.name, c.attname)}} COMMENT ON COLUMN {{conn|qtIdent(data.basensp, data.name, c.name)}}
IS {{c.description|qtLiteral(conn)}}; IS {{c.description|qtLiteral(conn)}};
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View File

@ -0,0 +1,13 @@
SELECT t.main_oid, pg_catalog.ARRAY_AGG(t.typname) as edit_types
FROM
(SELECT pc.castsource AS main_oid, pg_catalog.format_type(tt.oid,NULL) AS typname
FROM pg_catalog.pg_type tt
JOIN pg_catalog.pg_cast pc ON tt.oid=pc.casttarget
WHERE pc.castsource IN ({{type_ids}})
AND pc.castcontext IN ('i', 'a')
UNION
SELECT tt.typbasetype AS main_oid, pg_catalog.format_type(tt.oid,NULL) AS typname
FROM pg_catalog.pg_type tt
WHERE tt.typbasetype IN ({{type_ids}})
) t
GROUP BY t.main_oid;

View File

@ -0,0 +1,3 @@
{% set enable_map = {'O':'ENABLE', 'D':'DISABLE'} %}
ALTER FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}
{{ enable_map[is_enable_trigger] }} TRIGGER ALL;

View File

@ -16,7 +16,7 @@ CREATE FOREIGN TABLE {{ conn|qtIdent(o_data.basensp, o_data.name) }}(
{% for c in columns %} {% for c in columns %}
{% if (not c.inheritedfrom or c.inheritedfrom =='' or c.inheritedfrom == None or c.inheritedfrom == 'None' ) %} {% if (not c.inheritedfrom or c.inheritedfrom =='' or c.inheritedfrom == None or c.inheritedfrom == 'None' ) %}
{% if is_columns.append('1') %}{% endif %} {% if is_columns.append('1') %}{% endif %}
{{conn|qtIdent(c.attname)}} {% if is_sql %}{{ c.fulltype }}{% else %}{{c.datatype }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% endif %}{% if c.coloptions %} {{conn|qtIdent(c.name)}} {% if is_sql %}{{ c.fulltype }}{% else %}{{c.cltype }}{% if c.attlen %}({{c.attlen}}{% if c.attprecision %}, {{c.attprecision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% endif %}{% if c.coloptions %}
{% for o in c.coloptions %}{% if o.option is defined and o.value is defined %} {% for o in c.coloptions %}{% if o.option is defined and o.value is defined %}
{% if loop.first %} OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral(conn)}}{% if loop.last %}){% endif %}{% endif %} {% if loop.first %} OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral(conn)}}{% if loop.last %}){% endif %}{% endif %}
{% endfor %}{% endif %} {% endfor %}{% endif %}

View File

@ -1,6 +1,6 @@
WITH INH_TABLES AS WITH INH_TABLES AS
(SELECT (SELECT
distinct on (at.attname) attname, ph.inhparent AS inheritedid, ph.inhseqno, at.attname AS name, ph.inhparent AS inheritedid, ph.inhseqno,
pg_catalog.concat(nmsp_parent.nspname, '.',parent.relname ) AS inheritedfrom pg_catalog.concat(nmsp_parent.nspname, '.',parent.relname ) AS inheritedfrom
FROM FROM
pg_catalog.pg_attribute at pg_catalog.pg_attribute at
@ -13,9 +13,12 @@ WITH INH_TABLES AS
GROUP BY at.attname, ph.inhparent, ph.inhseqno, inheritedfrom GROUP BY at.attname, ph.inhparent, ph.inhseqno, inheritedfrom
ORDER BY at.attname, ph.inhparent, ph.inhseqno, inheritedfrom ORDER BY at.attname, ph.inhparent, ph.inhseqno, inheritedfrom
) )
SELECT INH.inheritedfrom, INH.inheritedid, att.attoptions, attfdwoptions, SELECT INH.inheritedfrom, INH.inheritedid, att.attoptions, att.atttypid, attfdwoptions,
att.attname, att.attndims, att.atttypmod, pg_catalog.format_type(t.oid,NULL) AS datatype, att.attname as name, att.attndims, att.atttypmod, pg_catalog.format_type(t.oid,NULL) AS cltype,
att.attnotnull, att.attstattarget, att.attnum, pg_catalog.format_type(t.oid, att.atttypmod) AS fulltype, att.attnotnull, att.attstattarget, att.attnum, pg_catalog.format_type(t.oid, att.atttypmod) AS fulltype,
CASE WHEN t.typelem > 0 THEN t.typelem ELSE t.oid END as elemoid,
(SELECT nspname FROM pg_catalog.pg_namespace WHERE oid = t.typnamespace) as typnspname,
pg_catalog.format_type(t.oid,NULL) AS typname,
CASE WHEN length(cn.nspname::text) > 0 AND length(cl.collname::text) > 0 THEN CASE WHEN length(cn.nspname::text) > 0 AND length(cl.collname::text) > 0 THEN
pg_catalog.concat(cn.nspname, '."', cl.collname,'"') pg_catalog.concat(cn.nspname, '."', cl.collname,'"')
ELSE '' END AS collname, ELSE '' END AS collname,
@ -25,7 +28,7 @@ SELECT INH.inheritedfrom, INH.inheritedid, att.attoptions, attfdwoptions,
FROM FROM
pg_catalog.pg_attribute att pg_catalog.pg_attribute att
LEFT JOIN LEFT JOIN
INH_TABLES as INH ON att.attname = INH.attname INH_TABLES as INH ON att.attname = INH.name
JOIN JOIN
pg_catalog.pg_type t ON t.oid=atttypid pg_catalog.pg_type t ON t.oid=atttypid
JOIN JOIN

View File

@ -0,0 +1 @@
SELECT count(*) FROM pg_catalog.pg_trigger WHERE tgrelid={{tid}} AND tgisinternal = FALSE AND tgenabled = 'O'

View File

@ -1,6 +1,6 @@
{% if attrelid %} {% if attrelid %}
SELECT SELECT
a.attname, pg_catalog.format_type(a.atttypid, NULL) AS datatype, a.attname as name, pg_catalog.format_type(a.atttypid, NULL) AS cltype,
pg_catalog.quote_ident(n.nspname)||'.'||quote_ident(c.relname) as inheritedfrom, pg_catalog.quote_ident(n.nspname)||'.'||quote_ident(c.relname) as inheritedfrom,
c.oid as inheritedid c.oid as inheritedid
FROM FROM

View File

@ -1,6 +1,8 @@
SELECT SELECT
c.oid, c.relname AS name, pg_catalog.pg_get_userbyid(relowner) AS owner, c.oid, c.relname AS name, pg_catalog.pg_get_userbyid(relowner) AS owner,
ftoptions, nspname as basensp, description ftoptions, nspname as basensp, description,
(SELECT count(*) FROM pg_catalog.pg_trigger WHERE tgrelid=c.oid AND tgisinternal = FALSE) AS triggercount,
(SELECT count(*) FROM pg_catalog.pg_trigger WHERE tgrelid=c.oid AND tgisinternal = FALSE AND tgenabled = 'O') AS has_enable_triggers
FROM FROM
pg_catalog.pg_class c pg_catalog.pg_class c
JOIN JOIN

View File

@ -17,41 +17,42 @@ ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }}
{% for c in data.columns.deleted %} {% for c in data.columns.deleted %}
{% if (not c.inheritedfrom or c.inheritedfrom =='' or c.inheritedfrom == None or c.inheritedfrom == 'None' ) %} {% if (not c.inheritedfrom or c.inheritedfrom =='' or c.inheritedfrom == None or c.inheritedfrom == 'None' ) %}
ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }} ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }}
DROP COLUMN {{conn|qtIdent(c.attname)}}; DROP COLUMN {{conn|qtIdent(c.name)}};
{% endif %} {% endif %}
{% endfor -%} {% endfor -%}
{% for c in data.columns.added %} {% for c in data.columns.added %}
{% if (not c.inheritedfrom or c.inheritedfrom =='' or c.inheritedfrom == None or c.inheritedfrom == 'None' ) %} {% if (not c.inheritedfrom or c.inheritedfrom =='' or c.inheritedfrom == None or c.inheritedfrom == 'None' ) %}
ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }} ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }}
ADD COLUMN {{conn|qtIdent(c.attname)}} {{ c.datatype }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %} ADD COLUMN {{conn|qtIdent(c.name)}} {{ c.cltype }}{% if c.attlen %}({{c.attlen}}{% if c.attprecision %}, {{c.attprecision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}
{% if c.coloptions %} {% if c.coloptions %}
{% for o in c.coloptions %}{% if o.option is defined and o.value is defined %} {% for o in c.coloptions %}{% if o.option is defined and o.value is defined %}
{% if loop.first %} OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral(conn)}}{% if loop.last %}){% endif %}{% endif %} {% if loop.first %} OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral(conn)}}{% if loop.last %}){% endif %}{% endif %}
{% endfor %}{% endif %} {% endfor %}{% endif %}
{% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %} {% if c.attnotnull %} NOT NULL{% else %} NULL{% endif %}
{% if c.typdefault is defined and c.typdefault is not none %} DEFAULT {{c.typdefault}}{% endif %} {% if c.defval is defined and c.defval is not none %} DEFAULT {{c.defval}}{% endif %}
{% if c.collname %} COLLATE {{c.collname}}{% endif %}; {% if c.collname %} COLLATE {{c.collname}}{% endif %};
{% endif %} {% endif %}
{% endfor -%} {% endfor -%}
{% for c in data.columns.changed %} {% for c in data.columns.changed %}
{% set col_name = o_data['columns'][c.attnum]['attname'] %} {% set col_name = o_data['columns'][c.attnum]['name'] %}
{% if c.attname != o_data['columns'][c.attnum]['attname'] %} {% if c.name %}{% if c.name != o_data['columns'][c.attnum]['name'] %}
{% set col_name = c.attname %} {% set col_name = c.name %}
ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }} ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }}
RENAME COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['attname'])}} TO {{conn|qtIdent(c.attname)}}; RENAME COLUMN {{conn|qtIdent(o_data['columns'][c.attnum]['name'])}} TO {{conn|qtIdent(c.name)}};
{% endif %}
{% endif %} {% endif %}
{% if c.attnotnull != o_data['columns'][c.attnum]['attnotnull'] %} {% if c.attnotnull != o_data['columns'][c.attnum]['attnotnull'] %}
ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }} ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }}
ALTER COLUMN {{conn|qtIdent(col_name)}}{% if c.attnotnull %} SET{% else %} DROP{% endif %} NOT NULL; ALTER COLUMN {{conn|qtIdent(col_name)}}{% if c.attnotnull %} SET{% else %} DROP{% endif %} NOT NULL;
{% endif %} {% endif %}
{% if c.datatype != o_data['columns'][c.attnum]['datatype'] or c.typlen != o_data['columns'][c.attnum]['typlen'] or {% if c.cltype != o_data['columns'][c.attnum]['cltype'] or c.attlen != o_data['columns'][c.attnum]['attlen'] or
c.precision != o_data['columns'][c.attnum]['precision'] %} c.attprecision != o_data['columns'][c.attnum]['attprecision'] %}
ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }} ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }}
ALTER COLUMN {{conn|qtIdent(col_name)}} TYPE {{ c.datatype }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}; ALTER COLUMN {{conn|qtIdent(col_name)}} TYPE {{ c.cltype }}{% if c.attlen %}({{c.attlen}}{% if c.attprecision %}, {{c.attprecision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %};
{% endif %} {% endif %}
{% if c.typdefault is defined and c.typdefault != o_data['columns'][c.attnum]['typdefault'] %} {% if c.typdefault is defined and c.typdefault != o_data['columns'][c.attnum]['typdefault'] %}
@ -83,7 +84,7 @@ ALTER FOREIGN TABLE IF EXISTS {{ conn|qtIdent(o_data.basensp, name) }}
ALTER COLUMN {{conn|qtIdent(col_name)}} OPTIONS (SET {% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral(conn)}}{% if loop.last %});{% endif %} ALTER COLUMN {{conn|qtIdent(col_name)}} OPTIONS (SET {% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral(conn)}}{% if loop.last %});{% endif %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif -%} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% if data.inherits and data.inherits|length > 0%} {% if data.inherits and data.inherits|length > 0%}

View File

@ -9,8 +9,8 @@
"basensp": "schema_name", "basensp": "schema_name",
"columns": [ "columns": [
{ {
"attname": "ename", "name": "ename",
"datatype": "text", "cltype": "text",
"coloptions": [] "coloptions": []
} }
], ],
@ -41,8 +41,8 @@
"basensp": "schema_name", "basensp": "schema_name",
"columns": [ "columns": [
{ {
"attname": "ename", "name": "ename",
"datatype": "text", "cltype": "text",
"coloptions": [] "coloptions": []
} }
], ],
@ -72,8 +72,8 @@
"basensp": "schema_name", "basensp": "schema_name",
"columns": [ "columns": [
{ {
"attname": "ename", "name": "ename",
"datatype": "text", "cltype": "text",
"coloptions": [] "coloptions": []
} }
], ],
@ -255,8 +255,8 @@
"columns": { "columns": {
"added": [ "added": [
{ {
"attname": "col2", "name": "col2",
"datatype": "character varying[]", "cltype": "character varying[]",
"coloptions": [] "coloptions": []
} }
], ],
@ -268,8 +268,8 @@
"value": "OptionValue" "value": "OptionValue"
} }
], ],
"attname": "emp", "name": "emp",
"datatype": "\"char\"", "cltype": "\"char\"",
"typdefault": null, "typdefault": null,
"attnotnull": false, "attnotnull": false,
"collname": "pg_catalog.\"default\"", "collname": "pg_catalog.\"default\"",

View File

@ -11,7 +11,8 @@
"fdwoptions": [] "fdwoptions": []
}, },
"store_object_id": "True" "store_object_id": "True"
}, { },
{
"type": "create", "type": "create",
"name": "Create foreign server for foreign table", "name": "Create foreign server for foreign table",
"endpoint": "NODE-foreign_server.obj", "endpoint": "NODE-foreign_server.obj",
@ -20,7 +21,8 @@
"name":"test_fs_for_foreign_table" "name":"test_fs_for_foreign_table"
}, },
"store_object_id": "True" "store_object_id": "True"
}, { },
{
"type": "create", "type": "create",
"name": "Create Foreign Table with all options", "name": "Create Foreign Table with all options",
"endpoint": "NODE-foreign_table.obj", "endpoint": "NODE-foreign_table.obj",
@ -34,12 +36,12 @@
"description":"Test Comment", "description":"Test Comment",
"ftsrvname":"test_fs_for_foreign_table", "ftsrvname":"test_fs_for_foreign_table",
"columns":[{ "columns":[{
"attname":"col1", "name":"col1",
"datatype":"bigint", "cltype":"bigint",
"coloptions":[] "coloptions":[]
},{ },{
"attname":"col2", "name":"col2",
"datatype":"text", "cltype":"text",
"coloptions":[] "coloptions":[]
}], }],
"constraints":[{ "constraints":[{
@ -107,12 +109,12 @@
"description":"Test Comment", "description":"Test Comment",
"columns": { "columns": {
"added": [{ "added": [{
"attname":"col1", "name":"col1",
"datatype":"bigint", "cltype":"bigint",
"coloptions":[] "coloptions":[]
},{ },{
"attname":"col2", "name":"col2",
"datatype":"text", "cltype":"text",
"coloptions":[] "coloptions":[]
}] }]
} }
@ -182,17 +184,17 @@
}, },
"columns": { "columns": {
"changed": [{ "changed": [{
"attname": "col1", "name": "col1",
"attnum": 1, "attnum": 1,
"attoptions": null, "attoptions": null,
"collname": "", "collname": "",
"coloptions": [], "coloptions": [],
"datatype": "integer", "cltype": "integer",
"fulltype": "bigint" "fulltype": "bigint"
}], }],
"deleted": [{ "deleted": [{
"attname":"col2", "name":"col2",
"datatype":"text" "cltype":"text"
}] }]
} }
}, },

View File

@ -34,12 +34,12 @@
"description":"Test Comment", "description":"Test Comment",
"ftsrvname":"test_fs_for_foreign_table", "ftsrvname":"test_fs_for_foreign_table",
"columns":[{ "columns":[{
"attname":"col1", "name":"col1",
"datatype":"bigint", "cltype":"bigint",
"coloptions":[] "coloptions":[]
},{ },{
"attname":"col2", "name":"col2",
"datatype":"text", "cltype":"text",
"coloptions":[] "coloptions":[]
}], }],
"constraints":[{ "constraints":[{
@ -107,12 +107,12 @@
"description":"Test Comment", "description":"Test Comment",
"columns": { "columns": {
"added": [{ "added": [{
"attname":"col1", "name":"col1",
"datatype":"bigint", "cltype":"bigint",
"coloptions":[] "coloptions":[]
},{ },{
"attname":"col2", "name":"col2",
"datatype":"text", "cltype":"text",
"coloptions":[] "coloptions":[]
}] }]
} }
@ -182,17 +182,17 @@
}, },
"columns": { "columns": {
"changed": [{ "changed": [{
"attname": "col1", "name": "col1",
"attnum": 1, "attnum": 1,
"attoptions": null, "attoptions": null,
"collname": "", "collname": "",
"coloptions": [], "coloptions": [],
"datatype": "integer", "cltype": "integer",
"fulltype": "bigint" "fulltype": "bigint"
}], }],
"deleted": [{ "deleted": [{
"attname":"col2", "name":"col2",
"datatype":"text" "cltype":"text"
}] }]
} }
}, },

View File

@ -25,6 +25,7 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
make_response as ajax_response, gone make_response as ajax_response, gone
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\ from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
columns import utils as column_utils columns import utils as column_utils
from pgadmin.utils.compile_template_name import compile_template_path
from pgadmin.utils.driver import get_driver from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER from config import PG_DEFAULT_DRIVER
from pgadmin.utils.ajax import ColParamsJSONDecoder from pgadmin.utils.ajax import ColParamsJSONDecoder
@ -214,6 +215,9 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
self.template_path = self.BASE_TEMPLATE_PATH.format( self.template_path = self.BASE_TEMPLATE_PATH.format(
self.manager.version) self.manager.version)
self.foreign_table_column_template_path = compile_template_path(
'foreign_table_columns/sql', self.manager.version)
# Allowed ACL for column 'Select/Update/Insert/References' # Allowed ACL for column 'Select/Update/Insert/References'
self.acl = ['a', 'r', 'w', 'x'] self.acl = ['a', 'r', 'w', 'x']
@ -420,7 +424,11 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
column_utils.type_formatter(data['cltype']) column_utils.type_formatter(data['cltype'])
data = column_utils.convert_length_precision_to_string(data) data = column_utils.convert_length_precision_to_string(data)
SQL = render_template("/".join([self.template_path, # Check if node is foreign_table_column
template_path = self.foreign_table_column_template_path \
if self.node_type == 'foreign_table_column' else self.template_path
SQL = render_template("/".join([template_path,
self._CREATE_SQL]), self._CREATE_SQL]),
data=data, conn=self.conn) data=data, conn=self.conn)
status, res = self.conn.execute_scalar(SQL) status, res = self.conn.execute_scalar(SQL)
@ -493,7 +501,12 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
data['schema'] = self.schema data['schema'] = self.schema
data['table'] = self.table data['table'] = self.table
SQL = render_template("/".join([self.template_path, # Check if node is foreign_table_column
template_path = self.foreign_table_column_template_path \
if self.node_type == 'foreign_table_column' \
else self.template_path
SQL = render_template("/".join([template_path,
self._DELETE_SQL]), self._DELETE_SQL]),
data=data, conn=self.conn) data=data, conn=self.conn)
status, res = self.conn.execute_scalar(SQL) status, res = self.conn.execute_scalar(SQL)
@ -649,9 +662,15 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
if 'attacl' in data: if 'attacl' in data:
data['attacl'] = parse_priv_to_db(data['attacl'], data['attacl'] = parse_priv_to_db(data['attacl'],
self.acl) self.acl)
# Check if node is foreign_table_column
template_path = self.foreign_table_column_template_path \
if self.node_type == 'foreign_table_column' \
else self.template_path
# If the request for new object which do not have did # If the request for new object which do not have did
sql = render_template( sql = render_template(
"/".join([self.template_path, self._CREATE_SQL]), "/".join([template_path, self._CREATE_SQL]),
data=data, conn=self.conn, is_sql=is_sql data=data, conn=self.conn, is_sql=is_sql
) )
@ -687,6 +706,11 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
data = column_utils.convert_length_precision_to_string(data) data = column_utils.convert_length_precision_to_string(data)
if clid is not None: if clid is not None:
# Check if node is foreign_table_column
template_path = self.foreign_table_column_template_path \
if self.node_type == 'foreign_table_column' \
else self.template_path
sql = render_template( sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]), "/".join([self.template_path, self._PROPERTIES_SQL]),
tid=tid, clid=clid, tid=tid, clid=clid,
@ -714,7 +738,7 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
self._parse_acl_to_db_parsing(data, old_data) self._parse_acl_to_db_parsing(data, old_data)
sql = render_template( sql = render_template(
"/".join([self.template_path, self._UPDATE_SQL]), "/".join([template_path, self._UPDATE_SQL]),
data=data, o_data=old_data, conn=self.conn, data=data, o_data=old_data, conn=self.conn,
is_view_only=is_view_only is_view_only=is_view_only
) )
@ -778,8 +802,12 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
self.conn, data['schema'], data['table'], data['name']) self.conn, data['schema'], data['table'], data['name'])
) )
# Join delete sql
template_path = self.foreign_table_column_template_path \
if self.node_type == 'foreign_table_column' \
else self.template_path
sql_header += render_template( sql_header += render_template(
"/".join([self.template_path, self._DELETE_SQL]), "/".join([template_path, self._DELETE_SQL]),
data=data, conn=self.conn data=data, conn=self.conn
) )
SQL = sql_header + '\n\n' + SQL SQL = sql_header + '\n\n' + SQL

View File

@ -155,6 +155,10 @@ def column_formatter(conn, tid, clid, data, edit_types_list=None,
data['seclabels'] = seclabels data['seclabels'] = seclabels
# Get formatted Column Options
if 'attfdwoptions' in data and data['attfdwoptions'] != '':
data['coloptions'] = _parse_options_for_column(data['attfdwoptions'])
# We need to parse & convert ACL coming from database to json format # We need to parse & convert ACL coming from database to json format
SQL = render_template("/".join([template_path, 'acl.sql']), SQL = render_template("/".join([template_path, 'acl.sql']),
tid=tid, clid=clid) tid=tid, clid=clid)
@ -186,12 +190,44 @@ def column_formatter(conn, tid, clid, data, edit_types_list=None,
# We will need present type in edit mode # We will need present type in edit mode
edit_types_list.append(data['typname']) edit_types_list.append(data['typname'])
data['edit_types'] = sorted(edit_types_list) data['edit_types'] = sorted(edit_types_list)
data['cltype'] = DataTypeReader.parse_type_name(data['cltype']) data['cltype'] = DataTypeReader.parse_type_name(data['cltype'])
return data return data
def _parse_options_for_column(db_variables):
"""
Function to format the output for variables.
Args:
db_variables: Variable object
Expected Object Format:
['option1=value1', ..]
where:
user_name and database are optional
Returns:
Variable Object in below format:
{
'variables': [
{'name': 'var_name', 'value': 'var_value',
'user_name': 'user_name', 'database': 'database_name'},
...]
}
where:
user_name and database are optional
"""
variables_lst = []
if db_variables is not None:
for row in db_variables:
# The value may contain equals in string, split on
# first equals only
var_name, var_value = row.split("=", 1)
var_dict = {'option': var_name, 'value': var_value}
variables_lst.append(var_dict)
return variables_lst
@get_template_path @get_template_path
def get_formatted_columns(conn, tid, data, other_columns, def get_formatted_columns(conn, tid, data, other_columns,
table_or_type, template_path=None): table_or_type, template_path=None):

View File

@ -1,5 +1,5 @@
SELECT DISTINCT ON (att.attnum) att.attname as name, att.atttypid, att.attlen, att.attnum, att.attndims, SELECT DISTINCT ON (att.attnum) att.attname as name, att.atttypid, att.attlen, att.attnum, att.attndims,
att.atttypmod, att.attacl, att.attnotnull, att.attoptions, att.attstattarget, att.atttypmod, att.attacl, att.attnotnull, att.attoptions, att.attfdwoptions, att.attstattarget,
att.attstorage, att.attidentity, att.attstorage, att.attidentity,
pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS defval, pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS defval,
pg_catalog.format_type(ty.oid,NULL) AS typname, pg_catalog.format_type(ty.oid,NULL) AS typname,

View File

@ -1,5 +1,5 @@
SELECT DISTINCT ON (att.attnum) att.attname as name, att.atttypid, att.attlen, att.attnum, att.attndims, SELECT DISTINCT ON (att.attnum) att.attname as name, att.atttypid, att.attlen, att.attnum, att.attndims,
att.atttypmod, att.attacl, att.attnotnull, att.attoptions, att.attstattarget, att.atttypmod, att.attacl, att.attnotnull, att.attoptions, att.attfdwoptions, att.attstattarget,
att.attstorage, att.attidentity, att.attstorage, att.attidentity,
pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS defval, pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS defval,
pg_catalog.format_type(ty.oid,NULL) AS typname, pg_catalog.format_type(ty.oid,NULL) AS typname,

View File

@ -1,5 +1,5 @@
SELECT att.attname as name, att.atttypid, att.attlen, att.attnum, att.attndims, SELECT att.attname as name, att.atttypid, att.attlen, att.attnum, att.attndims,
att.atttypmod, att.attacl, att.attnotnull, att.attoptions, att.attstattarget, att.atttypmod, att.attacl, att.attnotnull, att.attoptions, att.attfdwoptions, att.attstattarget,
att.attstorage, att.attidentity, att.attstorage, att.attidentity,
pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS defval, pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS defval,
pg_catalog.format_type(ty.oid,NULL) AS typname, pg_catalog.format_type(ty.oid,NULL) AS typname,

View File

@ -228,20 +228,6 @@ describe('ForeignTableColumnSchema', ()=>{
mount(getEditView(schemaObj, getInitData)); mount(getEditView(schemaObj, getInitData));
}); });
it('column editable', ()=>{
let state = {};
let editable = _.find(schemaObj.fields, (f)=>f.id=='attname').editable;
let status = editable(state);
expect(status).toBe(true);
});
it('typdefault editable', ()=>{
let state = {};
let editable = _.find(schemaObj.fields, (f)=>f.id=='typdefault').editable;
let status = editable(state);
expect(status).toBe(true);
});
it('typdefault_edit', ()=>{ it('typdefault_edit', ()=>{
let defaultSchemaObj = new ForeignTableSchema( let defaultSchemaObj = new ForeignTableSchema(
()=>new MockSchema(), ()=>new MockSchema(),

View File

@ -710,6 +710,8 @@ class ReverseEngineeredSQLTestCases(BaseTestGenerator):
self.parent_ids['fsid'] = object_id self.parent_ids['fsid'] = object_id
elif endpoint.__contains__("NODE-role.obj"): elif endpoint.__contains__("NODE-role.obj"):
object_name = object_data['rolname'] object_name = object_data['rolname']
elif endpoint.__contains__("NODE-foreign_table"):
self.parent_ids['tid'] = object_id
# Store object id with object name # Store object id with object name
self.all_object_ids[object_name] = object_id self.all_object_ids[object_name] = object_id

View File

@ -480,6 +480,7 @@ module.exports = [{
'pure|pgadmin.node.user_mapping', 'pure|pgadmin.node.user_mapping',
'pure|pgadmin.node.schema', 'pure|pgadmin.node.schema',
'pure|pgadmin.node.catalog', 'pure|pgadmin.node.catalog',
'pure|pgadmin.node.foreign_table_column',
'pure|pgadmin.node.catalog_object', 'pure|pgadmin.node.catalog_object',
'pure|pgadmin.node.collation', 'pure|pgadmin.node.collation',
'pure|pgadmin.node.domain', 'pure|pgadmin.node.domain',

View File

@ -115,6 +115,7 @@ let webpackShimConfig = {
'pgadmin.node.foreign_key': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key'), 'pgadmin.node.foreign_key': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key'),
'pgadmin.node.foreign_server': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/js/foreign_server'), 'pgadmin.node.foreign_server': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/js/foreign_server'),
'pgadmin.node.foreign_table': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table'), 'pgadmin.node.foreign_table': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table'),
'pgadmin.node.foreign_table_column': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/foreign_table_columns/static/js/foreign_table_column'),
'pgadmin.node.fts_configuration': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration'), 'pgadmin.node.fts_configuration': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration'),
'pgadmin.node.fts_dictionary': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary'), 'pgadmin.node.fts_dictionary': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary'),
'pgadmin.node.fts_parser': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/fts_parsers/static/js/fts_parser'), 'pgadmin.node.fts_parser': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/fts_parsers/static/js/fts_parser'),