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
Ashesh Vashi 2016-01-02 14:54:01 +05:30
parent eb83d57c5a
commit 2aeae06882
5 changed files with 216 additions and 3 deletions

View File

@ -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):

View File

@ -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)

View File

@ -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;

View File

@ -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