mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
FTS Dictionary support.
This commit is contained in:
parent
b47577390a
commit
7305986e14
@ -0,0 +1,818 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""Defines views for management of Fts Dictionary node"""
|
||||
|
||||
import json
|
||||
from flask import render_template, make_response, current_app, request, jsonify
|
||||
from flask.ext.babel import gettext as _
|
||||
from pgadmin.utils.ajax import make_json_response, \
|
||||
make_response as ajax_response, internal_server_error, gone
|
||||
from pgadmin.browser.utils import PGChildNodeView
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
|
||||
import SchemaChildModule
|
||||
import pgadmin.browser.server_groups.servers.databases.schemas as schemas
|
||||
from pgadmin.utils.ajax import precondition_required
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from functools import wraps
|
||||
|
||||
|
||||
class FtsDictionaryModule(SchemaChildModule):
|
||||
"""
|
||||
class FtsDictionaryModule(SchemaChildModule)
|
||||
|
||||
A module class for FTS Dictionary node derived from SchemaChildModule.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(*args, **kwargs)
|
||||
- Method is used to initialize the FtsDictionaryModule and
|
||||
it's base module.
|
||||
|
||||
* get_nodes(gid, sid, did, scid)
|
||||
- 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 FTS Dictionary, when any of the schema
|
||||
node is initialized.
|
||||
"""
|
||||
NODE_TYPE = 'fts_dictionary'
|
||||
COLLECTION_LABEL = _('FTS Dictionaries')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.min_ver = None
|
||||
self.max_ver = None
|
||||
self.manager = None
|
||||
super(FtsDictionaryModule, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_nodes(self, gid, sid, did, scid):
|
||||
"""
|
||||
Generate the collection node
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:param scid: schema id
|
||||
"""
|
||||
yield self.generate_browser_collection_node(scid)
|
||||
|
||||
@property
|
||||
def node_inode(self):
|
||||
"""
|
||||
Override the property to make the node as leaf node
|
||||
"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def script_load(self):
|
||||
"""
|
||||
Load the module script for fts template, when any of the schema
|
||||
node is initialized.
|
||||
"""
|
||||
return schemas.SchemaModule.NODE_TYPE
|
||||
|
||||
|
||||
blueprint = FtsDictionaryModule(__name__)
|
||||
|
||||
|
||||
class FtsDictionaryView(PGChildNodeView):
|
||||
"""
|
||||
class FtsDictionaryView(PGChildNodeView)
|
||||
|
||||
A view class for FTS Dictionary node derived from PGChildNodeView.
|
||||
This class is responsible for all the stuff related to view like
|
||||
create/update/delete FTS Dictionary,
|
||||
showing properties of node, showing sql in sql pane.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(**kwargs)
|
||||
- Method is used to initialize the FtsDictionaryView and it's base view.
|
||||
|
||||
* module_js()
|
||||
- This property defines (if javascript) exists for this node.
|
||||
Override this property for your own logic
|
||||
|
||||
* check_precondition()
|
||||
- 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
|
||||
|
||||
* tokenize_options(self, option_value):
|
||||
- This function will tokenize the string stored in database
|
||||
e.g. database store the value as below
|
||||
key1=value1, key2=value2, key3=value3, ....
|
||||
This function will extract key and value from above string
|
||||
|
||||
* list()
|
||||
- This function is used to list all the nodes within that collection.
|
||||
|
||||
* nodes()
|
||||
- This function will be used to create all the child node within collection.
|
||||
Here it will create all the FTS Dictionary nodes.
|
||||
|
||||
* node()
|
||||
- This function will be used to create a node given its oid
|
||||
Here it will create the FTS Template node based on its oid
|
||||
|
||||
* properties(gid, sid, did, scid, dcid)
|
||||
- This function will show the properties of the selected FTS Dictionary node
|
||||
|
||||
* create(gid, sid, did, scid)
|
||||
- This function will create the new FTS Dictionary object
|
||||
|
||||
* update(gid, sid, did, scid, dcid)
|
||||
- This function will update the data for the selected FTS Dictionary node
|
||||
|
||||
* delete(self, gid, sid, did, scid, dcid):
|
||||
- This function will drop the FTS Dictionary object
|
||||
|
||||
* msql(gid, sid, did, scid, dcid)
|
||||
- This function is used to return modified SQL for the selected node
|
||||
|
||||
* get_sql(data, dcid)
|
||||
- This function will generate sql from model data
|
||||
|
||||
* sql(gid, sid, did, scid, dcid):
|
||||
- This function will generate sql to show in sql pane for node.
|
||||
|
||||
* fetch_templates():
|
||||
- This function will fetch all templates related to node
|
||||
|
||||
* dependents(gid, sid, did, scid, dcid):
|
||||
- This function get the dependents and return ajax response for the node.
|
||||
|
||||
* dependencies(self, gid, sid, did, scid, dcid):
|
||||
- This function get the dependencies and return ajax response for node.
|
||||
|
||||
"""
|
||||
|
||||
node_type = blueprint.node_type
|
||||
|
||||
parent_ids = [
|
||||
{'type': 'int', 'id': 'gid'},
|
||||
{'type': 'int', 'id': 'sid'},
|
||||
{'type': 'int', 'id': 'did'},
|
||||
{'type': 'int', 'id': 'scid'}
|
||||
]
|
||||
ids = [
|
||||
{'type': 'int', 'id': 'dcid'}
|
||||
]
|
||||
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
],
|
||||
'children': [{
|
||||
'get': 'children'
|
||||
}],
|
||||
'delete': [{'delete': 'delete'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||
'stats': [{'get': 'statistics'}],
|
||||
'dependency': [{'get': 'dependencies'}],
|
||||
'dependent': [{'get': 'dependents'}],
|
||||
'module.js': [{}, {}, {'get': 'module_js'}],
|
||||
'fetch_templates': [{'get': 'fetch_templates'},
|
||||
{'get': 'fetch_templates'}],
|
||||
})
|
||||
|
||||
def _init_(self, **kwargs):
|
||||
self.conn = None
|
||||
self.template_path = None
|
||||
self.manager = None
|
||||
super(FtsDictionaryView, self).__init__(**kwargs)
|
||||
|
||||
def module_js(self):
|
||||
"""
|
||||
Load JS file (fts_dictionary.js) for this module.
|
||||
"""
|
||||
return make_response(
|
||||
render_template(
|
||||
"fts_dictionary/js/fts_dictionary.js",
|
||||
_=_
|
||||
),
|
||||
200, {'Content-Type': 'application/x-javascript'}
|
||||
)
|
||||
|
||||
def check_precondition(f):
|
||||
"""
|
||||
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(f)
|
||||
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['sid'])
|
||||
self.conn = self.manager.connection(did=kwargs['did'])
|
||||
# If DB not connected then return error to browser
|
||||
if not self.conn.connected():
|
||||
return precondition_required(
|
||||
_("Connection to the server has been lost!")
|
||||
)
|
||||
# we will set template path for sql scripts depending upon server version
|
||||
ver = self.manager.version
|
||||
if ver >= 90100:
|
||||
self.template_path = 'fts_dictionary/sql/9.1_plus'
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return wrap
|
||||
|
||||
def tokenize_options(self, option_value):
|
||||
"""
|
||||
This function will tokenize the string stored in database
|
||||
e.g. database store the value as below
|
||||
key1=value1, key2=value2, key3=value3, ....
|
||||
This function will extract key and value from above string
|
||||
|
||||
Args:
|
||||
option_value: key value option/value pair read from database
|
||||
"""
|
||||
if option_value is not None:
|
||||
option_str = option_value.split(',')
|
||||
options = []
|
||||
for fdw_option in option_str:
|
||||
k, v = fdw_option.split('=', 1)
|
||||
options.append({'option': k, 'value': v})
|
||||
return options
|
||||
|
||||
|
||||
@check_precondition
|
||||
def list(self, gid, sid, did, scid):
|
||||
"""
|
||||
List all FTS Dictionary nodes.
|
||||
|
||||
Args:
|
||||
gid: Server Group Id
|
||||
sid: Server Id
|
||||
did: Database Id
|
||||
scid: Schema Id
|
||||
"""
|
||||
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
scid=scid
|
||||
)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
for row in res['rows']:
|
||||
if row['options'] is not None:
|
||||
row['options'] = self.tokenize_options(row['options'])
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def nodes(self, gid, sid, did, scid):
|
||||
"""
|
||||
Return all FTS Dictionaries to generate nodes.
|
||||
|
||||
Args:
|
||||
gid: Server Group Id
|
||||
sid: Server Id
|
||||
did: Database Id
|
||||
scid: Schema Id
|
||||
"""
|
||||
|
||||
res = []
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'nodes.sql']),
|
||||
scid=scid
|
||||
)
|
||||
status, rset = self.conn.execute_2darray(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
res.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
row['oid'],
|
||||
did,
|
||||
row['name'],
|
||||
icon="icon-fts_dictionary"
|
||||
))
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def node(self, gid, sid, did, scid, dcid):
|
||||
"""
|
||||
Return FTS Dictionary node to generate node
|
||||
|
||||
Args:
|
||||
gid: Server Group Id
|
||||
sid: Server Id
|
||||
did: Database Id
|
||||
scid: Schema Id
|
||||
dcid: fts dictionary id
|
||||
"""
|
||||
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'nodes.sql']),
|
||||
dcid=dcid
|
||||
)
|
||||
status, rset = self.conn.execute_2darray(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
if len(rset['rows']) == 0:
|
||||
return gone(_("""
|
||||
Could not find the FTS Dictionary node.
|
||||
"""))
|
||||
|
||||
for row in rset['rows']:
|
||||
return make_json_response(
|
||||
data=self.blueprint.generate_browser_node(
|
||||
row['oid'],
|
||||
did,
|
||||
row['name'],
|
||||
icon="icon-fts_dictionary"
|
||||
),
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, scid, dcid):
|
||||
"""
|
||||
Show properties of FTS Dictionary node
|
||||
|
||||
Args:
|
||||
gid: Server Group Id
|
||||
sid: Server Id
|
||||
did: Database Id
|
||||
scid: Schema Id
|
||||
dcid: fts dictionary id
|
||||
"""
|
||||
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
scid=scid,
|
||||
dcid=dcid
|
||||
)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(_("""
|
||||
Could not find the FTS Dictionary node.
|
||||
"""))
|
||||
|
||||
if res['rows'][0]['options'] is not None:
|
||||
res['rows'][0]['options'] = self.tokenize_options(res['rows'][0]['options'])
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'][0],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def create(self, gid, sid, did, scid):
|
||||
"""
|
||||
This function will creates new the FTS Dictionary object
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:param scid: schema id
|
||||
"""
|
||||
|
||||
# Mandatory fields to create a new FTS Dictionary
|
||||
required_args = [
|
||||
'template',
|
||||
'schema',
|
||||
'name'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data.decode())
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=_(
|
||||
"Couldn't find the required parameter (%s)." % arg
|
||||
)
|
||||
)
|
||||
try:
|
||||
# Fetch schema name from schema oid
|
||||
sql = render_template("/".join([self.template_path, 'schema.sql']),
|
||||
data=data,
|
||||
conn=self.conn,
|
||||
)
|
||||
|
||||
status, schema = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=schema)
|
||||
|
||||
# Replace schema oid with schema name before passing to create.sql
|
||||
# To generate proper sql query
|
||||
new_data = data.copy()
|
||||
new_data['schema'] = schema
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']),
|
||||
data=new_data,
|
||||
conn=self.conn,
|
||||
)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
# We need dcid to add object in tree at browser,
|
||||
# Below sql will give the same
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
name=data['name']
|
||||
)
|
||||
status, dcid = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=dcid)
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
dcid,
|
||||
did,
|
||||
data['name'],
|
||||
icon="icon-fts_dictionary"
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def update(self, gid, sid, did, scid, dcid):
|
||||
"""
|
||||
This function will update FTS Dictionary object
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:param scid: schema id
|
||||
:param dcid: fts dictionary id
|
||||
"""
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data.decode())
|
||||
|
||||
# Fetch sql query to update fts dictionary
|
||||
sql = self.get_sql(gid, sid, did, scid, data, dcid)
|
||||
try:
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if dcid is not None:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
dcid=dcid,
|
||||
scid=scid
|
||||
)
|
||||
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(_("""
|
||||
Could not find the FTS Dictionary node to update.
|
||||
"""))
|
||||
|
||||
data = res['rows'][0]
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
dcid,
|
||||
did,
|
||||
data['name'],
|
||||
icon="icon-fts_dictionary"
|
||||
)
|
||||
)
|
||||
# In case FTS Dictionary node is not present
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': dcid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did,
|
||||
'scid': scid
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, scid, dcid):
|
||||
"""
|
||||
This function will drop the FTS Dictionary object
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:param scid: schema id
|
||||
:param dcid: FTS Dictionary id
|
||||
"""
|
||||
# Below will decide if it's simple drop or drop with cascade call
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
cascade = True
|
||||
else:
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name for FTS Dictionary from dcid
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||
dcid=dcid)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(_("""
|
||||
Could not find the FTS Dictionary node to delete.
|
||||
"""))
|
||||
|
||||
# Drop FTS Dictionary
|
||||
result = res['rows'][0]
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||
name=result['name'],
|
||||
schema=result['schema'],
|
||||
cascade=cascade
|
||||
)
|
||||
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=_("FTS Dictionary dropped"),
|
||||
data={
|
||||
'id': dcid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did,
|
||||
'scid': scid
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, scid, dcid=None):
|
||||
"""
|
||||
This function returns modified SQL
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:param scid: schema id
|
||||
:param dcid: FTS Dictionary id
|
||||
"""
|
||||
#data = request.args
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
|
||||
# Fetch sql query for modified data
|
||||
sql = self.get_sql(gid, sid, did, scid, data, dcid)
|
||||
|
||||
if isinstance(sql, str) and sql and sql.strip('\n') and sql.strip(' '):
|
||||
return make_json_response(
|
||||
data=sql,
|
||||
status=200
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
data="--modified SQL",
|
||||
status=200
|
||||
)
|
||||
|
||||
def get_sql(self, gid, sid, did, scid, data, dcid=None):
|
||||
"""
|
||||
This function will return SQL for model data
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:param scid: schema id
|
||||
:param dcid: fts dictionary id
|
||||
"""
|
||||
try:
|
||||
# Fetch sql for update
|
||||
if dcid is not None:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
dcid=dcid,
|
||||
scid=scid
|
||||
)
|
||||
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(_("""
|
||||
Could not find the FTS Dictionary node.
|
||||
"""))
|
||||
|
||||
old_data = res['rows'][0]
|
||||
|
||||
# If user has changed the schema then fetch new schema directly
|
||||
# using its oid otherwise fetch old schema name using its oid
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'schema.sql']),
|
||||
data=data)
|
||||
|
||||
status, new_schema = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=new_schema)
|
||||
|
||||
# Replace schema oid with schema name
|
||||
new_data = data.copy()
|
||||
if 'schema' in new_data:
|
||||
new_data['schema'] = new_schema
|
||||
|
||||
# Fetch old schema name using old schema oid
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'schema.sql']),
|
||||
data=old_data)
|
||||
|
||||
status, old_schema = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=old_schema)
|
||||
|
||||
# Replace old schema oid with old schema name
|
||||
old_data['schema'] = old_schema
|
||||
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'update.sql']),
|
||||
data=new_data, o_data=old_data
|
||||
)
|
||||
# Fetch sql query for modified data
|
||||
else:
|
||||
# Fetch schema name from schema oid
|
||||
sql = render_template("/".join([self.template_path, 'schema.sql']),
|
||||
data=data)
|
||||
|
||||
status, schema = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=schema)
|
||||
|
||||
# Replace schema oid with schema name
|
||||
new_data = data.copy()
|
||||
new_data['schema'] = schema
|
||||
|
||||
if 'template' in new_data and \
|
||||
'name' in new_data and \
|
||||
'schema' in new_data:
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'create.sql']),
|
||||
data=new_data,
|
||||
conn=self.conn
|
||||
)
|
||||
else:
|
||||
sql = "-- incomplete definition"
|
||||
return str(sql.strip('\n'))
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def fetch_templates(self, gid, sid, did, scid):
|
||||
"""
|
||||
This function will return templates list for FTS Dictionary
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:param scid: schema id
|
||||
"""
|
||||
# Fetch last system oid
|
||||
datlastsysoid = self.manager.db_info[did]['datlastsysoid']
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'templates.sql']),
|
||||
template=True)
|
||||
status, rset = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
# Empty set is added before actual list as initially it will be visible
|
||||
# at template control while creating a new FTS Dictionary
|
||||
res = [{'label': '', 'value': ''}]
|
||||
for row in rset['rows']:
|
||||
if row['schemaoid'] > datlastsysoid :
|
||||
row['tmplname'] = row['nspname'] + '.' + row['tmplname']
|
||||
|
||||
res.append({'label': row['tmplname'],
|
||||
'value': row['tmplname']})
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, scid, dcid):
|
||||
"""
|
||||
This function will reverse generate sql for sql panel
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:param scid: schema id
|
||||
:param dcid: FTS Dictionary id
|
||||
"""
|
||||
try:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'sql.sql']),
|
||||
dcid=dcid,
|
||||
scid=scid,
|
||||
conn=self.conn
|
||||
)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(
|
||||
_(
|
||||
"ERROR: Couldn't generate reversed engineered query for the FTS Dictionary!\n{0}").format(
|
||||
res
|
||||
)
|
||||
)
|
||||
|
||||
if res is None:
|
||||
return gone(
|
||||
_(
|
||||
"ERROR: Couldn't generate reversed engineered query for FTS Dictionary node!")
|
||||
)
|
||||
|
||||
return ajax_response(response=res)
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def dependents(self, gid, sid, did, scid, dcid):
|
||||
"""
|
||||
This function get the dependents and return ajax response
|
||||
for the FTS Dictionary node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
dcid: FTS Dictionary ID
|
||||
"""
|
||||
dependents_result = self.get_dependents(self.conn, dcid)
|
||||
return ajax_response(
|
||||
response=dependents_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def dependencies(self, gid, sid, did, scid, dcid):
|
||||
"""
|
||||
This function get the dependencies and return ajax response
|
||||
for the FTS Dictionary node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
dcid: FTS Dictionary ID
|
||||
"""
|
||||
dependencies_result = self.get_dependencies(self.conn, dcid)
|
||||
return ajax_response(
|
||||
response=dependencies_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
FtsDictionaryView.register_node_view(blueprint)
|
@ -0,0 +1,185 @@
|
||||
define(
|
||||
['jquery', 'underscore', 'underscore.string', 'pgadmin',
|
||||
'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
|
||||
function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
|
||||
// Extend the browser's node model class to create a option/value pair
|
||||
var OptionLabelModel = pgAdmin.Browser.Node.Model.extend({
|
||||
defaults: {
|
||||
options: undefined,
|
||||
value: undefined
|
||||
},
|
||||
// Define the schema for the Options
|
||||
schema: [
|
||||
{
|
||||
id: 'option', label:'Option', type:'text', group: null,
|
||||
cellHeaderClasses:'width_percent_50', editable: true
|
||||
},{
|
||||
id: 'value', label:'Value', type: 'text', group:null,
|
||||
cellHeaderClasses:'width_percent_50', editable: true
|
||||
},
|
||||
],
|
||||
validate: function() {
|
||||
// Clear any existing errors.
|
||||
this.errorModel.clear()
|
||||
|
||||
if (_.isUndefined(this.get('option')) ||
|
||||
String(this.get('option')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Option can not be empty!') }}';
|
||||
this.errorModel.set('option',msg);
|
||||
return msg;
|
||||
}
|
||||
if (_.isUndefined(this.get('value')) ||
|
||||
String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Value can not be empty!') }}';
|
||||
this.errorModel.set('value',msg);
|
||||
return msg;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Extend the collection class for FTS Dictionary
|
||||
if (!pgBrowser.Nodes['coll-fts_dictionary']) {
|
||||
var fts_dictionaries = pgAdmin.Browser.Nodes['coll-fts_dictionary'] =
|
||||
pgAdmin.Browser.Collection.extend({
|
||||
node: 'fts_dictionary',
|
||||
label: '{{ _('FTS Dictionaries') }}',
|
||||
type: 'coll-fts_dictionary',
|
||||
columns: ['name', 'description']
|
||||
});
|
||||
};
|
||||
|
||||
// Extend the node class for FTS Dictionary
|
||||
if (!pgBrowser.Nodes['fts_dictionary']) {
|
||||
pgAdmin.Browser.Nodes['fts_dictionary'] = pgAdmin.Browser.Node.extend({
|
||||
parent_type: ['schema', 'catalog'],
|
||||
type: 'fts_dictionary',
|
||||
sqlAlterHelp: 'sql-altertsdictionary.html',
|
||||
sqlCreateHelp: 'sql-createtsdictionary.html',
|
||||
canDrop: true,
|
||||
canDropCascade: true,
|
||||
label: '{{ _('FTS dictionaries') }}',
|
||||
hasSQL: true,
|
||||
hasDepends: true,
|
||||
Init: function() {
|
||||
|
||||
// Avoid multiple registration of menus
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
// Add context menus for FTS Dictionary
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_fts_dictionary_on_schema', node: 'schema', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{_('FTS Dictionary...')}}',
|
||||
icon: 'wcTabIcon icon-fts_dictionary', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_fts_dictionary_on_coll', node: 'coll-fts_dictionary',
|
||||
module: this, applies: ['object', 'context'], priority: 4,
|
||||
callback: 'show_obj_properties', category: 'create',
|
||||
label: '{{ _('FTS Dictionary...') }}', data: {action: 'create'},
|
||||
icon: 'wcTabIcon icon-fts_dictionary'
|
||||
},{
|
||||
name: 'create_fts_dictionary', node: 'fts_dictionary', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{_('FTS Dictionary...')}}',
|
||||
icon: 'wcTabIcon icon-fts_dictionary', data: {action: 'create'}
|
||||
}]);
|
||||
},
|
||||
|
||||
// Defining backform model for FTS Dictionary node
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
defaults: {
|
||||
name: undefined, // FTS Dictionary name
|
||||
owner: undefined, // FTS Dictionary owner
|
||||
description: undefined, // Comment on FTS Dictionary
|
||||
schema: undefined, // Schema name FTS dictionary belongs to
|
||||
template: undefined, // Template list for FTS dictionary node
|
||||
options: undefined // option/value pair list for FTS Dictionary
|
||||
},
|
||||
initialize: function(attrs, args) {
|
||||
var isNew = (_.size(attrs) === 0);
|
||||
|
||||
if (isNew) {
|
||||
var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
|
||||
this.set({'owner': userInfo.name}, {silent: true});
|
||||
}
|
||||
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
|
||||
if (_.isUndefined(this.get('schema'))) {
|
||||
this.set('schema', this.node_info.schema._id);
|
||||
}
|
||||
},
|
||||
// Defining schema for fts dictionary
|
||||
schema: [{
|
||||
id: 'name', label: '{{ _('Name') }}', cell: 'string',
|
||||
type: 'text', cellHeaderClasses: 'width_percent_50'
|
||||
},{
|
||||
id: 'oid', label:'{{ _('OID') }}', cell: 'string',
|
||||
editable: false, type: 'text', disabled: true, mode:['properties']
|
||||
},{
|
||||
id: 'owner', label:'{{ _('Owner') }}', cell: 'string',
|
||||
type: 'text', mode: ['properties', 'edit','create'], node: 'role',
|
||||
control: Backform.NodeListByNameControl
|
||||
},{
|
||||
id: 'schema', label: '{{ _('Schema')}}', cell: 'string',
|
||||
type: 'text', mode: ['create','edit'], node: 'schema',
|
||||
control: 'node-list-by-id'
|
||||
},{
|
||||
id: 'description', label:'{{ _('Comment') }}', cell: 'string',
|
||||
type: 'multiline', cellHeaderClasses: 'width_percent_50'
|
||||
},{
|
||||
id: 'template', label: '{{ _('Template')}}',type: 'text',
|
||||
disabled: function(m) { return !m.isNew(); }, url: 'fetch_templates',
|
||||
group: '{{ _('Definition') }}',control: 'node-ajax-options'
|
||||
},{
|
||||
id: 'options', label: '{{ _('Option') }}', type: 'collection',
|
||||
group: '{{ _('Options') }}', control: 'unique-col-collection',
|
||||
model: OptionLabelModel, columns: ['option', 'value'],
|
||||
uniqueCol : ['option'], mode: ['edit', 'create'],
|
||||
canAdd: true, canEdit: false,canDelete: true
|
||||
}],
|
||||
|
||||
/*
|
||||
* Triggers control specific error messages for dictionary name,
|
||||
* template and schema, if any one of them is not specified
|
||||
* while creating new fts dictionary
|
||||
*/
|
||||
validate: function(keys){
|
||||
var name = this.get('name');
|
||||
var template = this.get('template');;
|
||||
var schema = this.get('schema');
|
||||
|
||||
// Validate FTS Dictionary name
|
||||
if (_.isUndefined(name) || _.isNull(name) || String(name).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Name must be specified!') }}';
|
||||
this.errorModel.set('name', msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
// Validate template name
|
||||
else if (_.isUndefined(template) || _.isNull(template) || String(template).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Template must be selected!') }}';
|
||||
this.errorModel.set('template', msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
// Validate schema
|
||||
else if (_.isUndefined(schema) || _.isNull(schema) || String(schema).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Schema must be selected!') }}';
|
||||
this.errorModel.set('schema', msg);
|
||||
return msg;
|
||||
}
|
||||
else this.errorModel.clear();
|
||||
|
||||
this.trigger('on-status-clear');
|
||||
return null;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return pgBrowser.Nodes['coll-fts_dictionary'];
|
||||
});
|
@ -0,0 +1,13 @@
|
||||
{# CREATE FTS DICTIONARY Statement #}
|
||||
{% if data and data.schema and data.name and data.template %}
|
||||
CREATE TEXT SEARCH DICTIONARY {{ conn|qtIdent(data.schema, data.name) }} (
|
||||
TEMPLATE = {{ data.template }}{% for variable in data.options %}{% if "option" in variable and variable.option != '' %},
|
||||
{{ conn|qtIdent(variable.option) }} = {{ variable.value|qtLiteral }}{% endif %}{% endfor %}
|
||||
|
||||
);
|
||||
{# Description for FTS_DICTIONARY #}
|
||||
|
||||
{% if data.description %}
|
||||
COMMENT ON TEXT SEARCH DICTIONARY {{ conn|qtIdent(data.schema, data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
{% endif %}{% endif %}
|
@ -0,0 +1,23 @@
|
||||
{# FETCH FTS DICTIONARY NAME Statement #}
|
||||
{% if dcid %}
|
||||
SELECT
|
||||
dict.dictname as name,
|
||||
(
|
||||
SELECT
|
||||
nspname
|
||||
FROM
|
||||
pg_namespace
|
||||
WHERE
|
||||
oid = dict.dictnamespace
|
||||
) as schema
|
||||
FROM
|
||||
pg_ts_dict dict LEFT OUTER JOIN pg_description des
|
||||
ON (des.objoid=dict.oid AND des.classoid='pg_ts_dict'::regclass)
|
||||
WHERE
|
||||
dict.oid = {{dcid}}::OID;
|
||||
{% endif %}
|
||||
|
||||
{# DROP FTS DICTIOANRY Statement #}
|
||||
{% if schema and name %}
|
||||
DROP TEXT SEARCH DICTIONARY {{conn|qtIdent(schema)}}.{{conn|qtIdent(name)}} {% if cascade %}CASCADE{%endif%};
|
||||
{% endif %}
|
@ -0,0 +1,13 @@
|
||||
{# FETCH FTS DICTIONARY name statement #}
|
||||
SELECT
|
||||
oid, dictname as name
|
||||
FROM
|
||||
pg_ts_dict dict
|
||||
WHERE
|
||||
{% if scid %}
|
||||
dict.dictnamespace = {{scid}}::OID
|
||||
{% elif dcid %}
|
||||
dict.oid = {{pid}}::OID
|
||||
{% endif %}
|
||||
|
||||
ORDER BY name
|
@ -0,0 +1,24 @@
|
||||
{# FETCH properties for FTS DICTIONARY #}
|
||||
SELECT
|
||||
dict.oid,
|
||||
dict.dictname as name,
|
||||
pg_get_userbyid(dict.dictowner) as owner,
|
||||
t.tmplname as template,
|
||||
dict.dictinitoption as options,
|
||||
dict.dictnamespace as schema,
|
||||
des.description
|
||||
FROM
|
||||
pg_ts_dict dict
|
||||
LEFT OUTER JOIN pg_ts_template t ON t.oid=dict.dicttemplate
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=dict.oid AND des.classoid='pg_ts_dict'::regclass)
|
||||
WHERE
|
||||
{% if scid %}
|
||||
dict.dictnamespace = {{scid}}::OID
|
||||
{% elif name %}
|
||||
dict.dictname = {{name|qtLiteral}}
|
||||
{% endif %}
|
||||
{% if dcid %}
|
||||
AND dict.oid = {{dcid}}::OID
|
||||
{% endif %}
|
||||
ORDER BY
|
||||
dict.dictname
|
@ -0,0 +1,19 @@
|
||||
{# FETCH statement for SCHEMA name #}
|
||||
{% if data.schema %}
|
||||
SELECT
|
||||
nspname
|
||||
FROM
|
||||
pg_namespace
|
||||
WHERE
|
||||
oid = {{data.schema}}::OID
|
||||
|
||||
{% elif data.id %}
|
||||
SELECT
|
||||
nspname
|
||||
FROM
|
||||
pg_namespace nsp
|
||||
LEFT JOIN pg_ts_dict dict
|
||||
ON dict.dictnamespace = nsp.oid
|
||||
WHERE
|
||||
dict.oid = {{data.id}}::OID
|
||||
{% endif %}
|
@ -0,0 +1,52 @@
|
||||
{# REVERSED ENGINEERED SQL FOR FTS DICTIONARY #}
|
||||
{% if dcid and scid %}
|
||||
SELECT
|
||||
array_to_string(array_agg(sql), E'\n\n') as sql
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
E'-- Text Search Dictionary: ' || nspname || E'.' || dict.dictname ||
|
||||
E'\n\n-- DROP TEXT SEARCH DICTIONARY ' || nspname || E'.' || dict.dictname ||
|
||||
E'\n\nCREATE TEXT SEARCH DICTIONARY ' || nspname || E'.' || dict.dictname || E' (\n' ||
|
||||
E'\tTEMPLATE = ' || template ||
|
||||
CASE
|
||||
WHEN dict.dictinitoption IS NOT NULL THEN E',\n\t' || dict.dictinitoption
|
||||
ELSE ''
|
||||
END ||
|
||||
E'\n);' ||
|
||||
CASE
|
||||
WHEN description IS NOT NULL THEN
|
||||
E'\n\nCOMMENT ON TEXT SEARCH TEMPLATE ' || nspname || E'.' || dict.dictname ||
|
||||
E' IS ' || pg_catalog.quote_literal(description) || E';'
|
||||
ELSE '' END as sql
|
||||
FROM
|
||||
pg_ts_dict dict
|
||||
LEFT JOIN(
|
||||
SELECT
|
||||
t.tmplname as template,
|
||||
t.oid as oid
|
||||
FROM
|
||||
pg_ts_template t
|
||||
) d on d.oid = dict.dicttemplate
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
des.description as description,
|
||||
des.objoid as descoid
|
||||
FROM
|
||||
pg_description des
|
||||
WHERE
|
||||
des.objoid={{dcid}}::OID AND des.classoid='pg_ts_dict'::regclass
|
||||
) a ON (a.descoid = dict.oid)
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
nspname,
|
||||
nsp.oid as noid
|
||||
FROM
|
||||
pg_namespace nsp
|
||||
WHERE
|
||||
oid = {{scid}}::OID
|
||||
) b ON (b.noid = dict.dictnamespace)
|
||||
WHERE
|
||||
dict.oid={{dcid}}::OID
|
||||
) as c;
|
||||
{% endif %}
|
@ -0,0 +1,11 @@
|
||||
{# FETCH templates for FTS DICTIONARY #}
|
||||
{% if template %}
|
||||
SELECT
|
||||
tmplname,
|
||||
nspname,
|
||||
n.oid as schemaoid
|
||||
FROM
|
||||
pg_ts_template JOIN pg_namespace n ON n.oid=tmplnamespace
|
||||
ORDER BY
|
||||
tmplname
|
||||
{% endif %}
|
@ -0,0 +1,49 @@
|
||||
{# UPDATE statement for FTS DICTIONARY #}
|
||||
{% if data %}
|
||||
{% set name = o_data.name %}
|
||||
{% set schema = o_data.schema %}
|
||||
{% if data.name and data.name != o_data.name %}
|
||||
{% set name = data.name %}
|
||||
ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(o_data.name)}}
|
||||
RENAME TO {{data.name}};
|
||||
|
||||
{% endif %}
|
||||
{% if 'options' in data %}
|
||||
{% if'changed' in data.options %}
|
||||
{% for opt in data.options.changed %}
|
||||
ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
|
||||
({{opt.option}}={{opt.value}});
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if'added' in data.options%}
|
||||
{% for opt in data.options.added %}
|
||||
ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
|
||||
({{opt.option}}={{opt.value}});
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if'deleted' in data.options%}
|
||||
{% for opt in data.options.deleted %}
|
||||
ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
|
||||
({{opt.option}});
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if 'owner' in data and data.owner != o_data.owner %}
|
||||
ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
|
||||
OWNER TO {{data.owner}};
|
||||
|
||||
{% endif %}
|
||||
{% if 'schema' in data and data.schema != o_data.schema %}
|
||||
{% set schema = data.schema%}
|
||||
ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
|
||||
SET SCHEMA {{data.schema}};
|
||||
|
||||
{% endif %}
|
||||
{% if 'description' in data and data.description != o_data.description %}
|
||||
COMMENT ON TEXT SEARCH DICTIONARY {{conn|qtIdent(schema)}}.{{conn|qtIdent(name)}}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
{% endif %}
|
||||
{% endif %}
|
Loading…
Reference in New Issue
Block a user