Added support for qtIdent, qtTypeIdent, qtLiteral for quoting the
inputs. In order to do the proper quoting around the identifier, different type, and literal, we introduced respective functions qtIdent, qtTypeIdent, qtLiteral in psycopg2 driver. Also, introduced them as the Jinja's custom filter for using it directly inside the templates. Also, created an utility - generate_keywords.py in order to generate keyword lists from the latest PostgreSQL installation.pull/3/head
							parent
							
								
									eb83d57c5a
								
							
						
					
					
						commit
						2aeae06882
					
				| 
						 | 
				
			
			@ -104,6 +104,19 @@ class ServerModule(sg.ServerGroupPluginModule):
 | 
			
		|||
 | 
			
		||||
        return scripts
 | 
			
		||||
 | 
			
		||||
    def register(self, app, options, first_registration=False):
 | 
			
		||||
        """
 | 
			
		||||
        Override the default register function to automagically register
 | 
			
		||||
        sub-modules at once.
 | 
			
		||||
        """
 | 
			
		||||
        if first_registration:
 | 
			
		||||
            from pgadmin.utils.driver import get_driver
 | 
			
		||||
            driver = get_driver(PG_DEFAULT_DRIVER, app)
 | 
			
		||||
            app.jinja_env.filters['qtLiteral'] = driver.qtLiteral
 | 
			
		||||
            app.jinja_env.filters['qtIdent'] = driver.qtIdent
 | 
			
		||||
            app.jinja_env.filters['qtTypeIdent'] = driver.qtTypeIdent
 | 
			
		||||
 | 
			
		||||
        super(ServerModule, self).register(app, options, first_registration)
 | 
			
		||||
 | 
			
		||||
class ServerMenuItem(MenuItem):
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,8 +2,12 @@ from flask import current_app
 | 
			
		|||
from .registry import DriverRegistry
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_driver(type):
 | 
			
		||||
    drivers = getattr(current_app, '_pgadmin_server_drivers', None)
 | 
			
		||||
def get_driver(type, app=None):
 | 
			
		||||
 | 
			
		||||
    if app is not None:
 | 
			
		||||
        DriverRegistry.load_drivers()
 | 
			
		||||
 | 
			
		||||
    drivers = getattr(app or current_app, '_pgadmin_server_drivers', None)
 | 
			
		||||
 | 
			
		||||
    if drivers is None or not isinstance(drivers, dict):
 | 
			
		||||
        drivers = dict()
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +19,7 @@ def get_driver(type):
 | 
			
		|||
 | 
			
		||||
    if driver is not None:
 | 
			
		||||
        drivers[type] = driver
 | 
			
		||||
        setattr(current_app, '_pgadmin_server_drivers', drivers)
 | 
			
		||||
        setattr(app or current_app, '_pgadmin_server_drivers', drivers)
 | 
			
		||||
 | 
			
		||||
    return driver
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +29,8 @@ def init_app(app):
 | 
			
		|||
    setattr(app, '_pgadmin_server_drivers', drivers)
 | 
			
		||||
    DriverRegistry.load_drivers()
 | 
			
		||||
 | 
			
		||||
    return drivers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ping():
 | 
			
		||||
    drivers = getattr(current_app, '_pgadmin_server_drivers', None)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ from datetime import datetime
 | 
			
		|||
 | 
			
		||||
import psycopg2
 | 
			
		||||
import psycopg2.extras
 | 
			
		||||
from psycopg2.extensions import adapt
 | 
			
		||||
 | 
			
		||||
from flask import g, current_app, session
 | 
			
		||||
from flask.ext.babel import gettext
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +21,8 @@ from pgadmin.settings.settings_model import Server, User
 | 
			
		|||
from pgadmin.utils.crypto import encrypt, decrypt
 | 
			
		||||
import random
 | 
			
		||||
 | 
			
		||||
from .keywords import ScanKeyword
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_ = gettext
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -746,3 +749,118 @@ class Driver(BaseDriver):
 | 
			
		|||
                for mgr in [m for m in sess_mgr if isinstance(m,
 | 
			
		||||
                        ServerManager)]:
 | 
			
		||||
                    mgr.release()
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def qtLiteral(value):
 | 
			
		||||
        return adapt(value).getquoted()
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def ScanKeywordExtraLookup(key):
 | 
			
		||||
        # UNRESERVED_KEYWORD      0
 | 
			
		||||
        # COL_NAME_KEYWORD        1
 | 
			
		||||
        # TYPE_FUNC_NAME_KEYWORD  2
 | 
			
		||||
        # RESERVED_KEYWORD        3
 | 
			
		||||
        extraKeywords = {
 | 
			
		||||
            'connect': 3,
 | 
			
		||||
            'convert': 3,
 | 
			
		||||
            'distributed': 0,
 | 
			
		||||
            'exec': 3,
 | 
			
		||||
            'log': 0,
 | 
			
		||||
            'long': 3,
 | 
			
		||||
            'minus': 3,
 | 
			
		||||
            'nocache': 3,
 | 
			
		||||
            'number': 3,
 | 
			
		||||
            'package': 3,
 | 
			
		||||
            'pls_integer': 3,
 | 
			
		||||
            'raw': 3,
 | 
			
		||||
            'return': 3,
 | 
			
		||||
            'smalldatetime': 3,
 | 
			
		||||
            'smallfloat': 3,
 | 
			
		||||
            'smallmoney': 3,
 | 
			
		||||
            'sysdate': 3,
 | 
			
		||||
            'systimestap': 3,
 | 
			
		||||
            'tinyint': 3,
 | 
			
		||||
            'tinytext': 3,
 | 
			
		||||
            'varchar2': 3
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        return (key in extraKeywords and extraKeywords[key]) or ScanKeyword(key)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def needsQuoting(key, forTypes):
 | 
			
		||||
 | 
			
		||||
            value = key.decode()
 | 
			
		||||
            valNoArray = value.decode()
 | 
			
		||||
 | 
			
		||||
            # check if the string is number or not
 | 
			
		||||
            if (isinstance(value, int)):
 | 
			
		||||
                return True;
 | 
			
		||||
            # certain types should not be quoted even though it contains a space. Evilness.
 | 
			
		||||
            elif forTypes and value[-2:] == u"[]":
 | 
			
		||||
                valNoArray = value[:-2]
 | 
			
		||||
 | 
			
		||||
            if forTypes and valNoArray.lower() in [
 | 
			
		||||
                u"bit varying"
 | 
			
		||||
                u"\"char\"",
 | 
			
		||||
                u"character varying",
 | 
			
		||||
                u"double precision"
 | 
			
		||||
                u"timestamp without time zone"
 | 
			
		||||
                u"timestamp with time zone"
 | 
			
		||||
                u"time without time zone"
 | 
			
		||||
                u"time with time zone"
 | 
			
		||||
                u"\"trigger\""
 | 
			
		||||
                u"\"unknown\""
 | 
			
		||||
                ]:
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
            if u'0' <= valNoArray[0] <= u'9':
 | 
			
		||||
                return True
 | 
			
		||||
 | 
			
		||||
            for c in valNoArray:
 | 
			
		||||
                if not (u'a' <= c <= u'z') and c != u'_':
 | 
			
		||||
                    return True
 | 
			
		||||
 | 
			
		||||
            # check string is keywaord or not
 | 
			
		||||
            category = Driver.ScanKeywordExtraLookup(value)
 | 
			
		||||
 | 
			
		||||
            if category is None:
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
            # UNRESERVED_KEYWORD
 | 
			
		||||
            if category == 0:
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
            # COL_NAME_KEYWORD
 | 
			
		||||
            if forTypes and category == 3:
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def qtTypeIdent(value):
 | 
			
		||||
 | 
			
		||||
        if (len(value) == 0):
 | 
			
		||||
            return value
 | 
			
		||||
 | 
			
		||||
        result = value;
 | 
			
		||||
 | 
			
		||||
        if (Driver.needsQuoting(result, True)):
 | 
			
		||||
            result.replace("\"", "\"\"")
 | 
			
		||||
            return "\"" + result + "\""
 | 
			
		||||
        else:
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def qtIdent(value):
 | 
			
		||||
 | 
			
		||||
        if (len(value) == 0):
 | 
			
		||||
            return value
 | 
			
		||||
 | 
			
		||||
        result = value;
 | 
			
		||||
 | 
			
		||||
        if (Driver.needsQuoting(result, False)):
 | 
			
		||||
            result.replace("\"", "\"\"")
 | 
			
		||||
            return "\"" + result + "\""
 | 
			
		||||
        else:
 | 
			
		||||
            return result;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
##########################################################################
 | 
			
		||||
#
 | 
			
		||||
# pgAdmin 4 - PostgreSQL Tools
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2013 - 2015, The pgAdmin Development Team
 | 
			
		||||
# This software is released under the PostgreSQL Licence
 | 
			
		||||
#
 | 
			
		||||
# This allows us to generate to keywords.py for PostgreSQL for used by
 | 
			
		||||
# qtIdent and qtTypeIdent functions for scanning the keywords type.
 | 
			
		||||
#
 | 
			
		||||
# In order to generate keywords.py for specific version of PostgreSQL, put
 | 
			
		||||
# pg_config executable in the PATH.
 | 
			
		||||
#
 | 
			
		||||
##########################################################################
 | 
			
		||||
import re
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
	include_dir = os.popen('pg_config --includedir').read().rstrip()
 | 
			
		||||
	version = os.popen('pg_config --version').read().rstrip()
 | 
			
		||||
 | 
			
		||||
	keywords_file = open('keywords.py', 'w')
 | 
			
		||||
 | 
			
		||||
	keywords_file.write("""##########################################################################
 | 
			
		||||
#
 | 
			
		||||
# pgAdmin 4 - PostgreSQL Tools
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2013 - 2015, The pgAdmin Development Team
 | 
			
		||||
# This software is released under the PostgreSQL Licence
 | 
			
		||||
#
 | 
			
		||||
##########################################################################
 | 
			
		||||
""")
 | 
			
		||||
	keywords_file.write('# ScanKeyword function for ' +  version)
 | 
			
		||||
	keywords_file.write('\n\ndef ScanKeyword(key):')
 | 
			
		||||
	keywords_file.write('\n    keywordDict = {\n')
 | 
			
		||||
 | 
			
		||||
	idx = 0
 | 
			
		||||
 | 
			
		||||
	with open(include_dir + "/postgresql/server/parser/kwlist.h", "rb") as ins:
 | 
			
		||||
 | 
			
		||||
		pattern = re.compile(r'"([^"]+)",\s*[^,]*\s*,\s*(.*)$')
 | 
			
		||||
		keyword_types = [
 | 
			
		||||
				u'UNRESERVED_KEYWORD', u'COL_NAME_KEYWORD',
 | 
			
		||||
				u'TYPE_FUNC_NAME_KEYWORD',	u'RESERVED_KEYWORD'
 | 
			
		||||
				]
 | 
			
		||||
 | 
			
		||||
		for line in ins:
 | 
			
		||||
			line = line.decode().rstrip()
 | 
			
		||||
			if line[0:11] == 'PG_KEYWORD(' and line[-1] == ')':
 | 
			
		||||
				match = pattern.match(line[11:-1])
 | 
			
		||||
				if idx != 0:
 | 
			
		||||
					keywords_file.write(", ")
 | 
			
		||||
				else:
 | 
			
		||||
					keywords_file.write("        ")
 | 
			
		||||
				keywords_file.write(
 | 
			
		||||
					'"' + match.group(1) + u'": ' +
 | 
			
		||||
					str(keyword_types.index(match.group(2)))
 | 
			
		||||
					)
 | 
			
		||||
				idx += 1
 | 
			
		||||
	keywords_file.write('\n        }\n')
 | 
			
		||||
	keywords_file.write('    return (key in keywordDict and keywordDict[key]) or None')
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
		Reference in New Issue