mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-02-07 12:50:32 +03:00
Generating the whole errors file from script
This commit is contained in:
parent
0bce58d0cd
commit
782fa39647
1515
lib/errors.py
1515
lib/errors.py
File diff suppressed because it is too large
Load Diff
|
@ -123,6 +123,10 @@ exception_from_sqlstate(const char *sqlstate)
|
|||
PyErr_Clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* IMPORTANT: if you change anything in this function you should change
|
||||
* make_errors.py accordingly.
|
||||
*/
|
||||
switch (sqlstate[0]) {
|
||||
case '0':
|
||||
switch (sqlstate[1]) {
|
||||
|
|
210
scripts/make_errors.py
Executable file
210
scripts/make_errors.py
Executable file
|
@ -0,0 +1,210 @@
|
|||
#!/usr/bin/env python
|
||||
"""Generate the errors module from PostgreSQL source code.
|
||||
|
||||
The script can be run at a new PostgreSQL release to refresh the module.
|
||||
"""
|
||||
|
||||
# Copyright (C) 2018 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||
#
|
||||
# psycopg2 is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
# License for more details.
|
||||
from __future__ import print_function
|
||||
|
||||
import re
|
||||
import sys
|
||||
import urllib2
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print("usage: %s /path/to/errors.py" % sys.argv[0], file=sys.stderr)
|
||||
return 2
|
||||
|
||||
filename = sys.argv[1]
|
||||
|
||||
file_start = read_base_file(filename)
|
||||
# If you add a version to the list fix the docs (in errors.rst)
|
||||
classes, errors = fetch_errors(
|
||||
['9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '10'])
|
||||
|
||||
f = open(filename, "w")
|
||||
for line in file_start:
|
||||
print(line, file=f)
|
||||
for line in generate_module_data(classes, errors):
|
||||
print(line, file=f)
|
||||
|
||||
|
||||
def read_base_file(filename):
|
||||
rv = []
|
||||
for line in open(filename):
|
||||
rv.append(line.rstrip("\n"))
|
||||
if line.startswith("# autogenerated"):
|
||||
return rv
|
||||
|
||||
raise ValueError("can't find the separator. Is this the right file?")
|
||||
|
||||
|
||||
def parse_errors_txt(url):
|
||||
classes = {}
|
||||
errors = defaultdict(dict)
|
||||
|
||||
page = urllib2.urlopen(url)
|
||||
for line in page:
|
||||
# Strip comments and skip blanks
|
||||
line = line.split('#')[0].strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Parse a section
|
||||
m = re.match(r"Section: (Class (..) - .+)", line)
|
||||
if m:
|
||||
label, class_ = m.groups()
|
||||
classes[class_] = label
|
||||
continue
|
||||
|
||||
# Parse an error
|
||||
m = re.match(r"(.....)\s+(?:E|W|S)\s+ERRCODE_(\S+)(?:\s+(\S+))?$", line)
|
||||
if m:
|
||||
errcode, macro, spec = m.groups()
|
||||
# skip errcodes without specs as they are not publically visible
|
||||
if not spec:
|
||||
continue
|
||||
errlabel = spec.upper()
|
||||
errors[class_][errcode] = errlabel
|
||||
continue
|
||||
|
||||
# We don't expect anything else
|
||||
raise ValueError("unexpected line:\n%s" % line)
|
||||
|
||||
return classes, errors
|
||||
|
||||
|
||||
errors_txt_url = \
|
||||
"http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;" \
|
||||
"f=src/backend/utils/errcodes.txt;hb=%s"
|
||||
|
||||
|
||||
def fetch_errors(versions):
|
||||
classes = {}
|
||||
errors = defaultdict(dict)
|
||||
|
||||
for version in versions:
|
||||
print(version, file=sys.stderr)
|
||||
tver = tuple(map(int, version.split()[0].split('.')))
|
||||
tag = '%s%s_STABLE' % (
|
||||
(tver[0] >= 10 and 'REL_' or 'REL'),
|
||||
version.replace('.', '_'))
|
||||
c1, e1 = parse_errors_txt(errors_txt_url % tag)
|
||||
classes.update(c1)
|
||||
|
||||
for c, cerrs in e1.items():
|
||||
errors[c].update(cerrs)
|
||||
|
||||
return classes, errors
|
||||
|
||||
|
||||
def generate_module_data(classes, errors):
|
||||
tmpl = """
|
||||
|
||||
class %(cls)s(%(base)s):
|
||||
pass
|
||||
|
||||
_by_sqlstate[%(errcode)r] = %(cls)s\
|
||||
"""
|
||||
for clscode, clslabel in sorted(classes.items()):
|
||||
if clscode in ('00', '01'):
|
||||
# success and warning - never raised
|
||||
continue
|
||||
|
||||
yield "\n\n# %s" % clslabel
|
||||
|
||||
for errcode, errlabel in sorted(errors[clscode].items()):
|
||||
clsname = errlabel.title().replace('_', '')
|
||||
yield tmpl % {
|
||||
'cls': clsname,
|
||||
'base': get_base_class_name(errcode),
|
||||
'errcode': errcode
|
||||
}
|
||||
|
||||
|
||||
def get_base_class_name(errcode):
|
||||
"""
|
||||
This is a python porting of exception_from_sqlstate code in pqpath.c
|
||||
"""
|
||||
if errcode[0] == '0':
|
||||
if errcode[1] == 'A': # Class 0A - Feature Not Supported
|
||||
return 'NotSupportedError'
|
||||
elif errcode[0] == '2':
|
||||
if errcode[1] in '01':
|
||||
# Class 20 - Case Not Found
|
||||
# Class 21 - Cardinality Violation
|
||||
return 'ProgrammingError'
|
||||
elif errcode[1] == '2': # Class 22 - Data Exception
|
||||
return 'DataError'
|
||||
elif errcode[1] == '3': # Class 23 - Integrity Constraint Violation
|
||||
return 'IntegrityError'
|
||||
elif errcode[1] in '45':
|
||||
# Class 24 - Invalid Cursor State
|
||||
# Class 25 - Invalid Transaction State
|
||||
return 'InternalError'
|
||||
elif errcode[1] in '678':
|
||||
# Class 26 - Invalid SQL Statement Name
|
||||
# Class 27 - Triggered Data Change Violation
|
||||
# Class 28 - Invalid Authorization Specification
|
||||
return 'OperationalError'
|
||||
elif errcode[1] in 'BDF':
|
||||
# Class 2B - Dependent Privilege Descriptors Still Exist
|
||||
# Class 2D - Invalid Transaction Termination
|
||||
# Class 2F - SQL Routine Exception
|
||||
return 'InternalError'
|
||||
elif errcode[0] == '3':
|
||||
if errcode[1] == '4': # Class 34 - Invalid Cursor Name
|
||||
return 'OperationalError'
|
||||
if errcode[1] in '89B':
|
||||
# Class 38 - External Routine Exception
|
||||
# Class 39 - External Routine Invocation Exception
|
||||
# Class 3B - Savepoint Exception
|
||||
return 'InternalError'
|
||||
if errcode[1] in 'DF':
|
||||
# Class 3D - Invalid Catalog Name
|
||||
# Class 3F - Invalid Schema Name
|
||||
return 'ProgrammingError'
|
||||
elif errcode[0] == '4':
|
||||
if errcode[1] == '0': # Class 40 - Transaction Rollback
|
||||
return 'TransactionRollbackError'
|
||||
if errcode[1] in '24':
|
||||
# Class 42 - Syntax Error or Access Rule Violation
|
||||
# Class 44 - WITH CHECK OPTION Violation
|
||||
return 'ProgrammingError'
|
||||
elif errcode[0] == '5':
|
||||
if errcode == "57014":
|
||||
return 'QueryCanceledError'
|
||||
# Class 53 - Insufficient Resources
|
||||
# Class 54 - Program Limit Exceeded
|
||||
# Class 55 - Object Not In Prerequisite State
|
||||
# Class 57 - Operator Intervention
|
||||
# Class 58 - System Error (errors external to PostgreSQL itself)
|
||||
else:
|
||||
return 'OperationalError'
|
||||
elif errcode[0] == 'F': # Class F0 - Configuration File Error
|
||||
return 'InternalError'
|
||||
elif errcode[0] == 'H': # Class HV - Foreign Data Wrapper Error (SQL/MED)
|
||||
return 'OperationalError'
|
||||
elif errcode[0] == 'P': # Class P0 - PL/pgSQL Error
|
||||
return 'InternalError'
|
||||
elif errcode[0] == 'X': # Class XX - Internal Error
|
||||
return 'InternalError'
|
||||
|
||||
return 'DatabaseError'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
Loading…
Reference in New Issue
Block a user