Hi, PFA the patch for FTS parser node for review. Please do review it and provide the comments.
Regards, Sanket Mehta Sr Software engineer Enterprisedb
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/__init__.py new file mode 100644 index 0000000..90a64b5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/__init__.py @@ -0,0 +1,816 @@ +########################################################################## +# +# 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 Parser node""" + +import json +from flask import render_template, make_response, current_app, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.server_groups.servers.databases.schemas.utils import SchemaChildModule +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 FtsParserModule(SchemaChildModule): + """ + class FtsParserModule(SchemaChildModule) + + A module class for FTS Parser node derived from SchemaChildModule. + + Methods: + ------- + * __init__(*args, **kwargs) + - Method is used to initialize the FtsParserModule 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 Parser, when any of the schema node is + initialized. + """ + NODE_TYPE = 'fts_parser' + COLLECTION_LABEL = gettext('FTS Parsers') + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + super(FtsParserModule, 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 = FtsParserModule(__name__) + + +class FtsParserView(PGChildNodeView): + """ + class FtsParserView(PGChildNodeView) + + A view class for FTS Parser node derived from PGChildNodeView. This class is + responsible for all the stuff related to view like create/update/delete + responsible for all the stuff related to view like create/update/delete + FTS Parser, showing properties of node, showing sql in sql pane. + + Methods: + ------- + * __init__(**kwargs) + - Method is used to initialize the FtsParserView and it's base view. + + * module_js() + - This property defines (if javascript) exists for this node. + Override this property for your own logic + + * check_precondition() + - This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to self + + * list() + - This function is used to list all the nodes within that collection. + + * nodes() + - This function will used to create all the child node within that collection. + Here it will create all the FTS Parser nodes. + + * properties(gid, sid, did, scid, pid) + - This function will show the properties of the selected FTS Parser node + + * create(gid, sid, did, scid) + - This function will create the new FTS Parser object + + * update(gid, sid, did, scid, pid) + - This function will update the data for the selected FTS Parser node + + * delete(self, gid, sid, did, scid, pid): + - This function will drop the FTS Parser object + + * msql(gid, sid, did, scid, pid) + - This function is used to return modified SQL for the selected FTS Parser node + + * get_sql(data, pid) + - This function will generate sql from model data + + * sql(gid, sid, did, scid, pid): + - This function will generate sql to show it in sql pane for the selected FTS Parser node. + + * get_type(): + - This function will fetch all the types for source and target types select control. + + * dependents(gid, sid, did, scid, pid): + - This function get the dependents and return ajax response for the Fts Parser node. + + * dependencies(self, gid, sid, did, scid, pid): + - This function get the dependencies and return ajax response for the FTS Parser 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': 'pid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'children': [{ + 'get': 'children' + }], + 'delete': [{'delete': 'delete'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_start': [{'get': 'get_start'}, {'get': 'get_start'}], + 'get_token': [{'get': 'get_token'}, {'get': 'get_token'}], + 'get_end': [{'get': 'get_end'}, {'get': 'get_end'}], + 'get_lextype': [{'get': 'get_lextype'}, {'get': 'get_lextype'}], + 'get_headline': [{'get': 'get_headline'}, {'get': 'get_headline'}], + }) + + def _init_(self, **kwargs): + self.conn = None + self.template_path = None + self.manager = None + super(FtsParserView, self).__init__(**kwargs) + + def module_js(self): + """ + This property defines whether javascript exists for this node. + """ + return make_response( + render_template( + "fts_parser/js/fts_parser.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to self + """ + + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager( + kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + # we will set template path for sql scripts depending upon server version + ver = self.manager.version + if ver >= 90100: + self.template_path = 'fts_parser/sql/9.1_plus' + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + sql = render_template( + "/".join([self.template_path, 'properties.sql']), + scid=scid + ) + status, res = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + res = [] + sql = render_template( + "/".join([self.template_path, 'nodes.sql']), + scid=scid + ) + status, rset = self.conn.execute_2darray(sql) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + did, + row['name'], + icon="icon-fts_parser" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def node(self, gid, sid, did, scid, pid): + res = [] + sql = render_template( + "/".join([self.template_path, 'nodes.sql']), + pid=pid + ) + status, rset = self.conn.execute_2darray(sql) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + return make_json_response( + data=self.blueprint.generate_browser_node( + row['oid'], + did, + row['name'], + icon="icon-fts_parser" + ), + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, pid): + sql = render_template( + "/".join([self.template_path, 'properties.sql']), + scid=scid, + pid=pid + ) + status, res = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + @check_precondition + def create(self, gid, sid, did, scid): + """ + This function will creates new the fts_parser object + :param gid: group id + :param sid: server id + :param did: database id + :param scid: schema id + """ + + # Mandatory fields to create a new fts parser + required_args = [ + 'prsstart', + 'prstoken', + 'prsend', + 'prslextype', + 'schema', + 'name' + ] + + data = request.form if request.form else json.loads( + request.data.decode()) + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + try: + # Fetch schema name from schema oid + sql = render_template("/".join([self.template_path, 'schema.sql']), + data=data, + conn=self.conn, + ) + + status, schema = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=schema) + + # replace schema oid with schema name before passing to create.sql + # to generate proper sql query + new_data = data.copy() + new_data['schema'] = schema + sql = render_template("/".join([self.template_path, 'create.sql']), + data=new_data, + conn=self.conn, + ) + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + # we need fts_parser id to to add object in tree at browser, + # below sql will give the same + sql = render_template( + "/".join([self.template_path, 'properties.sql']), + name=data['name'] + ) + status, pid = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=pid) + + return jsonify( + node=self.blueprint.generate_browser_node( + pid, + did, + data['name'], + icon="icon-fts_parser" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, pid): + """ + This function will update text search parser object + :param gid: group id + :param sid: server id + :param did: database id + :param scid: schema id + :param pid: fts parser id + """ + data = request.form if request.form else json.loads( + request.data.decode()) + + # Fetch sql query to update fts parser + sql = self.get_sql(gid, sid, did, scid, data, pid) + try: + if sql and sql.strip('\n') and sql.strip(' '): + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="FTS Parser updated", + data={ + 'id': pid, + 'sid': sid, + 'gid': gid, + 'did': did, + 'scid': scid + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': pid, + 'sid': sid, + 'gid': gid, + 'did': did, + 'scid': scid + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, pid): + """ + This function will drop the fts_parser object + :param gid: group id + :param sid: server id + :param did: database id + :param scid: schema id + :param pid: fts tempate id + """ + # Below will decide if it's simple drop or drop with cascade call + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + # Get name for Parser from pid + sql = render_template("/".join([self.template_path, 'delete.sql']), + pid=pid) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + # Drop fts Parser + result = res['rows'][0] + sql = render_template("/".join([self.template_path, 'delete.sql']), + name=result['name'], + schema=result['schema'], + cascade=cascade + ) + + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("FTS Parser dropped"), + data={ + 'id': pid, + 'sid': sid, + 'gid': gid, + 'did': did, + 'scid': scid + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, scid, pid=None): + """ + This function returns modified SQL + :param gid: group id + :param sid: server id + :param did: database id + :param scid: schema id + :param pid: fts tempate id + """ + data = request.args + + # Fetch sql query for modified data + sql = self.get_sql(gid, sid, did, scid, data, pid) + + 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, pid=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 pid: fts tempate id + """ + try: + # Fetch sql for update + if pid is not None: + sql = render_template( + "/".join([self.template_path, 'properties.sql']), + pid=pid, + scid=scid + ) + + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + # If user has changed the schema then fetch new schema directly + # using its oid otherwise fetch old schema name using fts Parser 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 'prsstart' in new_data and \ + 'prstoken' in new_data and \ + 'prsend' in new_data and \ + 'prslextype' in new_data and \ + 'name' in new_data and \ + 'schema' in new_data: + sql = render_template("/".join([self.template_path, + 'create.sql']), + data=new_data, + conn=self.conn + ) + else: + sql = "-- incomplete definition" + return str(sql.strip('\n')) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def get_start(self, gid, sid, did, scid, pid=None): + """ + This function will return start functions list for fts Parser + :param gid: group id + :param sid: server id + :param did: database id + :param scid: schema id + :param pid: fts parser id + """ + data = request.args + sql = render_template("/".join([self.template_path, 'functions.sql']), + start=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 start select control while creating a new fts parser + res = [{'label': '', 'value': ''}] + for row in rset['rows']: + res.append({'label': row['proname'], + 'value': row['proname']}) + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def get_token(self, gid, sid, did, scid, pid=None): + """ + This function will return token functions list for fts Parser + :param gid: group id + :param sid: server id + :param did: database id + :param scid: schema id + :param pid: fts parser id + """ + data = request.args + sql = render_template("/".join([self.template_path, 'functions.sql']), + token=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 token select control while creating a new fts parser + res = [{'label': '', 'value': ''}] + for row in rset['rows']: + res.append({'label': row['proname'], + 'value': row['proname']}) + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def get_end(self, gid, sid, did, scid, pid=None): + """ + This function will return end functions list for fts Parser + :param gid: group id + :param sid: server id + :param did: database id + :param scid: schema id + :param pid: fts parser id + """ + data = request.args + sql = render_template("/".join([self.template_path, 'functions.sql']), + end=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 end select control while creating a new fts parser + res = [{'label': '', 'value': ''}] + for row in rset['rows']: + res.append({'label': row['proname'], + 'value': row['proname']}) + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def get_lextype(self, gid, sid, did, scid, pid=None): + """ + This function will return lextype functions list for fts Parser + :param gid: group id + :param sid: server id + :param did: database id + :param scid: schema id + :param pid: fts parser id + """ + data = request.args + sql = render_template("/".join([self.template_path, 'functions.sql']), + lextype=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 lextype select control while creating a new fts parser + res = [{'label': '', 'value': ''}] + for row in rset['rows']: + res.append({'label': row['proname'], + 'value': row['proname']}) + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def get_headline(self, gid, sid, did, scid, pid=None): + """ + This function will return headline functions list for fts Parser + :param gid: group id + :param sid: server id + :param did: database id + :param scid: schema id + :param pid: fts parser id + """ + data = request.args + sql = render_template("/".join([self.template_path, 'functions.sql']), + headline=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 headline select control while creating a new fts parser + res = [{'label': '', 'value': ''}] + for row in rset['rows']: + res.append({'label': row['proname'], + 'value': row['proname']}) + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def sql(self, gid, sid, did, scid, pid): + """ + 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 pid: fts tempate id + """ + try: + sql = render_template( + "/".join([self.template_path, 'sql.sql']), + pid=pid, + 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 Parser!\n{0}").format( + res + ) + ) + + if res is None: + return gone( + _( + "ERROR: Couldn't generate reversed engineered Query for FTS Parser node!") + ) + + return ajax_response(response=res) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def dependents(self, gid, sid, did, scid, pid): + """ + This function get the dependents and return ajax response + for the FTS Parser node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + pid: FTS Parser ID + """ + dependents_result = self.get_dependents(self.conn, pid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, pid): + """ + This function get the dependencies and return ajax response + for the FTS Parser node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + pid: FTS Tempalte ID + """ + dependencies_result = self.get_dependencies(self.conn, pid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +FtsParserView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/img/coll-fts_parser.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/img/coll-fts_parser.png new file mode 100644 index 0000000000000000000000000000000000000000..340ec6b7fbdcf1702f66fe2c833fcdcd512bb141 GIT binary patch literal 620 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47+C{+LR^8|m3!MN7a1f>VVS)( zuW6md$rp<r{6BdA|NguGcHjQD?dIPN*Z-`z`g_TNr=9g{4YFrR-1xeE_4R_vRqAES z71C!2EI5^vGfy;Uw$zRP8?XFZe&OHzvwx<a`rUWzSIeOvmHWQu@BdbC?0v`FZ3XxL z@4NVK(QcLNhkw@0O`Wt$`Rbm@lMjC_Nt?lc``?z+e<q*!)p_J+-GT4LhdWo_On!X# z$ksbYHrz<Md$@hY^*<|*{%mSo=3lqeW5?I{0}_v}99lJFSJ1Sbz7MW0KdJou%!iqW zew6R}n!NRM*oIGDyV|B)Pr7sS@UokS7hO-fb)a!s^G4I7UuzD1E!_V(bI+%w1CwVT zH@UUHZ(jX+&1wblhrobfED7=pW^j0RBMr#mEbxdd2GSm2>~=ES4#=42>Eak7aXC5R z07H+@8G!>wIG%0ZuxVp)K~dr751&46ZeU?yX>D2P<>KPVbS5doYsS%wQw$bsPh`-V zIAzkbiBl(s2ZRVEBm|_Tg$0HN2M1pdxP0YO8r!RjiPx@PHa9RaGBY$aX6Mu}j*f_m zynW-=O=}CQMiz;ZIk7Q!@7UVx-BVkm>cDiW&g&mzqvJtE#f6Ed1g>*C+*Leq$bp4{ z;lM(nj5%k`L4Hv!ag8WRNi0dVN-jzTQVd20h6cKZM!E)uAw~vPCdO7KrrHLkRt5(1 t-s!DE(U6;;l9^Ts(O_T+)&Nv(Vr5_k(Qs;d=o+8~22WQ%mvv4FO#s2@A-Vtn literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/img/fts_parser.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/img/fts_parser.png new file mode 100644 index 0000000000000000000000000000000000000000..47eca9950499fc0ca1e2ee0a419a8a380fd528f3 GIT binary patch literal 715 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47-Ir_LR^8|m3!MN7a1f>VVS)( zuW6md$rp<r{6Bd2-<~`Fw%_`<>H42FSAQ?N{CnQTU$gf;X{leUmpx13#@FpP{%^Sa zZ|V8JbI<;ncItQEnctJH|6YFO*MdtwXJ7s?=gh~+_y6zPt#bY3@9s&PGFKn&x-jF& z5v?ajv>tCMU3>k<lImr8cmC}-_jl&$KNC;<>OAtZ;p(r&7rIZ}On!3j=#D!_w%ka% zccJUVt-l-FS0uEqh&ueUwsD#7io?yvWZpcvzGK#|sF}M$A6{E?R^`L>KdX-YYB}_y zYTx&Q-QUvA|Cn^9Yu}Be`?n9Ty>)oi^`u+pIuBm?y{K_nK>ae$+9gigzC^aI@m_SW zcvI=}Q>G6OY2I63yx{!TzQP3xIkTlseVBgmd+E-viCaI1ZT#f3_M_9LkA8>0lpOq= zci>aj;ZOPdUl-2ZR?xiB?D+fc6W<z+d@VotC4c|tj6I){c7BT9{xNL(hmh?r<LlRJ z{;19`0fsMQNswPKgTu2MX+REVfk$L9koEv$x0Bg+K*lmp7sn8Z%gG4}Oa?}V&Wys! z9p?|7I(F{h$)jfvpFXaCfJsC^L`Y0fRCxM?DU+s66lLKuIDPW;X>AQH&DASbt;}v< zG1<DMwB+j-UY_0_-vdm`(w>P;3%h2vt?V0L0;9Wnx_G#GIlsStzWj!U_4Df;5&{Z5 zG9oHmR00&Vtklf(>=X?(Emci*xilusn>cgo+{v@2#|K1&%;77Wsc>d#lg^PJJivq_ zeouzQkE7)|(8a1Ht`Q|Ei6yC4$wjF^iowXh&_LJFNY}tH#K^$P#MsKjRNKJR%D`aW tJH1sX8glbfGSey{8VoGK8i1-ztPIQ`8ct0QT?5p>;OXk;vd$@?2>`SDU7!E} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/js/fts_parser.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/js/fts_parser.js new file mode 100644 index 0000000..0014427 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/js/fts_parser.js @@ -0,0 +1,169 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Extend the collection class for fts parser + if (!pgBrowser.Nodes['coll-fts_parser']) { + var fts_parsers = pgAdmin.Browser.Nodes['coll-fts_parser'] = + pgAdmin.Browser.Collection.extend({ + node: 'fts_parser', + label: '{{ _('FTS Parsers') }}', + type: 'coll-fts_parser', + columns: ['name', 'description'] + }); + }; + + // Extend the node class for fts parser + if (!pgBrowser.Nodes['fts_parser']) { + pgAdmin.Browser.Nodes['fts_parser'] = pgAdmin.Browser.Node.extend({ + parent_type: ['schema', 'catalog'], + type: 'fts_parser', + canDrop: true, + canDropCascade: true, + label: '{{ _('FTS Parsers') }}', + hasSQL: true, + hasDepends: true, + Init: function() { + + // Avoid multiple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + // Add context menus for fts parser + pgBrowser.add_menus([{ + name: 'create_fts_parser_on_schema', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('FTS Parser...') }}', + icon: 'wcTabIcon icon-fts_parser', data: {action: 'create'} + },{ + name: 'create_fts_parser_on_coll', node: 'coll-fts_parser', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('FTS Parser...') }}', + icon: 'wcTabIcon icon-fts_parser', data: {action: 'create'} + },{ + name: 'create_fts_parser', node: 'fts_parser', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('FTS Parser...') }}', + icon: 'wcTabIcon icon-fts_parser', data: {action: 'create'} + }]); + + }, + + // Defining backform model for fts parser node + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, // Fts parser name + description: undefined, // Comment on parser + schema: undefined, // Schema name to which parser belongs + tmplinit: undefined, // Init function for fts parser + tmpllexize: undefined // Lexize function for fts parser + }, + initialize: function() { + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + if (_.isUndefined(this.get('schema'))) { + this.set('schema', this.node_info.schema._id); + } + }, + // Defining schema for fts parser + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', cellHeaderClasses: 'width_percent_50' + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + editable: false, type: 'text', disabled: true, mode:['properties'] + },{ + id: 'schema', label: '{{ _('Schema')}}', cell: 'string', + type: 'text', mode: ['create','edit'], node: 'schema', + control: 'node-list-by-id' + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline', cellHeaderClasses: 'width_percent_50' + },{ + id: 'prsstart', label: '{{ _('Start Function')}}', group: '{{ _('Definition') }}', + type: 'text', disabled: function(m) { return !m.isNew(); }, + control: 'node-ajax-options', url: 'get_start' + },{ + id: 'prstoken', label: '{{ _('Gettoken Function')}}', group: '{{ _('Definition') }}', + type: 'text', disabled: function(m) { return !m.isNew(); }, + control: 'node-ajax-options', url: 'get_token' + },{ + id: 'prsend', label: '{{ _('End Function')}}', group: '{{ _('Definition') }}', + type: 'text', disabled: function(m) { return !m.isNew(); }, + control: 'node-ajax-options', url: 'get_end' + },{ + id: 'prslextype', label: '{{ _('Lextypes Function')}}', group: '{{ _('Definition') }}', + type: 'text', disabled: function(m) { return !m.isNew(); }, + control: 'node-ajax-options', url: 'get_lextype' + },{ + id: 'prsheadline', label: '{{ _('Headline Function')}}', group: '{{ _('Definition') }}', + type: 'text', disabled: function(m) { return !m.isNew(); }, + control: 'node-ajax-options', url: 'get_headline' + }], + + /* + * Triggers control specific error messages for parser name, + * lexize function and schema, if any one of them is not specified + * while creating new fts parser + */ + validate: function(keys){ + var name = this.get('name'); + var start = this.get('prsstart'); + var token = this.get('prstoken'); + var end = this.get('prsend'); + var lextype = this.get('prslextype'); + var schema = this.get('schema'); + + // Validate fts parser 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 start function control + else if (_.isUndefined(start) || _.isNull(start) || String(start).replace(/^\s+|\s+$/g, '') == '') { + var msg = '{{ _('Start function must be selected!') }}'; + this.errorModel.set('prsstart', msg); + return msg; + } + + // Validate gettoken function control + else if (_.isUndefined(token) || _.isNull(token) || String(token).replace(/^\s+|\s+$/g, '') == '') { + var msg = '{{ _('Gettoken function must be selected!') }}'; + this.errorModel.set('prstoken', msg); + return msg; + } + + // Validate end function control + else if (_.isUndefined(end) || _.isNull(end) || String(end).replace(/^\s+|\s+$/g, '') == '') { + var msg = '{{ _('End function must be selected!') }}'; + this.errorModel.set('prsend', msg); + return msg; + } + + // Validate lextype function control + else if (_.isUndefined(lextype) || _.isNull(lextype) || String(lextype).replace(/^\s+|\s+$/g, '') == '') { + var msg = '{{ _('Lextype function must be selected!') }}'; + this.errorModel.set('prslextype', msg); + return msg; + } + + // Validate schema for fts parser + 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_parser']; +}); \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/create.sql new file mode 100644 index 0000000..de6e5b1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/create.sql @@ -0,0 +1,14 @@ +{# CREATE FTS PARSER Statement #} +{% if data and data.schema and data.name and data.prsstart and data.prstoken and data.prsend and data.prslextype %} +CREATE TEXT SEARCH PARSER {{ conn|qtIdent(data.schema, data.name) }} ( + START = {{data.prsstart}}, + GETTOKEN = {{data.prstoken}}, + END = {{data.prsend}}, + LEXTYPES = {{data.prslextype}}{% if data.prsheadline and data.prsheadline != '-'%}, + HEADLINE = {{data.prsheadline}},{% endif %} +); +{# Description for FTS_PARSER #} +{% if data.description %} +COMMENT ON TEXT SEARCH PARSER {{ 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_parser/templates/fts_parser/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..cf2b533 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/delete.sql @@ -0,0 +1,23 @@ +{# FETCH FTS PARSER NAME Statement #} +{% if pid %} +SELECT + p.prsname AS name, + ( + SELECT + nspname + FROM + pg_namespace + WHERE + oid = t.tmplnamespace + ) as schema +FROM + pg_ts_parser p LEFT JOIN pg_description d + ON d.objoid=p.oid AND d.classoid='pg_ts_parser'::regclass +WHERE + p.oid = {{pid}}::OID; +{% endif %} + +{# DROP FTS PARSER Statement #} +{% if schema and name %} +DROP TEXT SEARCH PARSER {{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_parser/templates/fts_parser/sql/9.1_plus/functions.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/functions.sql new file mode 100644 index 0000000..9597790 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/functions.sql @@ -0,0 +1,58 @@ +{# FETCH start functions for FTS_PARSER #} +{% if start %} +SELECT + proname, nspname +FROM + pg_proc JOIN pg_namespace n ON n.oid=pronamespace +WHERE + proargtypes='2281 23' +ORDER BY proname; +{% endif %} + +{# FETCH token functions for FTS_PARSER #} +{% if token %} +SELECT + proname, nspname +FROM + pg_proc JOIN pg_namespace n ON n.oid=pronamespace +WHERE + proargtypes='2281 2281 2281' +ORDER BY + proname; +{% endif %} + +{# FETCH end functions for FTS_PARSER #} +{% if end %} +SELECT + proname, nspname +FROM + pg_proc JOIN pg_namespace n ON n.oid=pronamespace +WHERE + prorettype=2278 and proargtypes='2281' +ORDER BY + proname; +{% endif %} + +{# FETCH lextype functions for FTS_PARSER #} +{% if lextype %} +SELECT + proname, nspname +FROM + pg_proc JOIN pg_namespace n ON n.oid=pronamespace +WHERE + prorettype=2281 and proargtypes='2281' +ORDER BY + proname; +{% endif %} + +{# FETCH headline functions for FTS_PARSER #} +{% if headline %} +SELECT + proname, nspname +FROM + pg_proc JOIN pg_namespace n ON n.oid=pronamespace +WHERE + proargtypes='2281 2281 3615' +ORDER BY + proname; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/nodes.sql new file mode 100644 index 0000000..b8eb522 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/nodes.sql @@ -0,0 +1,13 @@ +{# FETCH FTS PARSER name statement #} +SELECT + oid, prsname as name +FROM + pg_ts_parser prs +WHERE +{% if scid %} + prs.prsnamespace = {{scid}}::OID +{% elif pid %} + prs.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_parser/templates/fts_parser/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..a79677d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/properties.sql @@ -0,0 +1,30 @@ +{# FETCH properties for FTS PARSER #} +SELECT + prs.oid, + prs.prsname as name, + prs.prsstart, + prs.prstoken, + prs.prsend, + prs.prslextype, + prs.prsheadline, + description, + prs.prsnamespace AS schema +FROM + pg_ts_parser prs + LEFT OUTER JOIN pg_description des +ON + ( + des.objoid=prs.oid + AND des.classoid='pg_ts_parser'::regclass + ) +WHERE +{% if scid %} + prs.prsnamespace = {{scid}}::OID +{% elif name %} + prs.prsname = {{name|qtLiteral}} +{% endif %} +{% if pid %} + AND prs.oid = {{pid}}::OID +{% endif %} +ORDER BY + prs.prsname \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/schema.sql new file mode 100644 index 0000000..29ddaba --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/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_parser prs + ON prs.prsnamespace = nsp.oid +WHERE + prs.oid = {{data.id}}::OID +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/sql.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/sql.sql new file mode 100644 index 0000000..2fa11e6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/sql.sql @@ -0,0 +1,46 @@ +{# Reverse engineered sql for FTS PARSER #} +{% if pid and scid %} +SELECT + array_to_string(array_agg(sql), E'\n\n') as sql +FROM + ( + SELECT + E'-- Text Search Parser: ' || nspname || E'.' || prs.prsname || + E'\n\n-- DROP TEXT SEARCH PARSER ' || nspname || E'.' || prs.prsname || + E'\n\nCREATE TEXT SEARCH PARSER ' || nspname || E'.' || prs.prsname || E' (\n' || + E' START = ' || prs.prsstart || E',\n' || + E' GETTOKEN = ' || prs.prstoken || E',\n' || + E' END = ' || prs.prsend || E',\n' || + E' LEXTYPES = ' || prs.prslextype || + CASE + WHEN prs.prsheadline != '-'::regclass THEN E',\n HEADLINE = ' || prs.prsheadline + ELSE '' END || E'\n);' || + CASE + WHEN description IS NOT NULL THEN + E'\n\nCOMMENT ON TEXT SEARCH TEMPLATE ' || nspname || E'.' || prs.prsname || + E' IS ' || pg_catalog.quote_literal(description) || E';' + ELSE '' END as sql + FROM + pg_ts_parser prs + LEFT JOIN ( + SELECT + des.description as description, + des.objoid as descoid + FROM + pg_description des + WHERE + des.objoid={{pid}}::OID AND des.classoid='pg_ts_parser'::regclass + ) a ON (a.descoid = prs.oid) + LEFT JOIN ( + SELECT + nspname, + nsp.oid as noid + FROM + pg_namespace nsp + WHERE + oid = {{scid}}::OID + ) b ON (b.noid = prs.prsnamespace) +WHERE + prs.oid={{pid}}::OID +) as c; +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/update.sql new file mode 100644 index 0000000..206575d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/templates/fts_parser/sql/9.1_plus/update.sql @@ -0,0 +1,22 @@ +{# UPDATE statement for FTS PARSER #} +{% if data %} +{% if data.name and data.name != o_data.name %} +ALTER TEXT SEARCH PARSER {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(o_data.name)}} + RENAME TO {{data.name}}; +{% endif %} + +{#in case of rename, use new fts template name #} +{% if data.name and data.name != o_data.name %} +{% set name = data.name %} +{% else %} +{% set name = o_data.name %} +{% endif %} +{% if data.schema and data.schema != o_data.schema %} +ALTER TEXT SEARCH PARSER {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}} + SET SCHEMA {{data.schema}}; +{% endif %} +{% if data.description != o_data.description %} +COMMENT ON TEXT SEARCH PARSER {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}} + IS {{ data.description|qtLiteral }}; +{% endif %} +{% endif %} \ No newline at end of file
-- Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers