Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for external tables in GPDB. Fixes #3168
- Loading branch information
1 parent
92a0bb6
commit 427314c
Showing
28 changed files
with
2,194 additions
and
0 deletions.
There are no files selected for viewing
275 changes: 275 additions & 0 deletions
275
web/pgadmin/browser/server_groups/servers/databases/external_tables/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,275 @@ | ||
########################################################################## | ||
# | ||
# pgAdmin 4 - PostgreSQL Tools | ||
# | ||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team | ||
# This software is released under the PostgreSQL Licence | ||
# | ||
########################################################################## | ||
|
||
"""Implements External Tables Node""" | ||
import os | ||
from functools import wraps | ||
from gettext import gettext | ||
|
||
from flask import render_template | ||
|
||
from config import PG_DEFAULT_DRIVER | ||
from pgadmin.browser.collection import CollectionNodeModule | ||
from pgadmin.browser.server_groups.servers import databases | ||
from pgadmin.browser.server_groups.servers.databases \ | ||
.external_tables.mapping_utils import map_execution_location | ||
from pgadmin.browser.server_groups.servers.databases \ | ||
.external_tables.properties import Properties, \ | ||
PropertiesTableNotFoundException, PropertiesException | ||
from pgadmin.browser.server_groups.servers.databases \ | ||
.external_tables.reverse_engineer_ddl import ReverseEngineerDDL | ||
from pgadmin.browser.utils import PGChildNodeView | ||
from pgadmin.utils.ajax import make_json_response, make_response, \ | ||
internal_server_error | ||
from pgadmin.utils.compile_template_name import compile_template_path | ||
from pgadmin.utils.driver import get_driver | ||
|
||
|
||
class ExternalTablesModule(CollectionNodeModule): | ||
""" | ||
class ExternalTablesModule(CollectionNodeModule) | ||
A module class for External Tables node derived from | ||
CollectionNodeModule. | ||
Methods: | ||
------- | ||
* __init__(*args, **kwargs) | ||
- Method is used to initialize the External Tables module | ||
and it's base module. | ||
* get_nodes(gid, sid, did) | ||
- Method is used to generate the browser collection node. | ||
* script_load() | ||
- Load the module script for External Tables, when any of | ||
the database node is initialized. | ||
""" | ||
|
||
NODE_TYPE = 'external_table' | ||
COLLECTION_LABEL = gettext("External Tables") | ||
|
||
def __init__(self, *args, **kwargs): | ||
""" | ||
Method is used to initialize the External tables module and | ||
it's base module. | ||
Args: | ||
*args: | ||
**kwargs: | ||
""" | ||
|
||
super(ExternalTablesModule, self).__init__(*args, **kwargs) | ||
self.max_ver = 0 | ||
|
||
def get_nodes(self, gid, sid, did): | ||
yield self.generate_browser_collection_node(did) | ||
|
||
@property | ||
def script_load(self): | ||
""" | ||
Load the module script for External tables, | ||
when any of the database node is initialized. | ||
Returns: node type of the database module. | ||
""" | ||
return databases.DatabaseModule.NODE_TYPE | ||
|
||
@property | ||
def module_use_template_javascript(self): | ||
""" | ||
Returns whether Jinja2 template is used for generating the javascript | ||
module. | ||
""" | ||
return False | ||
|
||
|
||
blueprint = ExternalTablesModule(__name__) | ||
|
||
|
||
class ExternalTablesView(PGChildNodeView): | ||
node_type = blueprint.node_type | ||
|
||
parent_ids = [ | ||
{'type': 'int', 'id': 'server_group_id'}, | ||
{'type': 'int', 'id': 'server_id'}, | ||
{'type': 'int', 'id': 'database_id'} | ||
] | ||
|
||
ids = [ | ||
{'type': 'int', 'id': 'external_table_id'} | ||
] | ||
|
||
operations = dict({ | ||
'obj': [ | ||
{'get': 'properties'} | ||
], | ||
'nodes': [{'get': 'node'}, {'get': 'nodes'}], | ||
'sql': [{'get': 'sql'}], | ||
'children': [{'get': 'children'}] | ||
}) | ||
|
||
def check_precondition(function_wrapped): | ||
""" | ||
This function will behave as a decorator which will checks | ||
database connection before running view, it will also attaches | ||
manager,conn & template_path properties to self | ||
""" | ||
|
||
@wraps(function_wrapped) | ||
def wrap(*args, **kwargs): | ||
# Here args[0] will hold self & kwargs will hold gid,sid,did | ||
self = args[0] | ||
self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager( | ||
kwargs['server_id'] | ||
) | ||
self.connection = self.manager.connection( | ||
did=kwargs['database_id'] | ||
) | ||
self.sql_template_path = compile_template_path( | ||
'sql/', | ||
self.manager.server_type, | ||
self.manager.sversion | ||
) | ||
|
||
return function_wrapped(*args, **kwargs) | ||
|
||
return wrap | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(ExternalTablesView, self).__init__(*args, **kwargs) | ||
self.connection = None | ||
self.manager = None | ||
self.sql_template_path = None | ||
|
||
@check_precondition | ||
def nodes(self, server_group_id, server_id, database_id): | ||
""" | ||
This function will used to create all the child node within that | ||
collection. | ||
Here it will create all the foreign data wrapper node. | ||
Args: | ||
server_group_id: Server Group ID | ||
server_id: Server ID | ||
database_id: Database ID | ||
""" | ||
sql_statement = render_template( | ||
os.path.join(self.sql_template_path, 'list.sql') | ||
) | ||
|
||
result = self.get_external_tables(database_id, sql_statement) | ||
|
||
if type(result) is not list: | ||
return result | ||
|
||
return make_json_response( | ||
data=result, | ||
status=200 | ||
) | ||
|
||
@check_precondition | ||
def node(self, server_group_id, server_id, database_id, external_table_id): | ||
""" | ||
This function will used to create all the child node within that | ||
collection. | ||
Here it will create all the foreign data wrapper node. | ||
Args: | ||
server_group_id: Server Group ID | ||
server_id: Server ID | ||
database_id: Database ID | ||
external_table_id: External Table ID | ||
""" | ||
sql_statement = render_template( | ||
template_name_or_list=os.path.join( | ||
self.sql_template_path, | ||
'node.sql' | ||
), | ||
external_table_id=external_table_id | ||
) | ||
result = self.get_external_tables(database_id, sql_statement) | ||
|
||
if type(result) is not list: | ||
return result | ||
|
||
if len(result) == 0: | ||
return make_json_response( | ||
data=gettext('Could not find the external table.'), | ||
status=404 | ||
) | ||
|
||
return make_json_response( | ||
data=result[0], | ||
status=200 | ||
) | ||
|
||
@check_precondition | ||
def sql(self, server_group_id, server_id, database_id, external_table_id): | ||
""" | ||
This function will used to create all the child node within that | ||
collection. | ||
Here it will create all the foreign data wrapper node. | ||
Args: | ||
server_group_id: Server Group ID | ||
server_id: Server ID | ||
database_id: Database ID | ||
external_table_id: External Table ID | ||
""" | ||
sql = ReverseEngineerDDL(self.sql_template_path, | ||
render_template, | ||
self.connection, server_group_id, server_id, | ||
database_id).execute(external_table_id) | ||
|
||
return make_response( | ||
sql.strip('\n') | ||
) | ||
|
||
@check_precondition | ||
def properties(self, server_group_id, server_id, database_id, | ||
external_table_id): | ||
try: | ||
response = Properties(render_template, self.connection, | ||
self.sql_template_path).retrieve( | ||
external_table_id) | ||
return make_response( | ||
response=response, | ||
status=200) | ||
except PropertiesTableNotFoundException: | ||
return make_json_response( | ||
data=gettext('Could not find the external table.'), | ||
status=404 | ||
) | ||
except PropertiesException as exception: | ||
return exception.response_object | ||
|
||
def children(self, **kwargs): | ||
return make_json_response(data=[]) | ||
|
||
def get_external_tables(self, database_id, sql_statement): | ||
status, external_tables = self.connection \ | ||
.execute_2darray(sql_statement) | ||
if not status: | ||
return internal_server_error(errormsg=external_tables) | ||
|
||
icon_css_class = 'icon-external_table' | ||
result = [] | ||
for external_table in external_tables['rows']: | ||
result.append(self.blueprint.generate_browser_node( | ||
external_table['oid'], | ||
database_id, | ||
external_table['name'], | ||
inode=False, | ||
icon=icon_css_class | ||
)) | ||
return result | ||
|
||
|
||
ExternalTablesView.register_node_view(blueprint) |
Empty file.
4 changes: 4 additions & 0 deletions
4
web/pgadmin/browser/server_groups/servers/databases/external_tables/actions/get_all_nodes.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
class GetAllNodes: | ||
def execute(self): | ||
pass |
Oops, something went wrong.