FTS Template support.
parent
22841f8664
commit
95566ed5e1
|
@ -0,0 +1,719 @@
|
||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# 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 Template node"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from flask import render_template, make_response, current_app, request, jsonify
|
||||||
|
from flask.ext.babel import gettext
|
||||||
|
from pgadmin.utils.ajax import make_json_response, \
|
||||||
|
make_response as ajax_response, internal_server_error
|
||||||
|
from pgadmin.browser.utils import PGChildNodeView
|
||||||
|
from pgadmin.browser.server_groups.servers.databases.schemas.utils import SchemaChildModule
|
||||||
|
from pgadmin.browser.server_groups.servers.databases import DatabaseModule
|
||||||
|
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 FtsTemplateModule(SchemaChildModule):
|
||||||
|
"""
|
||||||
|
class FtsTemplateModule(SchemaChildModule)
|
||||||
|
|
||||||
|
A module class for FTS Template node derived from SchemaChildModule.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
-------
|
||||||
|
* __init__(*args, **kwargs)
|
||||||
|
- Method is used to initialize the FtsTemplateModule 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 Template, when any of the schema node is
|
||||||
|
initialized.
|
||||||
|
"""
|
||||||
|
NODE_TYPE = 'fts_template'
|
||||||
|
COLLECTION_LABEL = gettext('FTS Templates')
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.min_ver = None
|
||||||
|
self.max_ver = None
|
||||||
|
super(FtsTemplateModule, 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 DatabaseModule.NODE_TYPE
|
||||||
|
|
||||||
|
|
||||||
|
blueprint = FtsTemplateModule(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FtsTemplateView(PGChildNodeView):
|
||||||
|
"""
|
||||||
|
class FtsTemplateView(PGChildNodeView)
|
||||||
|
|
||||||
|
A view class for FTS Tempalte node derived from PGChildNodeView. This class is
|
||||||
|
responsible for all the stuff related to view like create/update/delete
|
||||||
|
responsible for all the stuff related to view like create/update/delete
|
||||||
|
FTS template, showing properties of node, showing sql in sql pane.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
-------
|
||||||
|
* __init__(**kwargs)
|
||||||
|
- Method is used to initialize the FtsTemplateView 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
|
||||||
|
|
||||||
|
* list()
|
||||||
|
- This function is used to list all the nodes within that collection.
|
||||||
|
|
||||||
|
* nodes()
|
||||||
|
- This function will used to create all the child node within that collection.
|
||||||
|
Here it will create all the FTS Template nodes.
|
||||||
|
|
||||||
|
* properties(gid, sid, did, scid, tid)
|
||||||
|
- This function will show the properties of the selected FTS Template node
|
||||||
|
|
||||||
|
* create(gid, sid, did, scid)
|
||||||
|
- This function will create the new FTS Template object
|
||||||
|
|
||||||
|
* update(gid, sid, did, scid, tid)
|
||||||
|
- This function will update the data for the selected FTS Template node
|
||||||
|
|
||||||
|
* delete(self, gid, sid, did, scid, tid):
|
||||||
|
- This function will drop the FTS Template object
|
||||||
|
|
||||||
|
* msql(gid, sid, did, scid, tid)
|
||||||
|
- This function is used to return modified SQL for the selected FTS Template node
|
||||||
|
|
||||||
|
* get_sql(data, tid)
|
||||||
|
- This function will generate sql from model data
|
||||||
|
|
||||||
|
* sql(gid, sid, did, scid, tid):
|
||||||
|
- This function will generate sql to show it in sql pane for the selected FTS Template node.
|
||||||
|
|
||||||
|
* get_type():
|
||||||
|
- This function will fetch all the types for source and target types select control.
|
||||||
|
|
||||||
|
* dependents(gid, sid, did, scid, tid):
|
||||||
|
- This function get the dependents and return ajax response for the Fts Tempalte node.
|
||||||
|
|
||||||
|
* dependencies(self, gid, sid, did, scid, tid):
|
||||||
|
- This function get the dependencies and return ajax response for the FTS Tempalte 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': 'tid'}
|
||||||
|
]
|
||||||
|
|
||||||
|
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'}],
|
||||||
|
'get_lexize': [{'get': 'get_lexize'}, {'get': 'get_lexize'}],
|
||||||
|
'get_init': [{'get': 'get_init'}, {'get': 'get_init'}],
|
||||||
|
})
|
||||||
|
|
||||||
|
def _init_(self, **kwargs):
|
||||||
|
self.conn = None
|
||||||
|
self.template_path = None
|
||||||
|
self.manager = None
|
||||||
|
super(FtsTemplateView, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def module_js(self):
|
||||||
|
"""
|
||||||
|
This property defines whether javascript exists for this node.
|
||||||
|
"""
|
||||||
|
return make_response(
|
||||||
|
render_template(
|
||||||
|
"fts_template/js/fts_templates.js",
|
||||||
|
_=gettext
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
gettext(
|
||||||
|
"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_template/sql/9.1_plus'
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrap
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def list(self, gid, sid, did, scid):
|
||||||
|
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)
|
||||||
|
|
||||||
|
return ajax_response(
|
||||||
|
response=res['rows'],
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def nodes(self, gid, sid, did, scid):
|
||||||
|
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_template"
|
||||||
|
))
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
data=res,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def node(self, gid, sid, did, scid, tid):
|
||||||
|
res = []
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'nodes.sql']),
|
||||||
|
tid=tid
|
||||||
|
)
|
||||||
|
status, rset = self.conn.execute_2darray(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=rset)
|
||||||
|
|
||||||
|
for row in rset['rows']:
|
||||||
|
return make_json_response(
|
||||||
|
data=self.blueprint.generate_browser_node(
|
||||||
|
row['oid'],
|
||||||
|
did,
|
||||||
|
row['name'],
|
||||||
|
icon="icon-fts_template"
|
||||||
|
),
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def properties(self, gid, sid, did, scid, tid):
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'properties.sql']),
|
||||||
|
scid=scid,
|
||||||
|
tid=tid
|
||||||
|
)
|
||||||
|
status, res = self.conn.execute_dict(sql)
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
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_template object
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Mandatory fields to create a new fts template
|
||||||
|
required_args = [
|
||||||
|
'tmpllexize',
|
||||||
|
'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=gettext(
|
||||||
|
"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 fts_template id to 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, tid = self.conn.execute_scalar(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=tid)
|
||||||
|
|
||||||
|
return jsonify(
|
||||||
|
node=self.blueprint.generate_browser_node(
|
||||||
|
tid,
|
||||||
|
did,
|
||||||
|
data['name'],
|
||||||
|
icon="icon-fts_template"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def update(self, gid, sid, did, scid, tid):
|
||||||
|
"""
|
||||||
|
This function will update text search template object
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
:param tid: fts tempate id
|
||||||
|
"""
|
||||||
|
data = request.form if request.form else json.loads(
|
||||||
|
request.data.decode())
|
||||||
|
|
||||||
|
# Fetch sql query to update fts template
|
||||||
|
sql = self.get_sql(gid, sid, did, scid, data, tid)
|
||||||
|
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)
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
success=1,
|
||||||
|
info="FTS Template updated",
|
||||||
|
data={
|
||||||
|
'id': tid,
|
||||||
|
'sid': sid,
|
||||||
|
'gid': gid,
|
||||||
|
'did': did,
|
||||||
|
'scid': scid
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return make_json_response(
|
||||||
|
success=1,
|
||||||
|
info="Nothing to update",
|
||||||
|
data={
|
||||||
|
'id': tid,
|
||||||
|
'sid': sid,
|
||||||
|
'gid': gid,
|
||||||
|
'did': did,
|
||||||
|
'scid': scid
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def delete(self, gid, sid, did, scid, tid):
|
||||||
|
"""
|
||||||
|
This function will drop the fts_template object
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
:param tid: fts tempate 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 template from tid
|
||||||
|
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||||
|
tid=tid)
|
||||||
|
status, res = self.conn.execute_dict(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
# Drop fts template
|
||||||
|
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=gettext("FTS template dropped"),
|
||||||
|
data={
|
||||||
|
'id': tid,
|
||||||
|
'sid': sid,
|
||||||
|
'gid': gid,
|
||||||
|
'did': did,
|
||||||
|
'scid': scid
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def msql(self, gid, sid, did, scid, tid=None):
|
||||||
|
"""
|
||||||
|
This function returns modified SQL
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
:param tid: fts tempate id
|
||||||
|
"""
|
||||||
|
data = request.args
|
||||||
|
|
||||||
|
# Fetch sql query for modified data
|
||||||
|
sql = self.get_sql(gid, sid, did, scid, data, tid)
|
||||||
|
|
||||||
|
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, tid=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 tid: fts tempate id
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Fetch sql for update
|
||||||
|
if tid is not None:
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'properties.sql']),
|
||||||
|
tid=tid,
|
||||||
|
scid=scid
|
||||||
|
)
|
||||||
|
|
||||||
|
status, res = self.conn.execute_dict(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
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 fts template 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 'tmpllexize' 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 get_lexize(self, gid, sid, did, scid, tid=None):
|
||||||
|
"""
|
||||||
|
This function will return lexize functions list for fts template
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
:param tid: fts tempate id
|
||||||
|
"""
|
||||||
|
data = request.args
|
||||||
|
sql = render_template("/".join([self.template_path, 'functions.sql']),
|
||||||
|
lexize=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 lexize select control while creating a new fts template
|
||||||
|
res = [{'label': '', 'value': ''}]
|
||||||
|
for row in rset['rows']:
|
||||||
|
res.append({'label': row['proname'],
|
||||||
|
'value': row['proname']})
|
||||||
|
return make_json_response(
|
||||||
|
data=res,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def get_init(self, gid, sid, did, scid, tid=None):
|
||||||
|
"""
|
||||||
|
This function will return init functions list for fts template
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
:param tid: fts tempate id
|
||||||
|
"""
|
||||||
|
data = request.args
|
||||||
|
sql = render_template("/".join([self.template_path, 'functions.sql']),
|
||||||
|
init=True)
|
||||||
|
status, rset = self.conn.execute_dict(sql)
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=rset)
|
||||||
|
|
||||||
|
# We have added this to map against '-' which is coming from server
|
||||||
|
res = [{'label': '', 'value': '-'}]
|
||||||
|
for row in rset['rows']:
|
||||||
|
res.append({'label': row['proname'],
|
||||||
|
'value': row['proname']})
|
||||||
|
return make_json_response(
|
||||||
|
data=res,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def sql(self, gid, sid, did, scid, tid):
|
||||||
|
"""
|
||||||
|
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 tid: fts tempate id
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'sql.sql']),
|
||||||
|
tid=tid,
|
||||||
|
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 template!\n{0}").format(
|
||||||
|
res
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if res is None:
|
||||||
|
return gone(
|
||||||
|
_(
|
||||||
|
"ERROR: Couldn't generate reversed engineered Query for FTS template node!")
|
||||||
|
)
|
||||||
|
|
||||||
|
return ajax_response(response=res)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def dependents(self, gid, sid, did, scid, tid):
|
||||||
|
"""
|
||||||
|
This function get the dependents and return ajax response
|
||||||
|
for the FTS Template node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
tid: FTS Template ID
|
||||||
|
"""
|
||||||
|
dependents_result = self.get_dependents(self.conn, tid)
|
||||||
|
return ajax_response(
|
||||||
|
response=dependents_result,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def dependencies(self, gid, sid, did, scid, tid):
|
||||||
|
"""
|
||||||
|
This function get the dependencies and return ajax response
|
||||||
|
for the FTS Template node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
tid: FTS Tempalte ID
|
||||||
|
"""
|
||||||
|
dependencies_result = self.get_dependencies(self.conn, tid)
|
||||||
|
return ajax_response(
|
||||||
|
response=dependencies_result,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
FtsTemplateView.register_node_view(blueprint)
|
Binary file not shown.
After Width: | Height: | Size: 601 B |
Binary file not shown.
After Width: | Height: | Size: 696 B |
|
@ -0,0 +1,133 @@
|
||||||
|
define(
|
||||||
|
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
|
||||||
|
function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||||
|
|
||||||
|
// Extend the collection class for fts template
|
||||||
|
if (!pgBrowser.Nodes['coll-fts_template']) {
|
||||||
|
var fts_templates = pgAdmin.Browser.Nodes['coll-fts_template'] =
|
||||||
|
pgAdmin.Browser.Collection.extend({
|
||||||
|
node: 'fts_template',
|
||||||
|
label: '{{ _('FTS Templates') }}',
|
||||||
|
type: 'coll-fts_template',
|
||||||
|
columns: ['name', 'description']
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extend the node class for fts template
|
||||||
|
if (!pgBrowser.Nodes['fts_template']) {
|
||||||
|
pgAdmin.Browser.Nodes['fts_template'] = pgAdmin.Browser.Node.extend({
|
||||||
|
parent_type: ['schema', 'catalog'],
|
||||||
|
type: 'fts_template',
|
||||||
|
canDrop: true,
|
||||||
|
canDropCascade: true,
|
||||||
|
label: '{{ _('FTS Templates') }}',
|
||||||
|
hasSQL: true,
|
||||||
|
hasDepends: true,
|
||||||
|
Init: function() {
|
||||||
|
|
||||||
|
// Avoid multiple registration of menus
|
||||||
|
if (this.initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
|
|
||||||
|
// Add context menus for fts template
|
||||||
|
pgBrowser.add_menus([{
|
||||||
|
name: 'create_fts_template_on_schema', node: 'schema', module: this,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
category: 'create', priority: 4, label: '{{ _('FTS Template...') }}',
|
||||||
|
icon: 'wcTabIcon icon-fts_template', data: {action: 'create'}
|
||||||
|
},{
|
||||||
|
name: 'create_fts_template_on_coll', node: 'coll-fts_template', module: this,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
category: 'create', priority: 4, label: '{{ _('FTS Template...') }}',
|
||||||
|
icon: 'wcTabIcon icon-fts_template', data: {action: 'create'}
|
||||||
|
},{
|
||||||
|
name: 'create_fts_template', node: 'fts_template', module: this,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
category: 'create', priority: 4, label: '{{ _('FTS Template...') }}',
|
||||||
|
icon: 'wcTabIcon icon-fts_template', data: {action: 'create'}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// Defining backform model for fts template node
|
||||||
|
model: pgAdmin.Browser.Node.Model.extend({
|
||||||
|
defaults: {
|
||||||
|
name: undefined, // Fts template name
|
||||||
|
description: undefined, // Comment on template
|
||||||
|
schema: undefined, // Schema name to which template belongs
|
||||||
|
tmplinit: undefined, // Init function for fts template
|
||||||
|
tmpllexize: undefined // Lexize function for fts template
|
||||||
|
},
|
||||||
|
initialize: function() {
|
||||||
|
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 template
|
||||||
|
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: '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: 'tmplinit', label: '{{ _('Init function')}}', group: '{{ _('Definition') }}',
|
||||||
|
type: 'text', disabled: function(m) { return !m.isNew(); },
|
||||||
|
control: 'node-ajax-options', url: 'get_init'
|
||||||
|
},{
|
||||||
|
id: 'tmpllexize', label: '{{ _('Lexize function')}}', group: '{{ _('Definition') }}',
|
||||||
|
type: 'text', disabled: function(m) { return !m.isNew(); },
|
||||||
|
control: 'node-ajax-options', url: 'get_lexize'
|
||||||
|
}],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Triggers control specific error messages for template name,
|
||||||
|
* lexize function and schema, if any one of them is not specified
|
||||||
|
* while creating new fts template
|
||||||
|
*/
|
||||||
|
validate: function(keys){
|
||||||
|
var name = this.get('name');
|
||||||
|
var lexize = this.get('tmpllexize');
|
||||||
|
var schema = this.get('schema');
|
||||||
|
|
||||||
|
// Validate fts template 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 lexize function control
|
||||||
|
else if (_.isUndefined(lexize) || _.isNull(lexize) || String(lexize).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
var msg = '{{ _('Lexize function must be selected!') }}';
|
||||||
|
this.errorModel.set('tmpllexize', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate schema for fts template
|
||||||
|
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_template'];
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
{# CREATE TEXT SEARCH TEMPLATE Statement #}
|
||||||
|
{% if data and data.schema and data.name and data.tmpllexize %}
|
||||||
|
CREATE TEXT SEARCH TEMPLATE {{ conn|qtIdent(data.schema, data.name) }} (
|
||||||
|
{% if data.tmplinit and data.tmplinit != '-'%} INIT = {{data.tmplinit}},{% endif %}
|
||||||
|
LEXIZE = {{data.tmpllexize}}
|
||||||
|
);
|
||||||
|
{# Description for TEXT SEARCH TEMPLATE #}
|
||||||
|
{% if data.description %}
|
||||||
|
COMMENT ON TEXT SEARCH TEMPLATE {{ conn|qtIdent(data.schema, data.name) }}
|
||||||
|
IS {{ data.description|qtLiteral }};
|
||||||
|
{% endif %}{% endif %}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{# FETCH TEXT SEARCH TEMPLATE NAME Statement #}
|
||||||
|
{% if tid %}
|
||||||
|
SELECT
|
||||||
|
t.tmplname AS name,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
nspname
|
||||||
|
FROM
|
||||||
|
pg_namespace
|
||||||
|
WHERE
|
||||||
|
oid = t.tmplnamespace
|
||||||
|
) as schema
|
||||||
|
FROM
|
||||||
|
pg_ts_template t LEFT JOIN pg_description d
|
||||||
|
ON d.objoid=t.oid AND d.classoid='pg_ts_template'::regclass
|
||||||
|
WHERE
|
||||||
|
t.oid = {{tid}}::OID;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# DROP TEXT SEARCH TEMPLATE Statement #}
|
||||||
|
{% if schema and name %}
|
||||||
|
DROP TEXT SEARCH TEMPLATE {{conn|qtIdent(schema)}}.{{conn|qtIdent(name)}} {% if cascade %}CASCADE{%endif%};
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{# FETCH lexize functions for TEXT SEARCH TEMPLATE #}
|
||||||
|
{% if lexize %}
|
||||||
|
SELECT
|
||||||
|
proname, nspname
|
||||||
|
FROM
|
||||||
|
pg_proc JOIN pg_namespace n ON n.oid=pronamespace
|
||||||
|
WHERE
|
||||||
|
prorettype=2281
|
||||||
|
AND proargtypes='2281 2281 2281 2281'
|
||||||
|
ORDER BY proname;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# FETCH init functions for TEXT SEARCH TEMPLATE #}
|
||||||
|
{% if init %}
|
||||||
|
SELECT
|
||||||
|
proname, nspname
|
||||||
|
FROM
|
||||||
|
pg_proc JOIN pg_namespace n ON n.oid=pronamespace
|
||||||
|
WHERE
|
||||||
|
prorettype=2281 and proargtypes='2281'
|
||||||
|
ORDER BY
|
||||||
|
proname;
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,12 @@
|
||||||
|
SELECT
|
||||||
|
oid, tmplname as name
|
||||||
|
FROM
|
||||||
|
pg_ts_template tmpl
|
||||||
|
WHERE
|
||||||
|
{% if scid %}
|
||||||
|
tmpl.tmplnamespace = {{scid}}::OID
|
||||||
|
{% elif tid %}
|
||||||
|
tmpl.oid = {{tid}}::OID
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
ORDER BY name
|
|
@ -0,0 +1,27 @@
|
||||||
|
{# Get properties for FTS TEMPLATE #}
|
||||||
|
SELECT
|
||||||
|
tmpl.oid,
|
||||||
|
tmpl.tmplname as name,
|
||||||
|
tmpl.tmplinit,
|
||||||
|
tmpl.tmpllexize,
|
||||||
|
description,
|
||||||
|
tmpl.tmplnamespace AS schema
|
||||||
|
FROM
|
||||||
|
pg_ts_template tmpl
|
||||||
|
LEFT OUTER JOIN pg_description des
|
||||||
|
ON
|
||||||
|
(
|
||||||
|
des.objoid=tmpl.oid
|
||||||
|
AND des.classoid='pg_ts_template'::regclass
|
||||||
|
)
|
||||||
|
WHERE
|
||||||
|
{% if scid %}
|
||||||
|
tmpl.tmplnamespace = {{scid}}::OID
|
||||||
|
{% elif name %}
|
||||||
|
tmpl.tmplname = {{name|qtLiteral}}
|
||||||
|
{% endif %}
|
||||||
|
{% if tid %}
|
||||||
|
AND tmpl.oid = {{tid}}::OID
|
||||||
|
{% endif %}
|
||||||
|
ORDER BY
|
||||||
|
tmpl.tmplname
|
|
@ -0,0 +1,19 @@
|
||||||
|
{# SCHEMA name FETCH statement #}
|
||||||
|
{% 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_template ts
|
||||||
|
ON ts.tmplnamespace = nsp.oid
|
||||||
|
WHERE
|
||||||
|
ts.oid = {{data.id}}::OID
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,41 @@
|
||||||
|
{# Reverse engineered sql for FTS TEMPLATE #}
|
||||||
|
SELECT
|
||||||
|
array_to_string(array_agg(sql), E'\n\n') as sql
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
E'-- Text Search Template: ' || nspname || E'.' || tmpl.tmplname ||
|
||||||
|
E'\n\n-- DROP TEXT SEARCH TEMPLATE ' || nspname || E'.' || tmpl.tmplname ||
|
||||||
|
E'\n\nCREATE TEXT SEARCH TEMPLATE ' || nspname || E'.' || tmpl.tmplname || E' (\n' ||
|
||||||
|
CASE
|
||||||
|
WHEN tmpl.tmplinit != '-'::regclass THEN E' INIT = ' || tmpl.tmplinit || E',\n'
|
||||||
|
ELSE '' END ||
|
||||||
|
E' LEXIZE = ' || tmpl.tmpllexize || E'\n);' ||
|
||||||
|
CASE
|
||||||
|
WHEN a.description IS NOT NULL THEN
|
||||||
|
E'\n\nCOMMENT ON TEXT SEARCH TEMPLATE ' || nspname || E'.' || tmpl.tmplname ||
|
||||||
|
E' IS ' || pg_catalog.quote_literal(description) || E';'
|
||||||
|
ELSE '' END as sql
|
||||||
|
FROM
|
||||||
|
pg_ts_template tmpl
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
des.description as description,
|
||||||
|
des.objoid as descoid
|
||||||
|
FROM
|
||||||
|
pg_description des
|
||||||
|
WHERE
|
||||||
|
des.objoid={{tid}}::OID AND des.classoid='pg_ts_template'::regclass
|
||||||
|
) a ON (a.descoid = tmpl.oid)
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
nspname,
|
||||||
|
nsp.oid as noid
|
||||||
|
FROM
|
||||||
|
pg_namespace nsp
|
||||||
|
WHERE
|
||||||
|
oid = {{scid}}::OID
|
||||||
|
) b ON (b.noid = tmpl.tmplnamespace)
|
||||||
|
WHERE
|
||||||
|
tmpl.oid={{tid}}::OID
|
||||||
|
) as c;
|
|
@ -0,0 +1,22 @@
|
||||||
|
{# UPDATE statement for TEXT SEARCH TEMPLATE #}
|
||||||
|
{% if data %}
|
||||||
|
{% if data.name and data.name != o_data.name %}
|
||||||
|
ALTER TEXT SEARCH TEMPLATE {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(o_data.name)}}
|
||||||
|
RENAME TO {{data.name}};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{#in case of rename, use new fts template name #}
|
||||||
|
{% if data.name and data.name != o_data.name %}
|
||||||
|
{% set name = data.name %}
|
||||||
|
{% else %}
|
||||||
|
{% set name = o_data.name %}
|
||||||
|
{% endif %}
|
||||||
|
{% if data.schema and data.schema != o_data.schema %}
|
||||||
|
ALTER TEXT SEARCH TEMPLATE {{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 TEMPLATE {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
|
||||||
|
IS {{ data.description|qtLiteral }};
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
Loading…
Reference in New Issue