Added support for FTS configuration node
parent
7b2e4fb467
commit
6895da9cbc
|
@ -0,0 +1,939 @@
|
||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# 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 Configuration 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 FtsConfigurationModule(SchemaChildModule):
|
||||||
|
"""
|
||||||
|
class FtsConfigurationModule(SchemaChildModule)
|
||||||
|
|
||||||
|
A module class for FTS Configuration node derived from SchemaChildModule.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
-------
|
||||||
|
* __init__(*args, **kwargs)
|
||||||
|
- Method is used to initialize the FtsConfigurationModule 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 Configuration, when any of the schema
|
||||||
|
node is initialized.
|
||||||
|
|
||||||
|
"""
|
||||||
|
NODE_TYPE = 'fts_configuration'
|
||||||
|
COLLECTION_LABEL = _('FTS Configurations')
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.min_ver = None
|
||||||
|
self.max_ver = None
|
||||||
|
self.manager = None
|
||||||
|
super(FtsConfigurationModule, 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 = FtsConfigurationModule(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FtsConfigurationView(PGChildNodeView):
|
||||||
|
"""
|
||||||
|
class FtsConfigurationView(PGChildNodeView)
|
||||||
|
|
||||||
|
A view class for FTS Configuration node derived from PGChildNodeView.
|
||||||
|
This class is responsible for all the stuff related to view like
|
||||||
|
create/update/delete FTS Configuration,
|
||||||
|
showing properties of node, showing sql in sql pane.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
-------
|
||||||
|
* __init__(**kwargs)
|
||||||
|
- Method is used to initialize the FtsConfigurationView 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 be used to create all the child node within collection.
|
||||||
|
Here it will create all the FTS Configuration 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, cfgid)
|
||||||
|
- This function will show the properties of the selected FTS Configuration node
|
||||||
|
|
||||||
|
* create(gid, sid, did, scid)
|
||||||
|
- This function will create the new FTS Configuration object
|
||||||
|
|
||||||
|
* update(gid, sid, did, scid, cfgid)
|
||||||
|
- This function will update the data for the selected FTS Configuration node
|
||||||
|
|
||||||
|
* delete(self, gid, sid, did, scid, cfgid):
|
||||||
|
- This function will drop the FTS Configuration object
|
||||||
|
|
||||||
|
* msql(gid, sid, did, scid, cfgid)
|
||||||
|
- This function is used to return modified SQL for the selected node
|
||||||
|
|
||||||
|
* get_sql(data, cfgid)
|
||||||
|
- This function will generate sql from model data
|
||||||
|
|
||||||
|
* sql(gid, sid, did, scid, cfgid):
|
||||||
|
- This function will generate sql to show in sql pane for node.
|
||||||
|
|
||||||
|
* parsers(gid, sid, did, scid):
|
||||||
|
- This function will fetch all ftp parsers from the same schema
|
||||||
|
|
||||||
|
* copyConfig():
|
||||||
|
- This function will fetch all existed fts configurations from same schema
|
||||||
|
|
||||||
|
* tokens():
|
||||||
|
- This function will fetch all tokens from fts parser related to node
|
||||||
|
|
||||||
|
* dictionaries():
|
||||||
|
- This function will fetch all dictionaries related to node
|
||||||
|
|
||||||
|
* dependents(gid, sid, did, scid, cfgid):
|
||||||
|
- This function get the dependents and return ajax response for the node.
|
||||||
|
|
||||||
|
* dependencies(self, gid, sid, did, scid, cfgid):
|
||||||
|
- 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': 'cfgid'}
|
||||||
|
]
|
||||||
|
|
||||||
|
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'}],
|
||||||
|
'parsers': [{'get': 'parsers'},
|
||||||
|
{'get': 'parsers'}],
|
||||||
|
'copyConfig': [{'get': 'copyConfig'},
|
||||||
|
{'get': 'copyConfig'}],
|
||||||
|
'tokens': [{'get': 'tokens'}, {'get': 'tokens'}],
|
||||||
|
'dictionaries': [{}, {'get': 'dictionaries'}],
|
||||||
|
})
|
||||||
|
|
||||||
|
def _init_(self, **kwargs):
|
||||||
|
self.conn = None
|
||||||
|
self.template_path = None
|
||||||
|
self.manager = None
|
||||||
|
super(FtsConfigurationView, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def module_js(self):
|
||||||
|
"""
|
||||||
|
Load JS file (fts_configuration.js) for this module.
|
||||||
|
"""
|
||||||
|
return make_response(
|
||||||
|
render_template(
|
||||||
|
"fts_configuration/js/fts_configuration.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_configuration/sql/9.1_plus'
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrap
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def list(self, gid, sid, did, scid):
|
||||||
|
"""
|
||||||
|
List all FTS Configuration 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)
|
||||||
|
|
||||||
|
return ajax_response(
|
||||||
|
response=res['rows'],
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def nodes(self, gid, sid, did, scid):
|
||||||
|
"""
|
||||||
|
Return all FTS Configurations 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_configuration"
|
||||||
|
))
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
data=res,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def node(self, gid, sid, did, scid, cfgid):
|
||||||
|
"""
|
||||||
|
Return FTS Configuration node to generate node
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group Id
|
||||||
|
sid: Server Id
|
||||||
|
did: Database Id
|
||||||
|
scid: Schema Id
|
||||||
|
cfgid: fts Configuration id
|
||||||
|
"""
|
||||||
|
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'nodes.sql']),
|
||||||
|
cfgid=cfgid
|
||||||
|
)
|
||||||
|
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 Configuration node.
|
||||||
|
"""))
|
||||||
|
|
||||||
|
for row in rset['rows']:
|
||||||
|
return make_json_response(
|
||||||
|
data=self.blueprint.generate_browser_node(
|
||||||
|
row['oid'],
|
||||||
|
did,
|
||||||
|
row['name'],
|
||||||
|
icon="icon-fts_configuration"
|
||||||
|
),
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def properties(self, gid, sid, did, scid, cfgid):
|
||||||
|
"""
|
||||||
|
Show properties of FTS Configuration node
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group Id
|
||||||
|
sid: Server Id
|
||||||
|
did: Database Id
|
||||||
|
scid: Schema Id
|
||||||
|
cfgid: fts Configuration id
|
||||||
|
"""
|
||||||
|
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'properties.sql']),
|
||||||
|
scid=scid,
|
||||||
|
cfgid=cfgid
|
||||||
|
)
|
||||||
|
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 Configuration node.
|
||||||
|
"""))
|
||||||
|
|
||||||
|
# In edit mode fetch token/dictionary list also
|
||||||
|
if cfgid:
|
||||||
|
sql = render_template("/".join([self.template_path,
|
||||||
|
'tokenDictList.sql']),
|
||||||
|
cfgid=cfgid)
|
||||||
|
|
||||||
|
status, rset = self.conn.execute_dict(sql)
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=rset)
|
||||||
|
|
||||||
|
res['rows'][0]['tokens'] = rset['rows']
|
||||||
|
|
||||||
|
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 Configuration object
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Mandatory fields to create a new FTS Configuration
|
||||||
|
required_args = [
|
||||||
|
'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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Either copy config or parser must be present in data
|
||||||
|
if 'copy_config' not in data and 'prsname' not in data:
|
||||||
|
return make_json_response(
|
||||||
|
status=410,
|
||||||
|
success=0,
|
||||||
|
errormsg=_(
|
||||||
|
"provide atleast copy config or parser"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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 cfgid 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, cfgid = self.conn.execute_scalar(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=cfgid)
|
||||||
|
|
||||||
|
return jsonify(
|
||||||
|
node=self.blueprint.generate_browser_node(
|
||||||
|
cfgid,
|
||||||
|
did,
|
||||||
|
data['name'],
|
||||||
|
icon="icon-fts_configuration"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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, cfgid):
|
||||||
|
"""
|
||||||
|
This function will update FTS Configuration node
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
:param cfgid: fts Configuration id
|
||||||
|
"""
|
||||||
|
data = request.form if request.form else json.loads(
|
||||||
|
request.data.decode())
|
||||||
|
|
||||||
|
# Fetch sql query to update fts Configuration
|
||||||
|
sql = self.get_sql(gid, sid, did, scid, data, cfgid)
|
||||||
|
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 cfgid is not None:
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'nodes.sql']),
|
||||||
|
cfgid=cfgid,
|
||||||
|
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 Configuration node to update.
|
||||||
|
"""))
|
||||||
|
|
||||||
|
data = res['rows'][0]
|
||||||
|
return make_json_response(
|
||||||
|
success=1,
|
||||||
|
info="FTS Configuration Updated.",
|
||||||
|
data={
|
||||||
|
'id': cfgid,
|
||||||
|
'sid': sid,
|
||||||
|
'gid': gid,
|
||||||
|
'did': did,
|
||||||
|
'scid': scid
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# In case FTS Configuration node is not present
|
||||||
|
else:
|
||||||
|
return make_json_response(
|
||||||
|
success=1,
|
||||||
|
info="Nothing to update",
|
||||||
|
data={
|
||||||
|
'id': cfgid,
|
||||||
|
'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, cfgid):
|
||||||
|
"""
|
||||||
|
This function will drop the FTS Configuration object
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
:param cfgid: FTS Configuration 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 Configuration from cfgid
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'get_name.sql']),
|
||||||
|
cfgid=cfgid
|
||||||
|
)
|
||||||
|
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 Configuration node to delete.
|
||||||
|
"""))
|
||||||
|
|
||||||
|
# Drop FTS Configuration
|
||||||
|
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 Configuration dropped"),
|
||||||
|
data={
|
||||||
|
'id': cfgid,
|
||||||
|
'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, cfgid=None):
|
||||||
|
"""
|
||||||
|
This function returns modified SQL
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
:param cfgid: FTS Configuration id
|
||||||
|
"""
|
||||||
|
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, cfgid)
|
||||||
|
|
||||||
|
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, cfgid=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 cfgid: fts Configuration id
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Fetch sql for update
|
||||||
|
if cfgid is not None:
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'properties.sql']),
|
||||||
|
cfgid=cfgid,
|
||||||
|
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 Configuration 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 '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 parsers(self, gid, sid, did, scid):
|
||||||
|
"""
|
||||||
|
This function will return fts parsers list for FTS Configuration
|
||||||
|
: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, 'parser.sql']),
|
||||||
|
parser=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 parser control while creating a new FTS Configuration
|
||||||
|
res = [{'label':'', 'value':''}]
|
||||||
|
for row in rset['rows']:
|
||||||
|
if row['schemaoid'] > datlastsysoid:
|
||||||
|
row['prsname'] = row['nspname'] + '.' + row['prsname']
|
||||||
|
|
||||||
|
res.append({'label': row['prsname'],
|
||||||
|
'value': row['prsname']})
|
||||||
|
return make_json_response(
|
||||||
|
data=res,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def copyConfig(self, gid, sid, did, scid):
|
||||||
|
"""
|
||||||
|
This function will return copy config list for FTS Configuration
|
||||||
|
: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, 'copy_config.sql']),
|
||||||
|
copy_config=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 copy_config control while creating a new FTS Configuration
|
||||||
|
res = [{'label': '', 'value': ''}]
|
||||||
|
for row in rset['rows']:
|
||||||
|
if row['oid'] > datlastsysoid:
|
||||||
|
row['cfgname'] = row['nspname'] + '.' + row['cfgname']
|
||||||
|
|
||||||
|
res.append({'label': row['cfgname'],
|
||||||
|
'value': row['cfgname']})
|
||||||
|
return make_json_response(
|
||||||
|
data=res,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def tokens(self, gid, sid, did, scid, cfgid=None):
|
||||||
|
"""
|
||||||
|
This function will return token list of fts parser node related to
|
||||||
|
current FTS Configuration node
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
:param cfgid: fts configuration id
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
res = []
|
||||||
|
if cfgid is not None:
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'parser.sql']),
|
||||||
|
cfgid=cfgid
|
||||||
|
)
|
||||||
|
status, parseroid = self.conn.execute_scalar(sql)
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=parseroid)
|
||||||
|
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'tokens.sql']),
|
||||||
|
parseroid=parseroid
|
||||||
|
)
|
||||||
|
status, rset = self.conn.execute_dict(sql)
|
||||||
|
|
||||||
|
for row in rset['rows']:
|
||||||
|
res.append({'label': row['alias'],
|
||||||
|
'value': row['alias']})
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
data=res,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.exception(e)
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def dictionaries(self, gid, sid, did, scid, cfgid=None):
|
||||||
|
"""
|
||||||
|
This function will return dictionary list for FTS Configuration
|
||||||
|
:param gid: group id
|
||||||
|
:param sid: server id
|
||||||
|
:param did: database id
|
||||||
|
:param scid: schema id
|
||||||
|
"""
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path,'dictionaries.sql'])
|
||||||
|
)
|
||||||
|
status, rset = self.conn.execute_dict(sql)
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=rset)
|
||||||
|
|
||||||
|
res = []
|
||||||
|
for row in rset['rows']:
|
||||||
|
res.append({'label': row['dictname'],
|
||||||
|
'value': row['dictname']})
|
||||||
|
return make_json_response(
|
||||||
|
data=res,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def sql(self, gid, sid, did, scid, cfgid):
|
||||||
|
"""
|
||||||
|
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 cfgid: FTS Configuration id
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([self.template_path, 'sql.sql']),
|
||||||
|
cfgid=cfgid,
|
||||||
|
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 Configuration!\n{0}"
|
||||||
|
).format(
|
||||||
|
res
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if res is None:
|
||||||
|
return gone(
|
||||||
|
_(
|
||||||
|
"ERROR: Couldn't generate reversed engineered query for FTS Configuration 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, cfgid):
|
||||||
|
"""
|
||||||
|
This function get the dependents and return ajax response
|
||||||
|
for the FTS Configuration node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
cfgid: FTS Configuration ID
|
||||||
|
"""
|
||||||
|
dependents_result = self.get_dependents(self.conn, cfgid)
|
||||||
|
return ajax_response(
|
||||||
|
response=dependents_result,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def dependencies(self, gid, sid, did, scid, cfgid):
|
||||||
|
"""
|
||||||
|
This function get the dependencies and return ajax response
|
||||||
|
for the FTS Configuration node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
cfgid: FTS Configuration ID
|
||||||
|
"""
|
||||||
|
dependencies_result = self.get_dependencies(self.conn, cfgid)
|
||||||
|
return ajax_response(
|
||||||
|
response=dependencies_result,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
FtsConfigurationView.register_node_view(blueprint)
|
Binary file not shown.
After Width: | Height: | Size: 369 B |
Binary file not shown.
After Width: | Height: | Size: 346 B |
|
@ -0,0 +1,579 @@
|
||||||
|
define(
|
||||||
|
['jquery', 'underscore', 'underscore.string', 'pgadmin',
|
||||||
|
'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
|
||||||
|
function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||||
|
|
||||||
|
|
||||||
|
// Model for tokens control
|
||||||
|
var TokenModel = pgAdmin.Browser.Node.Model.extend({
|
||||||
|
defaults: {
|
||||||
|
token: undefined,
|
||||||
|
dictname: undefined
|
||||||
|
},
|
||||||
|
keys: ['token'],
|
||||||
|
// Define the schema for the token/dictionary list
|
||||||
|
schema: [{
|
||||||
|
id: 'token', label:'Token', type:'text', group: null,
|
||||||
|
cellHeaderClasses:'width_percent_50', editable: true,
|
||||||
|
editable: false, cell: 'string', url: 'tokens'
|
||||||
|
},{
|
||||||
|
id: 'dictname', label: 'Dictionaries', type: 'text', group:null,
|
||||||
|
cellHeaderClasses:'width_percent_50', editable: true,
|
||||||
|
cell:Backgrid.Extension.MultiSelectAjaxCell, url: 'dictionaries'
|
||||||
|
}],
|
||||||
|
// Validation for token and dictionary list
|
||||||
|
validate: function() {
|
||||||
|
// Clear any existing errors.
|
||||||
|
var msg;
|
||||||
|
this.errorModel.clear();
|
||||||
|
var token = this.get('token');
|
||||||
|
var dictionary = this.get('dictname');
|
||||||
|
|
||||||
|
if (_.isNull(token) ||
|
||||||
|
_.isUndefined(token) ||
|
||||||
|
String(token).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = '{{ _('Token can not be empty!') }}';
|
||||||
|
this.errorModel.set('token',msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isNull(dictionary) ||
|
||||||
|
_.isUndefined(dictionary) ||
|
||||||
|
String(dictionary).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = '{{ _('Dictionary name can not be empty!') }}';
|
||||||
|
this.errorModel.set('dictname',msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Customized control for token control
|
||||||
|
var TokenControl = Backform.TokenControl =
|
||||||
|
Backform.UniqueColCollectionControl.extend({
|
||||||
|
|
||||||
|
initialize: function(opts) {
|
||||||
|
Backform.UniqueColCollectionControl.prototype.initialize.apply(
|
||||||
|
this, arguments
|
||||||
|
);
|
||||||
|
|
||||||
|
var self = that = this,
|
||||||
|
node = 'fts_configuration',
|
||||||
|
headerSchema = [{
|
||||||
|
id: 'token', label:'', type:'text', url: 'tokens',
|
||||||
|
node:'fts_configuration', canAdd: true, 'url_with_id': true,
|
||||||
|
|
||||||
|
// Defining control for tokens dropdown control in header
|
||||||
|
control: Backform.NodeAjaxOptionsControl.extend({
|
||||||
|
formatter: Backform.NodeAjaxOptionsControl.prototype.formatter,
|
||||||
|
initialize: function() {
|
||||||
|
Backform.NodeAjaxOptionsControl.prototype.initialize.apply(
|
||||||
|
this,
|
||||||
|
arguments
|
||||||
|
);
|
||||||
|
var self = this,
|
||||||
|
url = self.field.get('url') || self.defaults.url,
|
||||||
|
m = self.model.top || self.model;
|
||||||
|
|
||||||
|
/* Fetch the tokens/dict list from 'that' node.
|
||||||
|
* Here 'that' refers to unique collection control where
|
||||||
|
* 'self' refers to nodeAjaxOptions control for dictionary
|
||||||
|
*/
|
||||||
|
var cfgid = that.model.get('oid');
|
||||||
|
if (url) {
|
||||||
|
var node = this.field.get('schema_node'),
|
||||||
|
node_info = this.field.get('node_info'),
|
||||||
|
full_url = node.generate_url.apply(
|
||||||
|
node, [
|
||||||
|
null, url, this.field.get('node_data'),
|
||||||
|
this.field.get('url_with_id') || false,
|
||||||
|
node_info
|
||||||
|
]),
|
||||||
|
cache_level = this.field.get('cache_level') || node.type,
|
||||||
|
cache_node = this.field.get('cache_node');
|
||||||
|
|
||||||
|
cache_node = (cache_node &&
|
||||||
|
pgAdmin.Browser.Nodes['cache_node'])
|
||||||
|
|| node;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We needs to check, if we have already cached data
|
||||||
|
* for this url. If yes - use it, and do not bother about
|
||||||
|
* fetching it again.
|
||||||
|
*/
|
||||||
|
var data = cache_node.cache(url, node_info, cache_level);
|
||||||
|
|
||||||
|
// Fetch token/dictionary list
|
||||||
|
if (this.field.get('version_compatible') &&
|
||||||
|
(_.isUndefined(data) || _.isNull(data))) {
|
||||||
|
m.trigger('pgadmin:view:fetching', m, self.field);
|
||||||
|
$.ajax({
|
||||||
|
async: false,
|
||||||
|
url: full_url,
|
||||||
|
success: function(res) {
|
||||||
|
/*
|
||||||
|
* We will cache this data for short period of time for
|
||||||
|
* avoiding same calls.
|
||||||
|
*/
|
||||||
|
data = cache_node.cache(url,
|
||||||
|
node_info,
|
||||||
|
cache_level,
|
||||||
|
res.data
|
||||||
|
);
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
m.trigger('pgadmin:view:fetch:error', m, self.field);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m.trigger('pgadmin:view:fetched', m, self.field);
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is feasible that the data may not have been fetched.
|
||||||
|
data = (data && data.data) || [];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transform the data
|
||||||
|
*/
|
||||||
|
transform = (this.field.get('transform')
|
||||||
|
|| self.defaults.transform);
|
||||||
|
if (transform && _.isFunction(transform)) {
|
||||||
|
self.field.set('options', transform.bind(self, data));
|
||||||
|
} else {
|
||||||
|
self.field.set('options', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
// Select2 control for adding new tokens
|
||||||
|
select2: {
|
||||||
|
allowClear: true, width: 'style',
|
||||||
|
placeholder: 'Select token'
|
||||||
|
},
|
||||||
|
first_empty: true,
|
||||||
|
disabled: function(m) {
|
||||||
|
return _.isUndefined(self.model.get('oid'));
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
headerDefaults = {token: null},
|
||||||
|
// Grid columns backgrid
|
||||||
|
gridCols = ['token', 'dictname'];
|
||||||
|
|
||||||
|
// Creating model for header control which is used to add new tokens
|
||||||
|
self.headerData = new (Backbone.Model.extend({
|
||||||
|
defaults: headerDefaults,
|
||||||
|
schema: headerSchema
|
||||||
|
}))({});
|
||||||
|
|
||||||
|
// Creating view from header schema in tokens control
|
||||||
|
var headerGroups = Backform.generateViewSchema(
|
||||||
|
self.field.get('node_info'), self.headerData, 'create',
|
||||||
|
self.field.get('schema_node'), self.field.get('node_data')
|
||||||
|
),
|
||||||
|
fields = [];
|
||||||
|
|
||||||
|
_.each(headerGroups, function(o) {
|
||||||
|
fields = fields.concat(o.fields);
|
||||||
|
});
|
||||||
|
self.headerFields = new Backform.Fields(fields);
|
||||||
|
|
||||||
|
// creating grid using grid columns
|
||||||
|
self.gridSchema = Backform.generateGridColumnsFromModel(
|
||||||
|
self.field.get('node_info'), self.field.get('model'),
|
||||||
|
'edit', gridCols, self.field.get('schema_node')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Providing behaviour control functions to header and grid control
|
||||||
|
self.controls = [];
|
||||||
|
self.listenTo(self.headerData, "change", self.headerDataChanged);
|
||||||
|
self.listenTo(self.headerData, "select2", self.headerDataChanged);
|
||||||
|
self.listenTo(self.collection, "add", self.onAddorRemoveTokens);
|
||||||
|
self.listenTo(self.collection, "remove", self.onAddorRemoveTokens);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Template for creating header view
|
||||||
|
generateHeader: function(data) {
|
||||||
|
var header = [
|
||||||
|
'<div class="subnode-header-form">',
|
||||||
|
' <div class="container-fluid">',
|
||||||
|
' <div class="row">',
|
||||||
|
' <div class="col-xs-3">',
|
||||||
|
' <label class="control-label"><%-token_label%></label>',
|
||||||
|
' </div>',
|
||||||
|
' <div class="col-xs-6" header="token"></div>',
|
||||||
|
' <div class="col-xs-2">',
|
||||||
|
' <button class="btn-sm btn-default add" <%=canAdd ? "" : "disabled=\'disabled\'"%> ><%-add_label%></buttton>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
'</div>',].join("\n")
|
||||||
|
|
||||||
|
_.extend(data, {
|
||||||
|
token_label: '{{ _('Tokens')}}',
|
||||||
|
add_label: '{{ _('ADD')}}'
|
||||||
|
});
|
||||||
|
|
||||||
|
var self = this,
|
||||||
|
headerTmpl = _.template(header),
|
||||||
|
$header = $(headerTmpl(data)),
|
||||||
|
controls = this.controls;
|
||||||
|
|
||||||
|
self.headerFields.each(function(field) {
|
||||||
|
var control = new (field.get("control"))({
|
||||||
|
field: field,
|
||||||
|
model: self.headerData
|
||||||
|
});
|
||||||
|
|
||||||
|
$header.find('div[header="' + field.get('name') + '"]').append(
|
||||||
|
control.render().$el
|
||||||
|
);
|
||||||
|
|
||||||
|
control.$el.find('.control-label').remove();
|
||||||
|
controls.push(control);
|
||||||
|
});
|
||||||
|
|
||||||
|
// We should not show add button in properties mode
|
||||||
|
if (data.mode == 'properties') {
|
||||||
|
$header.find("button.add").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable add button in token control in create mode
|
||||||
|
if(data.mode == 'create') {
|
||||||
|
$header.find("button.add").attr('disabled', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.$header = $header;
|
||||||
|
return $header;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Providing event handler for add button in header
|
||||||
|
events: _.extend(
|
||||||
|
{}, Backform.UniqueColCollectionControl.prototype.events,
|
||||||
|
{'click button.add': 'addTokens'}
|
||||||
|
),
|
||||||
|
|
||||||
|
// Show token/dictionary grid
|
||||||
|
showGridControl: function(data) {
|
||||||
|
|
||||||
|
var self = this,
|
||||||
|
titleTmpl = _.template("<div class='subnode-header'></div>"),
|
||||||
|
$gridBody = $("<div></div>", {
|
||||||
|
class:'pgadmin-control-group backgrid form-group col-xs-12 object subnode'
|
||||||
|
}).append(
|
||||||
|
titleTmpl({label: data.label})
|
||||||
|
);
|
||||||
|
|
||||||
|
$gridBody.append(self.generateHeader(data));
|
||||||
|
|
||||||
|
var gridColumns = _.clone(this.gridSchema.columns);
|
||||||
|
|
||||||
|
// Insert Delete Cell into Grid
|
||||||
|
if (data.disabled == false && data.canDelete) {
|
||||||
|
gridColumns.unshift({
|
||||||
|
name: "pg-backform-delete", label: "",
|
||||||
|
cell: Backgrid.Extension.DeleteCell,
|
||||||
|
editable: false, cell_priority: -1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.grid) {
|
||||||
|
self.grid.remove();
|
||||||
|
self.grid.null;
|
||||||
|
}
|
||||||
|
// Initialize a new Grid instance
|
||||||
|
var grid = self.grid = new Backgrid.Grid({
|
||||||
|
columns: gridColumns,
|
||||||
|
collection: self.collection,
|
||||||
|
className: "backgrid table-bordered"
|
||||||
|
});
|
||||||
|
self.$grid = grid.render().$el;
|
||||||
|
|
||||||
|
$gridBody.append(self.$grid);
|
||||||
|
|
||||||
|
// Find selected dictionaries in grid and show it all together
|
||||||
|
setTimeout(function() {
|
||||||
|
self.headerData.set({
|
||||||
|
'token': self.$header.find(
|
||||||
|
'div[header="token"] select'
|
||||||
|
).val()
|
||||||
|
}, {silent:true}
|
||||||
|
);
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
// Render node grid
|
||||||
|
return $gridBody;
|
||||||
|
},
|
||||||
|
|
||||||
|
// When user change the header control to add a new token
|
||||||
|
headerDataChanged: function() {
|
||||||
|
var self = this, val,
|
||||||
|
data = this.headerData.toJSON(),
|
||||||
|
inSelected = (_.isEmpty(data) || _.isUndefined(data)),
|
||||||
|
checkVars = ['token'];
|
||||||
|
|
||||||
|
if (!self.$header) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.$header.find('button.add').prop('disabled', inSelected);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get called when user click on add button header
|
||||||
|
addTokens: function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
var self = this,
|
||||||
|
token = self.headerData.get('token');
|
||||||
|
|
||||||
|
if (!token || token == '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var coll = self.model.get(self.field.get('name')),
|
||||||
|
m = new (self.field.get('model'))(
|
||||||
|
self.headerData.toJSON(), {
|
||||||
|
silent: true, top: self.model.top,
|
||||||
|
collection: coll, handler: coll
|
||||||
|
}),
|
||||||
|
checkVars = ['token'],
|
||||||
|
idx = -1;
|
||||||
|
|
||||||
|
// Find if token exists in grid
|
||||||
|
self.collection.each(function(m) {
|
||||||
|
_.each(checkVars, function(v) {
|
||||||
|
val = m.get(v);
|
||||||
|
if(val == token) {
|
||||||
|
idx = coll.indexOf(m);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// remove 'm' if duplicate value found.
|
||||||
|
if (idx == -1) {
|
||||||
|
coll.add(m);
|
||||||
|
idx = coll.indexOf(m);
|
||||||
|
}
|
||||||
|
self.$grid.find('.new').removeClass('new');
|
||||||
|
var newRow = self.grid.body.rows[idx].$el;
|
||||||
|
newRow.addClass("new");
|
||||||
|
//$(newRow).pgMakeVisible('table-bordered');
|
||||||
|
$(newRow).pgMakeVisible('backform-tab');
|
||||||
|
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// When user delete token/dictionary entry from grid
|
||||||
|
onAddorRemoveTokens: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for collection to be updated before checking for the button to
|
||||||
|
* be enabled, or not.
|
||||||
|
*/
|
||||||
|
setTimeout(function() {
|
||||||
|
self.collection.trigger('pgadmin:tokens:updated', self.collection);
|
||||||
|
self.headerDataChanged();
|
||||||
|
}, 10);
|
||||||
|
},
|
||||||
|
|
||||||
|
// When control is about to destroy
|
||||||
|
remove: function() {
|
||||||
|
/*
|
||||||
|
* Stop listening the events registered by this control.
|
||||||
|
*/
|
||||||
|
this.stopListening(this.headerData, "change", this.headerDataChanged);
|
||||||
|
this.listenTo(this.headerData, "select2", this.headerDataChanged);
|
||||||
|
this.listenTo(this.collection, "remove", this.onAddorRemoveTokens);
|
||||||
|
|
||||||
|
TokenControl.__super__.remove.apply(this, arguments);
|
||||||
|
|
||||||
|
// Remove the header model
|
||||||
|
delete (this.headerData);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Extend the collection class for FTS Configuration
|
||||||
|
if (!pgBrowser.Nodes['coll-fts_configuration']) {
|
||||||
|
var fts_configurations = pgAdmin.Browser.Nodes['coll-fts_configuration'] =
|
||||||
|
pgAdmin.Browser.Collection.extend({
|
||||||
|
node: 'fts_configuration',
|
||||||
|
label: '{{ _('FTS Configurations') }}',
|
||||||
|
type: 'coll-fts_configuration',
|
||||||
|
columns: ['name', 'description']
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extend the node class for FTS Configuration
|
||||||
|
if (!pgBrowser.Nodes['fts_configuration']) {
|
||||||
|
pgAdmin.Browser.Nodes['fts_configuration'] = pgAdmin.Browser.Node.extend({
|
||||||
|
parent_type: ['schema', 'catalog'],
|
||||||
|
type: 'fts_configuration',
|
||||||
|
sqlAlterHelp: 'sql-altertsconfig.html',
|
||||||
|
sqlCreateHelp: 'sql-createtsconfig.html',
|
||||||
|
canDrop: true,
|
||||||
|
canDropCascade: true,
|
||||||
|
label: '{{ _('FTS Configuration') }}',
|
||||||
|
hasSQL: true,
|
||||||
|
hasDepends: true,
|
||||||
|
Init: function() {
|
||||||
|
|
||||||
|
// Avoid multiple registration of menus
|
||||||
|
if (this.initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
|
|
||||||
|
// Add context menus for FTS Configuration
|
||||||
|
pgBrowser.add_menus([{
|
||||||
|
name: 'create_fts_configuration_on_schema', node: 'schema',
|
||||||
|
module: this, category: 'create', priority: 4,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
label: '{{_('FTS Configuration...')}}',
|
||||||
|
icon: 'wcTabIcon icon-fts_configuration', data: {action: 'create'}
|
||||||
|
},{
|
||||||
|
name: 'create_fts_configuration_on_coll', module: this, priority: 4,
|
||||||
|
node: 'coll-fts_configuration', applies: ['object', 'context'],
|
||||||
|
callback: 'show_obj_properties', category: 'create',
|
||||||
|
label: '{{ _('FTS Configuration...') }}', data: {action: 'create'},
|
||||||
|
icon: 'wcTabIcon icon-fts_configuration'
|
||||||
|
},{
|
||||||
|
name: 'create_fts_configuration', node: 'fts_configuration',
|
||||||
|
module: this, applies: ['object', 'context'],
|
||||||
|
callback: 'show_obj_properties', category: 'create', priority: 4,
|
||||||
|
label: '{{_('FTS Configuration...')}}', data: {action: 'create'},
|
||||||
|
icon: 'wcTabIcon icon-fts_configuration'
|
||||||
|
}]);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Defining model for FTS Configuration node
|
||||||
|
model: pgAdmin.Browser.Node.Model.extend({
|
||||||
|
defaults: {
|
||||||
|
name: undefined, // FTS Configuration name
|
||||||
|
owner: undefined, // FTS Configuration owner
|
||||||
|
description: undefined, // Comment on FTS Configuration
|
||||||
|
schema: undefined, // Schema name FTS Configuration belongs to
|
||||||
|
prsname: undefined, // FTS parser list for FTS Configuration node
|
||||||
|
copy_config: undefined, // FTS configuration list to copy from
|
||||||
|
tokens: undefined // token/dictionary pair list for node
|
||||||
|
},
|
||||||
|
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 Configuration
|
||||||
|
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, select2: { allowClear: false }
|
||||||
|
},{
|
||||||
|
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: 'prsname', label: '{{ _('Parser')}}',type: 'text',
|
||||||
|
url: 'parsers', first_empty: true,
|
||||||
|
group: '{{ _('Definition') }}', control: 'node-ajax-options',
|
||||||
|
deps: ['copy_config'],
|
||||||
|
//disable parser when user select copy_config manually and vica-versa
|
||||||
|
disabled: function(m) {
|
||||||
|
var copy_config = m.get('copy_config');
|
||||||
|
return m.isNew() &&
|
||||||
|
(_.isNull(copy_config) ||
|
||||||
|
_.isUndefined(copy_config) ||
|
||||||
|
copy_config === '') ? false : true;
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'copy_config', label: '{{ _('Copy Config')}}',type: 'text',
|
||||||
|
mode: ['create'], group: '{{ _('Definition') }}',
|
||||||
|
control: 'node-ajax-options', url: 'copyConfig', deps: ['prsname'],
|
||||||
|
|
||||||
|
//disable copy_config when user select parser manually and vica-versa
|
||||||
|
disabled: function(m) {
|
||||||
|
var parser = m.get('prsname');
|
||||||
|
return m.isNew() &&
|
||||||
|
(_.isNull(parser) ||
|
||||||
|
_.isUndefined(parser) ||
|
||||||
|
parser === '') ? false : true;
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'tokens', label: '{{ _('Tokens') }}', type: 'collection',
|
||||||
|
group: '{{ _('Tokens') }}', control: TokenControl,
|
||||||
|
model: TokenModel, columns: ['token', 'dictionary'],
|
||||||
|
uniqueCol : ['token'], mode: ['create','edit'],
|
||||||
|
canAdd: true, canEdit: false, canDelete: true
|
||||||
|
}],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Triggers control specific error messages for name,
|
||||||
|
* copy_config/parser and schema, if any one of them is not specified
|
||||||
|
* while creating new fts configuration
|
||||||
|
*/
|
||||||
|
validate: function(keys){
|
||||||
|
var msg;
|
||||||
|
var name = this.get('name');
|
||||||
|
var parser = this.get('prsname');
|
||||||
|
var copy_config_or_parser = !(parser === '' ||
|
||||||
|
_.isUndefined(parser) ||
|
||||||
|
_.isNull(parser)) ?
|
||||||
|
this.get('prsname') : this.get('copy_config');
|
||||||
|
var schema = this.get('schema');
|
||||||
|
|
||||||
|
// Clear the existing error model
|
||||||
|
this.errorModel.clear();
|
||||||
|
this.trigger('on-status-clear');
|
||||||
|
|
||||||
|
// Validate the name
|
||||||
|
if (_.isUndefined(name) ||
|
||||||
|
_.isNull(name) ||
|
||||||
|
String(name).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = '{{ _('Name must be specified!') }}';
|
||||||
|
this.errorModel.set('name', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate parser or copy_config
|
||||||
|
else if (_.isUndefined(copy_config_or_parser) ||
|
||||||
|
_.isNull(copy_config_or_parser) ||
|
||||||
|
String(copy_config_or_parser).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = '{{ _('Select parser or configuration to copy!') }}';
|
||||||
|
this.errorModel.set('parser', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate schema
|
||||||
|
else if (_.isUndefined(schema) ||
|
||||||
|
_.isNull(schema) ||
|
||||||
|
String(schema).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = '{{ _('Schema must be selected!') }}';
|
||||||
|
this.errorModel.set('schema', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return pgBrowser.Nodes['coll-fts_configuration'];
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
{# FETCH copy config for FTS CONFIGURATION #}
|
||||||
|
{% if copy_config %}
|
||||||
|
SELECT
|
||||||
|
cfg.oid,
|
||||||
|
cfgname,
|
||||||
|
nspname,
|
||||||
|
n.oid as schemaoid
|
||||||
|
FROM
|
||||||
|
pg_ts_config cfg
|
||||||
|
JOIN pg_namespace n
|
||||||
|
ON n.oid=cfgnamespace
|
||||||
|
ORDER BY
|
||||||
|
nspname,
|
||||||
|
cfgname
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{# CREATE FTS CONFIGURATION Statement #}
|
||||||
|
{% if data and data.schema and data.name %}
|
||||||
|
CREATE TEXT SEARCH CONFIGURATION {{ conn|qtIdent(data.schema, data.name) }} (
|
||||||
|
{% if 'copy_config' in data and data.copy_config != '' %}
|
||||||
|
COPY={{ data.copy_config }}
|
||||||
|
{% elif 'prsname' in data and data.prsname != '' %}
|
||||||
|
PARSER = {{ data.prsname }}
|
||||||
|
{% endif %}
|
||||||
|
);
|
||||||
|
|
||||||
|
{% if 'owner' in data and data.owner != '' %}
|
||||||
|
ALTER TEXT SEARCH CONFIGURATION {{ conn|qtIdent(data.schema, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# Description for FTS_CONFIGURATION #}
|
||||||
|
{% if data.description %}
|
||||||
|
COMMENT ON TEXT SEARCH CONFIGURATION {{ conn|qtIdent(data.schema, data.name) }}
|
||||||
|
IS {{ data.description|qtLiteral }};
|
||||||
|
{% endif %}{% endif %}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{# DROP FTS CONFIGURATION Statement #}
|
||||||
|
{% if schema and name %}
|
||||||
|
DROP TEXT SEARCH CONFIGURATION {{conn|qtIdent(schema)}}.{{conn|qtIdent(name)}} {% if cascade %}CASCADE{%endif%};
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{# FETCH DICTIONARIES statement #}
|
||||||
|
SELECT
|
||||||
|
dictname
|
||||||
|
FROM
|
||||||
|
pg_ts_dict
|
||||||
|
ORDER BY
|
||||||
|
dictname
|
|
@ -0,0 +1,17 @@
|
||||||
|
{# GET FTS CONFIGURATION name #}
|
||||||
|
{% if cfgid %}
|
||||||
|
SELECT
|
||||||
|
cfg.cfgname as name,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
nspname
|
||||||
|
FROM
|
||||||
|
pg_namespace
|
||||||
|
WHERE
|
||||||
|
oid = cfg.cfgnamespace
|
||||||
|
) as schema
|
||||||
|
FROM
|
||||||
|
pg_ts_config cfg
|
||||||
|
WHERE
|
||||||
|
cfg.oid = {{cfgid}}::OID;
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{# FETCH FTS CONFIGURATION NAME statement #}
|
||||||
|
SELECT
|
||||||
|
oid, cfgname as name
|
||||||
|
FROM
|
||||||
|
pg_ts_config cfg
|
||||||
|
WHERE
|
||||||
|
{% if scid %}
|
||||||
|
cfg.cfgnamespace = {{scid}}::OID
|
||||||
|
{% elif cfgid %}
|
||||||
|
cfg.oid = {{cfgid}}::OID
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
ORDER BY name
|
|
@ -0,0 +1,24 @@
|
||||||
|
{# PARSER name from FTS CONFIGURATION OID #}
|
||||||
|
{% if cfgid %}
|
||||||
|
SELECT
|
||||||
|
cfgparser
|
||||||
|
FROM
|
||||||
|
pg_ts_config
|
||||||
|
where
|
||||||
|
oid = {{cfgid}}::OID
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{# PARSER list #}
|
||||||
|
{% if parser %}
|
||||||
|
SELECT
|
||||||
|
prsname,
|
||||||
|
nspname,
|
||||||
|
n.oid as schemaoid
|
||||||
|
FROM
|
||||||
|
pg_ts_parser
|
||||||
|
JOIN pg_namespace n
|
||||||
|
ON n.oid=prsnamespace
|
||||||
|
ORDER BY
|
||||||
|
prsname;
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,25 @@
|
||||||
|
{# FETCH properties for FTS CONFIGURATION #}
|
||||||
|
SELECT
|
||||||
|
cfg.oid,
|
||||||
|
cfg.cfgname as name,
|
||||||
|
pg_get_userbyid(cfg.cfgowner) as owner,
|
||||||
|
cfg.cfgparser as parser,
|
||||||
|
cfg.cfgnamespace as schema,
|
||||||
|
parser.prsname as prsname,
|
||||||
|
description
|
||||||
|
FROM
|
||||||
|
pg_ts_config cfg
|
||||||
|
LEFT OUTER JOIN pg_ts_parser parser
|
||||||
|
ON parser.oid=cfg.cfgparser
|
||||||
|
LEFT OUTER JOIN pg_description des
|
||||||
|
ON (des.objoid=cfg.oid AND des.classoid='pg_ts_config'::regclass)
|
||||||
|
WHERE
|
||||||
|
{% if scid %}
|
||||||
|
cfg.cfgnamespace = {{scid}}::OID
|
||||||
|
{% elif name %}
|
||||||
|
cfg.cfgname = {{name|qtLiteral}}
|
||||||
|
{% endif %}
|
||||||
|
{% if cfgid %}
|
||||||
|
AND cfg.oid = {{cfgid}}::OID
|
||||||
|
{% endif %}
|
||||||
|
ORDER BY cfg.cfgname
|
|
@ -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_config cfg
|
||||||
|
ON cfg.cfgnamespace = nsp.oid
|
||||||
|
WHERE
|
||||||
|
cfg.oid = {{data.id}}::OID
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,76 @@
|
||||||
|
{# REVERSED ENGINEERED SQL FOR FTS CONFIGURATION #}
|
||||||
|
{% if cfgid and scid %}
|
||||||
|
SELECT
|
||||||
|
array_to_string(array_agg(sql), E'\n\n') as sql
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
E'-- Text Search CONFIGURATION: ' || quote_ident(nspname) || E'.'
|
||||||
|
|| quote_ident(cfg.cfgname) ||
|
||||||
|
E'\n\n-- DROP TEXT SEARCH CONFIGURATION ' || quote_ident(nspname) ||
|
||||||
|
E'.' || quote_ident(cfg.cfgname) ||
|
||||||
|
E'\n\nCREATE TEXT SEARCH CONFIGURATION ' || quote_ident(nspname) ||
|
||||||
|
E'.' || quote_ident(cfg.cfgname) || E' (\n' ||
|
||||||
|
E'\tPARSER = ' || parsername ||
|
||||||
|
E'\n);' ||
|
||||||
|
CASE
|
||||||
|
WHEN description IS NOT NULL THEN
|
||||||
|
E'\n\nCOMMENT ON TEXT SEARCH CONFIGURATION ' ||
|
||||||
|
quote_ident(nspname) || E'.' || quote_ident(cfg.cfgname) ||
|
||||||
|
E' IS ' || pg_catalog.quote_literal(description) || E';'
|
||||||
|
ELSE ''
|
||||||
|
END || E'\n' ||
|
||||||
|
|
||||||
|
array_to_string(
|
||||||
|
array(
|
||||||
|
SELECT
|
||||||
|
'ALTER TEXT SEARCH CONFIGURATION ' || quote_ident(nspname) ||
|
||||||
|
E'.' || quote_ident(cfg.cfgname) || ' ADD MAPPING FOR ' ||
|
||||||
|
t.alias || ' WITH ' ||
|
||||||
|
array_to_string(array_agg(dict.dictname), ', ') || ';'
|
||||||
|
FROM
|
||||||
|
pg_ts_config_map map
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
tokid,
|
||||||
|
alias
|
||||||
|
FROM
|
||||||
|
pg_catalog.ts_token_type(cfg.cfgparser)
|
||||||
|
) t ON (t.tokid = map.maptokentype)
|
||||||
|
LEFT OUTER JOIN pg_ts_dict dict ON (map.mapdict = dict.oid)
|
||||||
|
WHERE
|
||||||
|
map.mapcfg = cfg.oid
|
||||||
|
GROUP BY t.alias
|
||||||
|
ORDER BY t.alias)
|
||||||
|
, E'\n') as sql
|
||||||
|
FROM
|
||||||
|
pg_ts_config cfg
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
des.description as description,
|
||||||
|
des.objoid as descoid
|
||||||
|
FROM
|
||||||
|
pg_description des
|
||||||
|
WHERE
|
||||||
|
des.objoid={{cfgid}}::OID AND des.classoid='pg_ts_config'::regclass
|
||||||
|
) a ON (a.descoid = cfg.oid)
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
nspname,
|
||||||
|
nsp.oid as noid
|
||||||
|
FROM
|
||||||
|
pg_namespace nsp
|
||||||
|
WHERE
|
||||||
|
oid = {{scid}}::OID
|
||||||
|
) b ON (b.noid = cfg.cfgnamespace)
|
||||||
|
LEFT JOIN(
|
||||||
|
SELECT
|
||||||
|
prs.prsname as parsername,
|
||||||
|
prs.oid as oid
|
||||||
|
FROM
|
||||||
|
pg_ts_parser prs
|
||||||
|
)c ON (c.oid = cfg.cfgparser)
|
||||||
|
WHERE
|
||||||
|
cfg.oid={{cfgid}}::OID
|
||||||
|
) e;
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{# Fetch token/dictionary list for FTS CONFIGURATION #}
|
||||||
|
{% if cfgid %}
|
||||||
|
SELECT
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
t.alias
|
||||||
|
FROM
|
||||||
|
pg_catalog.ts_token_type(cfgparser) AS t
|
||||||
|
WHERE
|
||||||
|
t.tokid = maptokentype
|
||||||
|
) AS token,
|
||||||
|
array_agg(dictname) AS dictname
|
||||||
|
FROM
|
||||||
|
pg_ts_config_map
|
||||||
|
LEFT OUTER JOIN pg_ts_config ON mapcfg = pg_ts_config.oid
|
||||||
|
LEFT OUTER JOIN pg_ts_dict ON mapdict = pg_ts_dict.oid
|
||||||
|
WHERE
|
||||||
|
mapcfg={{cfgid}}::OID
|
||||||
|
GROUP BY
|
||||||
|
token
|
||||||
|
ORDER BY
|
||||||
|
1
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{# Tokens for FTS CONFIGURATION node #}
|
||||||
|
|
||||||
|
{% if parseroid %}
|
||||||
|
SELECT
|
||||||
|
alias
|
||||||
|
FROM
|
||||||
|
ts_token_type({{parseroid}}::OID)
|
||||||
|
ORDER BY
|
||||||
|
alias
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,51 @@
|
||||||
|
{# UPDATE statement for FTS CONFIGURATION #}
|
||||||
|
{% 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 CONFIGURATION {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(o_data.name)}}
|
||||||
|
RENAME TO {{conn|qtIdent(data.name)}};
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if 'tokens' in data %}
|
||||||
|
{% if'changed' in data.tokens %}
|
||||||
|
{% for tok in data.tokens.changed %}
|
||||||
|
ALTER TEXT SEARCH CONFIGURATION {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
|
||||||
|
ALTER MAPPING FOR {{tok.token}}
|
||||||
|
WITH {% for dict in tok.dictname %}{{dict}}{% if not loop.last %}, {% endif %}{% endfor %};
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if'added' in data.tokens %}
|
||||||
|
{% for tok in data.tokens.added %}
|
||||||
|
ALTER TEXT SEARCH CONFIGURATION {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
|
||||||
|
ADD MAPPING FOR {{tok.token}}
|
||||||
|
WITH {% for dict in tok.dictname %}{{dict}}{% if not loop.last %}, {% endif %}{% endfor %};
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if'deleted' in data.tokens %}
|
||||||
|
{% for tok in data.tokens.deleted %}
|
||||||
|
ALTER TEXT SEARCH CONFIGURATION {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
|
||||||
|
DROP MAPPING FOR {{tok.token}};
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if 'owner' in data and data.owner != '' and data.owner != o_data.owner %}
|
||||||
|
ALTER TEXT SEARCH CONFIGURATION {{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 CONFIGURATION {{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 CONFIGURATION {{conn|qtIdent(schema)}}.{{conn|qtIdent(name)}}
|
||||||
|
IS {{ data.description|qtLiteral }};
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
|
@ -59,7 +59,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||||
sqlCreateHelp: 'sql-createtsdictionary.html',
|
sqlCreateHelp: 'sql-createtsdictionary.html',
|
||||||
canDrop: true,
|
canDrop: true,
|
||||||
canDropCascade: true,
|
canDropCascade: true,
|
||||||
label: '{{ _('FTS dictionaries') }}',
|
label: '{{ _('FTS dictionary') }}',
|
||||||
hasSQL: true,
|
hasSQL: true,
|
||||||
hasDepends: true,
|
hasDepends: true,
|
||||||
Init: function() {
|
Init: function() {
|
||||||
|
|
|
@ -504,6 +504,35 @@ function($, _, pgAdmin, Backbone, Backform, Alertify, Node) {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Extend the browser's node model class to create a option/value pair
|
||||||
|
var MultiSelectAjaxCell = Backgrid.Extension.MultiSelectAjaxCell = Backgrid.Extension.NodeAjaxOptionsCell.extend({
|
||||||
|
defaults: _.extend({}, NodeAjaxOptionsCell.prototype.defaults, {
|
||||||
|
transform: undefined,
|
||||||
|
url_with_id: false,
|
||||||
|
select2: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: 'Select from the list',
|
||||||
|
width: 'style',
|
||||||
|
multiple: true
|
||||||
|
},
|
||||||
|
opt: {
|
||||||
|
label: null,
|
||||||
|
value: null,
|
||||||
|
image: null,
|
||||||
|
selected: false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
getValueFromDOM: function() {
|
||||||
|
var res = [];
|
||||||
|
|
||||||
|
this.$el.find("select").find(':selected').each(function() {
|
||||||
|
res.push($(this).attr('value'));
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Control to select multiple columns.
|
* Control to select multiple columns.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue