Hi,
PFA patch for FTS Dictionaries.
Please do review it and let me know in case of any issues.
Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/__init__.py
new file mode 100644
index 0000000..3f7f278
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/__init__.py
@@ -0,0 +1,818 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Defines views for management of Fts Dictionary node"""
+
+import json
+from flask import render_template, make_response, current_app, request, jsonify
+from flask.ext.babel import gettext as _
+from pgadmin.utils.ajax import make_json_response, \
+ make_response as ajax_response, internal_server_error, gone
+from pgadmin.browser.utils import PGChildNodeView
+from pgadmin.browser.server_groups.servers.databases.schemas.utils \
+ import SchemaChildModule
+import pgadmin.browser.server_groups.servers.databases.schemas as schemas
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class FtsDictionaryModule(SchemaChildModule):
+ """
+ class FtsDictionaryModule(SchemaChildModule)
+
+ A module class for FTS Dictionary node derived from SchemaChildModule.
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the FtsDictionaryModule and
+ it's base module.
+
+ * get_nodes(gid, sid, did, scid)
+ - Method is used to generate the browser collection node.
+
+ * node_inode()
+ - Method is overridden from its base class to make the node as leaf node
+
+ * script_load()
+ - Load the module script for FTS Dictionary, when any of the schema
+ node is initialized.
+ """
+ NODE_TYPE = 'fts_dictionary'
+ COLLECTION_LABEL = _('FTS Dictionaries')
+
+ def __init__(self, *args, **kwargs):
+ self.min_ver = None
+ self.max_ver = None
+ self.manager = None
+ super(FtsDictionaryModule, self).__init__(*args, **kwargs)
+
+ def get_nodes(self, gid, sid, did, scid):
+ """
+ Generate the collection node
+ :param gid: group id
+ :param sid: server id
+ :param did: database id
+ :param scid: schema id
+ """
+ yield self.generate_browser_collection_node(scid)
+
+ @property
+ def node_inode(self):
+ """
+ Override the property to make the node as leaf node
+ """
+ return False
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for fts template, when any of the schema
+ node is initialized.
+ """
+ return schemas.SchemaModule.NODE_TYPE
+
+
+blueprint = FtsDictionaryModule(__name__)
+
+
+class FtsDictionaryView(PGChildNodeView):
+ """
+ class FtsDictionaryView(PGChildNodeView)
+
+ A view class for FTS Dictionary node derived from PGChildNodeView.
+ This class is responsible for all the stuff related to view like
+ create/update/delete FTS Dictionary,
+ showing properties of node, showing sql in sql pane.
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the FtsDictionaryView and it's base view.
+
+ * module_js()
+ - This property defines (if javascript) exists for this node.
+ Override this property for your own logic
+
+ * check_precondition()
+ - This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+
+ * tokenize_options(self, option_value):
+ - This function will tokenize the string stored in database
+ e.g. database store the value as below
+ key1=value1, key2=value2, key3=value3, ....
+ This function will extract key and value from above string
+
+ * list()
+ - This function is used to list all the nodes within that collection.
+
+ * nodes()
+ - This function will be used to create all the child node within collection.
+ Here it will create all the FTS Dictionary nodes.
+
+ * node()
+ - This function will be used to create a node given its oid
+ Here it will create the FTS Template node based on its oid
+
+ * properties(gid, sid, did, scid, dcid)
+ - This function will show the properties of the selected FTS Dictionary node
+
+ * create(gid, sid, did, scid)
+ - This function will create the new FTS Dictionary object
+
+ * update(gid, sid, did, scid, dcid)
+ - This function will update the data for the selected FTS Dictionary node
+
+ * delete(self, gid, sid, did, scid, dcid):
+ - This function will drop the FTS Dictionary object
+
+ * msql(gid, sid, did, scid, dcid)
+ - This function is used to return modified SQL for the selected node
+
+ * get_sql(data, dcid)
+ - This function will generate sql from model data
+
+ * sql(gid, sid, did, scid, dcid):
+ - This function will generate sql to show in sql pane for node.
+
+ * fetch_templates():
+ - This function will fetch all templates related to node
+
+ * dependents(gid, sid, did, scid, dcid):
+ - This function get the dependents and return ajax response for the node.
+
+ * dependencies(self, gid, sid, did, scid, dcid):
+ - This function get the dependencies and return ajax response for node.
+
+ """
+
+ node_type = blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'dcid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'children': [{
+ 'get': 'children'
+ }],
+ 'delete': [{'delete': 'delete'}],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'stats': [{'get': 'statistics'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'fetch_templates': [{'get': 'fetch_templates'},
+ {'get': 'fetch_templates'}],
+ })
+
+ def _init_(self, **kwargs):
+ self.conn = None
+ self.template_path = None
+ self.manager = None
+ super(FtsDictionaryView, self).__init__(**kwargs)
+
+ def module_js(self):
+ """
+ Load JS file (fts_dictionary.js) for this module.
+ """
+ return make_response(
+ render_template(
+ "fts_dictionary/js/fts_dictionary.js",
+ _=_
+ ),
+ 200, {'Content-Type': 'application/x-javascript'}
+ )
+
+ def check_precondition(f):
+ """
+ This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+ """
+
+ @wraps(f)
+ def wrap(*args, **kwargs):
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+ kwargs['sid'])
+ self.conn = self.manager.connection(did=kwargs['did'])
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ _("Connection to the server has been lost!")
+ )
+ # we will set template path for sql scripts depending upon server version
+ ver = self.manager.version
+ if ver >= 90100:
+ self.template_path = 'fts_dictionary/sql/9.1_plus'
+ return f(*args, **kwargs)
+
+ return wrap
+
+ def tokenize_options(self, option_value):
+ """
+ This function will tokenize the string stored in database
+ e.g. database store the value as below
+ key1=value1, key2=value2, key3=value3, ....
+ This function will extract key and value from above string
+
+ Args:
+ option_value: key value option/value pair read from database
+ """
+ if option_value is not None:
+ option_str = option_value.split(',')
+ options = []
+ for fdw_option in option_str:
+ k, v = fdw_option.split('=', 1)
+ options.append({'option': k, 'value': v})
+ return options
+
+
+ @check_precondition
+ def list(self, gid, sid, did, scid):
+ """
+ List all FTS Dictionary nodes.
+
+ Args:
+ gid: Server Group Id
+ sid: Server Id
+ did: Database Id
+ scid: Schema Id
+ """
+
+ sql = render_template(
+ "/".join([self.template_path, 'properties.sql']),
+ scid=scid
+ )
+ status, res = self.conn.execute_dict(sql)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in res['rows']:
+ if row['options'] is not None:
+ row['options'] = self.tokenize_options(row['options'])
+
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid):
+ """
+ Return all FTS Dictionaries to generate nodes.
+
+ Args:
+ gid: Server Group Id
+ sid: Server Id
+ did: Database Id
+ scid: Schema Id
+ """
+
+ res = []
+ sql = render_template(
+ "/".join([self.template_path, 'nodes.sql']),
+ scid=scid
+ )
+ status, rset = self.conn.execute_2darray(sql)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['oid'],
+ did,
+ row['name'],
+ icon="icon-fts_dictionary"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ @check_precondition
+ def node(self, gid, sid, did, scid, dcid):
+ """
+ Return FTS Dictionary node to generate node
+
+ Args:
+ gid: Server Group Id
+ sid: Server Id
+ did: Database Id
+ scid: Schema Id
+ dcid: fts dictionary id
+ """
+
+ sql = render_template(
+ "/".join([self.template_path, 'nodes.sql']),
+ dcid=dcid
+ )
+ status, rset = self.conn.execute_2darray(sql)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ if len(rset['rows']) == 0:
+ return gone(_("""
+ Could not find the fts dictionary node.
+ """))
+
+ for row in rset['rows']:
+ return make_json_response(
+ data=self.blueprint.generate_browser_node(
+ row['oid'],
+ did,
+ row['name'],
+ icon="icon-fts_dictionary"
+ ),
+ status=200
+ )
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, dcid):
+ """
+ Show properties of FTS Dictionary node
+
+ Args:
+ gid: Server Group Id
+ sid: Server Id
+ did: Database Id
+ scid: Schema Id
+ dcid: fts dictionary id
+ """
+
+ sql = render_template(
+ "/".join([self.template_path, 'properties.sql']),
+ scid=scid,
+ dcid=dcid
+ )
+ status, res = self.conn.execute_dict(sql)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ if len(res['rows']) == 0:
+ return gone(_("""
+ Could not find the fts dictionary node.
+ """))
+
+ if res['rows'][0]['options'] is not None:
+ res['rows'][0]['options'] = self.tokenize_options(res['rows'][0]['options'])
+
+ return ajax_response(
+ response=res['rows'][0],
+ status=200
+ )
+
+ @check_precondition
+ def create(self, gid, sid, did, scid):
+ """
+ This function will creates new the FTS Dictionary object
+ :param gid: group id
+ :param sid: server id
+ :param did: database id
+ :param scid: schema id
+ """
+
+ # Mandatory fields to create a new FTS Dictionary
+ required_args = [
+ 'template',
+ 'schema',
+ 'name'
+ ]
+
+ data = request.form if request.form else json.loads(
+ request.data.decode())
+ for arg in required_args:
+ if arg not in data:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=_(
+ "Couldn't find the required parameter (%s)." % arg
+ )
+ )
+ try:
+ # Fetch schema name from schema oid
+ sql = render_template("/".join([self.template_path, 'schema.sql']),
+ data=data,
+ conn=self.conn,
+ )
+
+ status, schema = self.conn.execute_scalar(sql)
+ if not status:
+ return internal_server_error(errormsg=schema)
+
+ # Replace schema oid with schema name before passing to create.sql
+ # To generate proper sql query
+ new_data = data.copy()
+ new_data['schema'] = schema
+ sql = render_template("/".join([self.template_path, 'create.sql']),
+ data=new_data,
+ conn=self.conn,
+ )
+ status, res = self.conn.execute_scalar(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # We need dcid to add object in tree at browser,
+ # Below sql will give the same
+ sql = render_template(
+ "/".join([self.template_path, 'properties.sql']),
+ name=data['name']
+ )
+ status, dcid = self.conn.execute_scalar(sql)
+ if not status:
+ return internal_server_error(errormsg=dcid)
+
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ dcid,
+ did,
+ data['name'],
+ icon="icon-fts_dictionary"
+ )
+ )
+ except Exception as e:
+ current_app.logger.exception(e)
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def update(self, gid, sid, did, scid, dcid):
+ """
+ This function will update FTS Dictionary object
+ :param gid: group id
+ :param sid: server id
+ :param did: database id
+ :param scid: schema id
+ :param dcid: fts dictionary id
+ """
+ data = request.form if request.form else json.loads(
+ request.data.decode())
+
+ # Fetch sql query to update fts dictionary
+ sql = self.get_sql(gid, sid, did, scid, data, dcid)
+ try:
+ if sql and sql.strip('\n') and sql.strip(' '):
+ status, res = self.conn.execute_scalar(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ if dcid is not None:
+ sql = render_template(
+ "/".join([self.template_path, 'properties.sql']),
+ dcid=dcid,
+ scid=scid
+ )
+
+ status, res = self.conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ if len(res['rows']) == 0:
+ return gone(_("""
+ Could not find the fts dictionary node to update.
+ """))
+
+ data = res['rows'][0]
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ dcid,
+ did,
+ data['name'],
+ icon="icon-fts_dictionary"
+ )
+ )
+ # In case FTS Dictionary node is not present
+ else:
+ return make_json_response(
+ success=1,
+ info="Nothing to update",
+ data={
+ 'id': dcid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did,
+ 'scid': scid
+ }
+ )
+
+ except Exception as e:
+ current_app.logger.exception(e)
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def delete(self, gid, sid, did, scid, dcid):
+ """
+ This function will drop the FTS Dictionary object
+ :param gid: group id
+ :param sid: server id
+ :param did: database id
+ :param scid: schema id
+ :param dcid: FTS Dictionary id
+ """
+ # Below will decide if it's simple drop or drop with cascade call
+ if self.cmd == 'delete':
+ # This is a cascade operation
+ cascade = True
+ else:
+ cascade = False
+
+ try:
+ # Get name for FTS Dictionary from dcid
+ sql = render_template("/".join([self.template_path, 'delete.sql']),
+ dcid=dcid)
+ status, res = self.conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ if len(res['rows']) == 0:
+ return gone(_("""
+ Could not find the fts dictionary node to delete.
+ """))
+
+ # Drop FTS Dictionary
+ result = res['rows'][0]
+ sql = render_template("/".join([self.template_path, 'delete.sql']),
+ name=result['name'],
+ schema=result['schema'],
+ cascade=cascade
+ )
+
+ status, res = self.conn.execute_scalar(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info=_("FTS Dictionary dropped"),
+ data={
+ 'id': dcid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did,
+ 'scid': scid
+ }
+ )
+
+ except Exception as e:
+ current_app.logger.exception(e)
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def msql(self, gid, sid, did, scid, dcid=None):
+ """
+ This function returns modified SQL
+ :param gid: group id
+ :param sid: server id
+ :param did: database id
+ :param scid: schema id
+ :param dcid: FTS Dictionary id
+ """
+ #data = request.args
+ data = {}
+ for k, v in request.args.items():
+ try:
+ data[k] = json.loads(v)
+ except ValueError:
+ data[k] = v
+
+ # Fetch sql query for modified data
+ sql = self.get_sql(gid, sid, did, scid, data, dcid)
+
+ if isinstance(sql, str) and sql and sql.strip('\n') and sql.strip(' '):
+ return make_json_response(
+ data=sql,
+ status=200
+ )
+ else:
+ return make_json_response(
+ data="--modified SQL",
+ status=200
+ )
+
+ def get_sql(self, gid, sid, did, scid, data, dcid=None):
+ """
+ This function will return SQL for model data
+ :param gid: group id
+ :param sid: server id
+ :param did: database id
+ :param scid: schema id
+ :param dcid: fts dictionary id
+ """
+ try:
+ # Fetch sql for update
+ if dcid is not None:
+ sql = render_template(
+ "/".join([self.template_path, 'properties.sql']),
+ dcid=dcid,
+ scid=scid
+ )
+
+ status, res = self.conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ if len(res['rows']) == 0:
+ return gone(_("""
+ Could not find the fts dictionary node.
+ """))
+
+ old_data = res['rows'][0]
+
+ # If user has changed the schema then fetch new schema directly
+ # using its oid otherwise fetch old schema name using its oid
+ sql = render_template(
+ "/".join([self.template_path, 'schema.sql']),
+ data=data)
+
+ status, new_schema = self.conn.execute_scalar(sql)
+ if not status:
+ return internal_server_error(errormsg=new_schema)
+
+ # Replace schema oid with schema name
+ new_data = data.copy()
+ if 'schema' in new_data:
+ new_data['schema'] = new_schema
+
+ # Fetch old schema name using old schema oid
+ sql = render_template(
+ "/".join([self.template_path, 'schema.sql']),
+ data=old_data)
+
+ status, old_schema = self.conn.execute_scalar(sql)
+ if not status:
+ return internal_server_error(errormsg=old_schema)
+
+ # Replace old schema oid with old schema name
+ old_data['schema'] = old_schema
+
+ sql = render_template(
+ "/".join([self.template_path, 'update.sql']),
+ data=new_data, o_data=old_data
+ )
+ # Fetch sql query for modified data
+ else:
+ # Fetch schema name from schema oid
+ sql = render_template("/".join([self.template_path, 'schema.sql']),
+ data=data)
+
+ status, schema = self.conn.execute_scalar(sql)
+ if not status:
+ return internal_server_error(errormsg=schema)
+
+ # Replace schema oid with schema name
+ new_data = data.copy()
+ new_data['schema'] = schema
+
+ if 'template' in new_data and \
+ 'name' in new_data and \
+ 'schema' in new_data:
+ sql = render_template("/".join([self.template_path,
+ 'create.sql']),
+ data=new_data,
+ conn=self.conn
+ )
+ else:
+ sql = "-- incomplete definition"
+ return str(sql.strip('\n'))
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def fetch_templates(self, gid, sid, did, scid):
+ """
+ This function will return templates list for FTS Dictionary
+ :param gid: group id
+ :param sid: server id
+ :param did: database id
+ :param scid: schema id
+ """
+ # Fetch last system oid
+ datlastsysoid = self.manager.db_info[did]['datlastsysoid']
+
+ sql = render_template("/".join([self.template_path, 'templates.sql']),
+ template=True)
+ status, rset = self.conn.execute_dict(sql)
+
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ # Empty set is added before actual list as initially it will be visible
+ # at template control while creating a new FTS Dictionary
+ res = [{'label': '', 'value': ''}]
+ for row in rset['rows']:
+ if row['schemaoid'] > datlastsysoid :
+ row['tmplname'] = row['nspname'] + '.' + row['tmplname']
+
+ res.append({'label': row['tmplname'],
+ 'value': row['tmplname']})
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ @check_precondition
+ def sql(self, gid, sid, did, scid, dcid):
+ """
+ This function will reverse generate sql for sql panel
+ :param gid: group id
+ :param sid: server id
+ :param did: database id
+ :param scid: schema id
+ :param dcid: FTS Dictionary id
+ """
+ try:
+ sql = render_template(
+ "/".join([self.template_path, 'sql.sql']),
+ dcid=dcid,
+ scid=scid,
+ conn=self.conn
+ )
+ status, res = self.conn.execute_scalar(sql)
+ if not status:
+ return internal_server_error(
+ _(
+ "ERROR: Couldn't generate reversed engineered Query for the FTS Dictionary!\n{0}").format(
+ res
+ )
+ )
+
+ if res is None:
+ return gone(
+ _(
+ "ERROR: Couldn't generate reversed engineered Query for FTS Dictionary node!")
+ )
+
+ return ajax_response(response=res)
+
+ except Exception as e:
+ current_app.logger.exception(e)
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, dcid):
+ """
+ This function get the dependents and return ajax response
+ for the FTS Dictionary node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ dcid: FTS Dictionary ID
+ """
+ dependents_result = self.get_dependents(self.conn, dcid)
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, dcid):
+ """
+ This function get the dependencies and return ajax response
+ for the FTS Dictionary node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ dcid: FTS Dictionary ID
+ """
+ dependencies_result = self.get_dependencies(self.conn, dcid)
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+FtsDictionaryView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/img/coll-fts_dictionary.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/img/coll-fts_dictionary.png
new file mode 100644
index 0000000000000000000000000000000000000000..a97eb49af2861ce712def8db538f583301361c79
GIT binary patch
literal 362
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Q-Dv1E0CVF<)~EUstZR-?YlSk
zEKWVU+dg9Y4y(?Mfk(f(?)hlD{k_k@&n6q*Xs>*!wD_6ioX3LGA978)FObyF+IKg7
z{+^#%+eLt;FqQ=Q1v5B2yO9Rua29w(76WMyFm^kcZ3kq;d%8G=NL)@m@6Fesz~dUI
zVb91Ic;kQA)}wo7-1y>mZbj%T^I1KW)0EF9%>DjEHC>WPFgL%kG0EpeYme9)R$UKK
z-4nc=Z=O0Ztt?ZSaJ!S`l<5SAx6FoT&cFQpfA=oN`TJORtT57g541|P#5JNMC9x#c
zD!C{XNHG{07#ipr8tEDsh8P)GnHXD{m}ncAS{WEv%v6;_(U6;;l9^Ts(O_T+)&Np%
YWnc!;aB6z!8lVOSPgg&ebxsLQ0H%k2tN;K2
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/img/fts_dictionary.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/img/fts_dictionary.png
new file mode 100644
index 0000000000000000000000000000000000000000..55a880b7a0e14db1433b90f559beefdd1d3071fb
GIT binary patch
literal 634
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47zG1-LR^8|wsnW4DpxJsebRK%
zRpW(MZPwg(+xy9R_eYDR*Nm3jwq1MQbN?sTy&o;+ol(u}SI+I%pL1S&!A<8iH$C=!
za^CS;J%56B`zD2~KG}?3o#juICZCzL<AlYu{kF@mJM8>mI{%_lcAwUi6FRG3YAk=D
zvgEnk^s8E(+l(geHCueuV)I+Qxt9&rzS3UxQhoUg<t5ML7e1Aq_e5gSW%ahL+C97V
z=3Lfa^Ga*wOSNS$lomgeTkuqB?h~=uk3{<~DK&3a>DZw*?Sksk=ZcG-$u4**Irp*n
z#7mRsq{}sIl4;r|JMo;%{3nu=E>E15b$pZCh5go&)$2rSw}`bJ>Ytu-be-4P-S&$%
zg@{(IKef$i{<`qSHHYS|iJEjwaUU>H7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWO2
zc)B=-NL<c6ca`r@fB?&d+nRYt@7}!|+r9hqe|e@m7aWAY<@%+Wd!M+U$Pgx@dTITh
zMLz2SR<ST%ky_h#S-MBQ@np`%BD39xUpCEdI{x}I`<sIS7D7v9k38y3(VBbm*{9Y=
z(GgQlP0d<+FJs#+*@it!Z@=bmTl|sb!OKXo%QtH_vV^_n{duBn`|*brR+mM!#6Mh@
zKEQGRUgL59C9;8k=Bh7^5_nkP@=+P+R@D;Mh?11Vl2ohYqEsNoU}RuuplfKPYhV~+
zWME}tY-M7iZD49;U|=y*RSrc%ZhlH;S|vn-fhAZ2NVS!L8AQXW>7i?Y8W=oX{an^L
HB{Ts5+LaYu
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/js/fts_dictionary.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/js/fts_dictionary.js
new file mode 100644
index 0000000..7427bd7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/js/fts_dictionary.js
@@ -0,0 +1,183 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+ 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+ // Extend the browser's node model class to create a option/value pair
+ var OptionLabelModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ options: undefined,
+ value: undefined
+ },
+ // Define the schema for the Options
+ schema: [
+ {
+ id: 'option', label:'Option', type:'text', group: null,
+ cellHeaderClasses:'width_percent_50', editable: true
+ },{
+ id: 'value', label:'Value', type: 'text', group:null,
+ cellHeaderClasses:'width_percent_50', editable: true
+ },
+ ],
+ validate: function() {
+ // Clear any existing errors.
+ this.errorModel.clear()
+
+ if (_.isUndefined(this.get('option')) ||
+ String(this.get('option')).replace(/^\s+|\s+$/g, '') == '') {
+ var msg = '{{ _('Option can not be empty!') }}';
+ this.errorModel.set('option',msg);
+ return msg;
+ }
+ if (_.isUndefined(this.get('value')) ||
+ String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
+ var msg = '{{ _('Value can not be empty!') }}';
+ this.errorModel.set('value',msg);
+ return msg;
+ }
+ return null;
+ }
+ });
+
+ // Extend the collection class for FTS Dictionary
+ if (!pgBrowser.Nodes['coll-fts_dictionary']) {
+ var fts_dictionaries = pgAdmin.Browser.Nodes['coll-fts_dictionary'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'fts_dictionary',
+ label: '{{ _('FTS Dictionaries') }}',
+ type: 'coll-fts_dictionary',
+ columns: ['name', 'description']
+ });
+ };
+
+ // Extend the node class for FTS Dictionary
+ if (!pgBrowser.Nodes['fts_dictionary']) {
+ pgAdmin.Browser.Nodes['fts_dictionary'] = pgAdmin.Browser.Node.extend({
+ parent_type: ['schema', 'catalog'],
+ type: 'fts_dictionary',
+ canDrop: true,
+ canDropCascade: true,
+ label: '{{ _('FTS dictionaries') }}',
+ hasSQL: true,
+ hasDepends: true,
+ Init: function() {
+
+ // Avoid multiple registration of menus
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ // Add context menus for FTS Dictionary
+ pgBrowser.add_menus([{
+ name: 'create_fts_dictionary_on_schema', node: 'schema', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{_('FTS Dictionary...')}}',
+ icon: 'wcTabIcon icon-fts_dictionary', data: {action: 'create'}
+ },{
+ name: 'create_fts_dictionary_on_coll', node: 'coll-fts_dictionary',
+ module: this, applies: ['object', 'context'], priority: 4,
+ callback: 'show_obj_properties', category: 'create',
+ label: '{{ _('FTS Dictionary...') }}', data: {action: 'create'},
+ icon: 'wcTabIcon icon-fts_dictionary'
+ },{
+ name: 'create_fts_dictionary', node: 'fts_dictionary', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{_('FTS Dictionary...')}}',
+ icon: 'wcTabIcon icon-fts_dictionary', data: {action: 'create'}
+ }]);
+ },
+
+ // Defining backform model for FTS Dictionary node
+ model: pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ name: undefined, // FTS Dictionary name
+ owner: undefined, // FTS Dictionary owner
+ description: undefined, // Comment on FTS Dictionary
+ schema: undefined, // Schema name FTS dictionary belongs to
+ template: undefined, // Template list for FTS dictionary node
+ options: undefined // option/value pair list for FTS Dictionary
+ },
+ initialize: function(attrs, args) {
+ var isNew = (_.size(attrs) === 0);
+
+ if (isNew) {
+ var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+ this.set({'owner': userInfo.name}, {silent: true});
+ }
+ pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+ if (_.isUndefined(this.get('schema'))) {
+ this.set('schema', this.node_info.schema._id);
+ }
+ },
+ // Defining schema for fts dictionary
+ schema: [{
+ id: 'name', label: '{{ _('Name') }}', cell: 'string',
+ type: 'text', cellHeaderClasses: 'width_percent_50'
+ },{
+ id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+ editable: false, type: 'text', disabled: true, mode:['properties']
+ },{
+ id: 'owner', label:'{{ _('Owner') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'edit','create'], node: 'role',
+ control: Backform.NodeListByNameControl
+ },{
+ id: 'schema', label: '{{ _('Schema')}}', cell: 'string',
+ type: 'text', mode: ['create','edit'], node: 'schema',
+ control: 'node-list-by-id'
+ },{
+ id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+ type: 'multiline', cellHeaderClasses: 'width_percent_50'
+ },{
+ id: 'template', label: '{{ _('Template')}}',type: 'text',
+ disabled: function(m) { return !m.isNew(); }, url: 'fetch_templates',
+ group: '{{ _('Definition') }}',control: 'node-ajax-options'
+ },{
+ id: 'options', label: '{{ _('Option') }}', type: 'collection',
+ group: '{{ _('Options') }}', control: 'unique-col-collection',
+ model: OptionLabelModel, columns: ['option', 'value'],
+ uniqueCol : ['option'], mode: ['edit', 'create', 'properties'],
+ canAdd: true, canEdit: false,canDelete: true
+ }],
+
+ /*
+ * Triggers control specific error messages for dictionary name,
+ * template and schema, if any one of them is not specified
+ * while creating new fts dictionary
+ */
+ validate: function(keys){
+ var name = this.get('name');
+ var template = this.get('template');;
+ var schema = this.get('schema');
+
+ // Validate FTS Dictionary name
+ if (_.isUndefined(name) || _.isNull(name) || String(name).replace(/^\s+|\s+$/g, '') == '') {
+ var msg = '{{ _('Name must be specified!') }}';
+ this.errorModel.set('name', msg);
+ return msg;
+ }
+
+ // Validate template name
+ else if (_.isUndefined(template) || _.isNull(template) || String(template).replace(/^\s+|\s+$/g, '') == '') {
+ var msg = '{{ _('Template must be selected!') }}';
+ this.errorModel.set('template', msg);
+ return msg;
+ }
+
+ // Validate schema
+ else if (_.isUndefined(schema) || _.isNull(schema) || String(schema).replace(/^\s+|\s+$/g, '') == '') {
+ var msg = '{{ _('Schema must be selected!') }}';
+ this.errorModel.set('schema', msg);
+ return msg;
+ }
+ else this.errorModel.clear();
+
+ this.trigger('on-status-clear');
+ return null;
+ }
+ })
+ });
+ }
+
+return pgBrowser.Nodes['coll-fts_dictionary'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..cd1187c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/create.sql
@@ -0,0 +1,14 @@
+{# CREATE FTS DICTIONARY Statement #}
+{% if data and data.schema and data.name and data.template %}
+CREATE TEXT SEARCH DICTIONARY {{ conn|qtIdent(data.schema, data.name) }} (
+ TEMPLATE = data.template {% for variable in data.options %}
+ {% if "options" in variable and variable.options != '' %}
+ ,{{ conn|qtIdent(variable.options) }}={{variable.value|qtLiteral}}
+ {% endif %}
+ {% endfor %}
+);
+{# Description for FTS_DICTIONARY #}
+{% if data.description %}
+COMMENT ON TEXT SEARCH DICTIONARY {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.description|qtLiteral }};
+{% endif %}{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..c344a7d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/delete.sql
@@ -0,0 +1,23 @@
+{# FETCH FTS DICTIONARY NAME Statement #}
+{% if dcid %}
+SELECT
+ dict.dictname as name,
+ (
+ SELECT
+ nspname
+ FROM
+ pg_namespace
+ WHERE
+ oid = dict.dictnamespace
+ ) as schema
+FROM
+ pg_ts_dict dict LEFT OUTER JOIN pg_description des
+ ON (des.objoid=dict.oid AND des.classoid='pg_ts_dict'::regclass)
+WHERE
+ dict.oid = {{dcid}}::OID;
+{% endif %}
+
+{# DROP FTS DICTIOANRY Statement #}
+{% if schema and name %}
+DROP TEXT SEARCH DICTIONARY {{conn|qtIdent(schema)}}.{{conn|qtIdent(name)}} {% if cascade %}CASCADE{%endif%};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..78abd0b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/nodes.sql
@@ -0,0 +1,13 @@
+{# FETCH FTS DICTIONARY name statement #}
+SELECT
+ oid, dictname as name
+FROM
+ pg_ts_dict dict
+WHERE
+{% if scid %}
+ dict.dictnamespace = {{scid}}::OID
+{% elif dcid %}
+ dict.oid = {{pid}}::OID
+{% endif %}
+
+ORDER BY name
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..16e5644
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/properties.sql
@@ -0,0 +1,24 @@
+{# FETCH properties for FTS DICTIONARY #}
+SELECT
+ dict.oid,
+ dict.dictname as name,
+ pg_get_userbyid(dict.dictowner) as owner,
+ t.tmplname as template,
+ dict.dictinitoption as options,
+ dict.dictnamespace as schema,
+ des.description
+FROM
+ pg_ts_dict dict
+ LEFT OUTER JOIN pg_ts_template t ON t.oid=dict.dicttemplate
+ LEFT OUTER JOIN pg_description des ON (des.objoid=dict.oid AND des.classoid='pg_ts_dict'::regclass)
+WHERE
+{% if scid %}
+ dict.dictnamespace = {{scid}}::OID
+{% elif name %}
+dict.dictname = {{name|qtLiteral}}
+{% endif %}
+{% if dcid %}
+ AND dict.oid = {{dcid}}::OID
+{% endif %}
+ORDER BY
+ dict.dictname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/schema.sql
new file mode 100644
index 0000000..2a38e7f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/schema.sql
@@ -0,0 +1,19 @@
+{# FETCH statement for SCHEMA name #}
+{% if data.schema %}
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{data.schema}}::OID
+
+{% elif data.id %}
+SELECT
+ nspname
+FROM
+ pg_namespace nsp
+ LEFT JOIN pg_ts_dict dict
+ ON dict.dictnamespace = nsp.oid
+WHERE
+ dict.oid = {{data.id}}::OID
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/sql.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/sql.sql
new file mode 100644
index 0000000..42537f6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/sql.sql
@@ -0,0 +1,52 @@
+{# REVERSED ENGINEERED SQL FOR FTS DICTIONARY #}
+{% if dcid and scid %}
+SELECT
+ array_to_string(array_agg(sql), E'\n\n') as sql
+FROM
+ (
+ SELECT
+ E'-- Text Search Dictionary: ' || nspname || E'.' || dict.dictname ||
+ E'\n\n-- DROP TEXT SEARCH DICTIONARY ' || nspname || E'.' || dict.dictname ||
+ E'\n\nCREATE TEXT SEARCH DICTIONARY ' || nspname || E'.' || dict.dictname || E' (\n' ||
+ E'\tTEMPLATE = ' || template ||
+ CASE
+ WHEN dict.dictinitoption IS NOT NULL THEN E',\n\t' || dict.dictinitoption
+ ELSE ''
+ END ||
+ E'\n);' ||
+ CASE
+ WHEN description IS NOT NULL THEN
+ E'\n\nCOMMENT ON TEXT SEARCH TEMPLATE ' || nspname || E'.' || dict.dictname ||
+ E' IS ' || pg_catalog.quote_literal(description) || E';'
+ ELSE '' END as sql
+ FROM
+ pg_ts_dict dict
+ LEFT JOIN(
+ SELECT
+ t.tmplname as template,
+ t.oid as oid
+ FROM
+ pg_ts_template t
+ ) d on d.oid = dict.dicttemplate
+ LEFT JOIN (
+ SELECT
+ des.description as description,
+ des.objoid as descoid
+ FROM
+ pg_description des
+ WHERE
+ des.objoid={{dcid}}::OID AND des.classoid='pg_ts_dict'::regclass
+ ) a ON (a.descoid = dict.oid)
+ LEFT JOIN (
+ SELECT
+ nspname,
+ nsp.oid as noid
+ FROM
+ pg_namespace nsp
+ WHERE
+ oid = {{scid}}::OID
+ ) b ON (b.noid = dict.dictnamespace)
+WHERE
+ dict.oid={{dcid}}::OID
+) as c;
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/templates.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/templates.sql
new file mode 100644
index 0000000..d3270ba
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/templates.sql
@@ -0,0 +1,11 @@
+{# FETCH templates for FTS DICTIONARY #}
+{% if template %}
+SELECT
+ tmplname,
+ nspname,
+ n.oid as schemaoid
+FROM
+ pg_ts_template JOIN pg_namespace n ON n.oid=tmplnamespace
+ORDER BY
+ tmplname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..1ef1eb4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/templates/fts_dictionary/sql/9.1_plus/update.sql
@@ -0,0 +1,43 @@
+{# UPDATE statement for FTS DICTIONARY #}
+{% if data %}
+{% set name = o_data.name %}
+{% set schema = o_data.schema %}
+{% if data.name and data.name != o_data.name %}
+{% set name = data.name %}
+ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(o_data.name)}}
+ RENAME TO {{data.name}};
+{% endif %}
+{% if 'options' in data%}
+{% if'changed' in data.options %}
+{% for opt in data.options.changed %}
+ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
+ ({{opt.option}}={{opt.value}});
+{% endfor %}
+{% endif %}
+{% if'added' in data.options%}
+{% for opt in data.options.added %}
+ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
+ ({{opt.option}}={{opt.value}});
+{% endfor %}
+{% endif %}
+{% if'deleted' in data.options%}
+{% for opt in data.options.deleted %}
+ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
+ ({{opt.option}});
+{% endfor %}
+{% endif %}
+{% endif %}
+{% if 'owner' in data and data.owner != o_data.owner %}
+ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
+ OWNER TO {{data.owner}};
+{% endif %}
+{% if 'schema' in data and data.schema != o_data.schema %}
+{% set schema = data.schema%}
+ALTER TEXT SEARCH DICTIONARY {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
+ SET SCHEMA {{data.schema}};
+{% endif %}
+{% if 'description' in data and data.description != o_data.description %}
+COMMENT ON TEXT SEARCH DICTIONARY {{conn|qtIdent(schema)}}.{{conn|qtIdent(name)}}
+ IS {{ data.description|qtLiteral }};
+{% endif %}
+{% endif %}
\ No newline at end of file
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers