Aurynn Shaw
This talk discusses Exceptable, a sane exception propagation framework for PG 8.3 and above. We will be covering the need for coherent exception propagation in stored-procedure-based applications, how exceptions are handled in PG 8.3, the tools 8.4 brings regarding raising exceptions from PL/PGSQL, and how a consistent exception model benefits architectures heavily invested in stored procedures. Finally, we will discuss Exceptable, a PL/PGSQL and Python API for consistent handling of exceptions from the Database to the Application Layer.
4. Stored Procedures are Rare
• Most people don’t use Stored Procedures.
Tuesday, October 20, 2009
5. The Present System
Wherein
• A Database Query
• A Stored Procedure
• Or, A Database Query
• Exceptions Galore
Tuesday, October 20, 2009
6. A Database Query
• The App queries the Database
Tuesday, October 20, 2009
7. A Database Query
• The App queries the Database
• Any variety of query
Tuesday, October 20, 2009
8. A Database Query
>>> c.Raw("SELECT * FROM auth_only")()
Traceback (most recent call last):
... <SNIP> ...
psycopg2.ProgrammingError: permission denied for
relation auth_only
Tuesday, October 20, 2009
9. A Database Query
• The App queries the Database
• Any variety of query
• Or A Stored Procedure
Tuesday, October 20, 2009
10. Or a Stored Procedure
CREATE OR REPLACE FUNCTION except_test() RETURNS
VOID AS $$
BEGIN
RAISE EXCEPTION 'Test!';
END;
$$ LANGUAGE PLPGSQL;
>>> c.Function("except_test")()
Traceback (most recent call last):
... <SNIP> ...
psycopg2.InternalError: Test!
Tuesday, October 20, 2009
11. Beget an Exception
• Stored procedures raise InternalErrors
• Every procedural exception becomes this
• Permissions violations raise ProgrammingErrors
• I haven’t tested if this is all PG exceptions.
Tuesday, October 20, 2009
12. Seriously?
• This is what we have to work with?
Tuesday, October 20, 2009
13. Why this is Important
Wherein
• Why?
• Defining your APIs
• Separation of Concerns
• Procedural Spaghetti
Tuesday, October 20, 2009
14. Why?
• Limited information to propagate upwards
• Stored Procedures are Harder to use
• Permissions violations are difficult to detect
Tuesday, October 20, 2009
15. API Undefined
• Exceptions are part of your API
Tuesday, October 20, 2009
16. API Undefined
• Exceptions are part of your API
• Easily overlooked
Tuesday, October 20, 2009
17. API Undefined
• Exceptions are part of your API
• Easily overlooked
• Delineations will make life easier
Tuesday, October 20, 2009
19. Separation of Concerns
• The Database is for Database Logic
• Harder to write Data Logic
Tuesday, October 20, 2009
20. Spaghetti
Early version of our
app didn’t have
Exceptable - we
were left catching
InternalErrors and
guessing at what
• InternalErrors everywhere the error was, based
on timing.
Tuesday, October 20, 2009
21. Spaghetti
• InternalErrors everywhere
• Insufficiency of Information
Tuesday, October 20, 2009
22. Spaghetti
• InternalErrors everywhere
• Insufficiency of Information
• Considerable Repeated Code
Tuesday, October 20, 2009
23. A Saving Grace
• Violating Procedure Signatures ->
Python DataError
Tuesday, October 20, 2009
24. A Better Deal
Wherein
• Database API
• Easy Python implementation
• Universality
• Exceptable
Tuesday, October 20, 2009
25. Database API
• Easier to define an API
Tuesday, October 20, 2009
26. Database API
• Easier to define an API
• The DB becomes part of that API
Tuesday, October 20, 2009
27. Database API
• Easier to define an API
• The DB becomes part of that API
• Simple Stored Procedure interface
Tuesday, October 20, 2009
28. Database API
• Easier to define an API
• The DB becomes part of that API
• Simple Stored Procedure interface
• Easily declare new Exceptions
Tuesday, October 20, 2009
30. Simply Decorated
from exceptable.exceptable import Except
base = Except()
@base
def db_function():
pass
Tuesday, October 20, 2009
31. Wonderfully Python
• A Simple Decorator
• Catches and re-emits Exceptions
• The core of Exceptable
• Easy to Integrate - 2 lines, in Simpycity
Tuesday, October 20, 2009
32. Universality
• Exceptable Procedures never change
• DB logic doesn’t change
• Application support is Easy
Tuesday, October 20, 2009
33. Exceptable
• More Pythonic Database Access
• Better exceptions means better app flow
Tuesday, October 20, 2009
34. Example Code
Wherein
• The DB Library
• The Application Implementation
• Catching Permissions Violations
• PostgreSQL 8.4
Tuesday, October 20, 2009
35. To Start,
CREATE TABLE exceptions (
name text primary key,
description text not null,
parent text references exceptions(name)
);
INSERT INTO exceptions VALUES ('Exception',
'Base exception',NULL);
INSERT INTO exceptions VALUES
('NotFoundException', 'Could not find
specified record', 'Exception');
Tuesday, October 20, 2009
36. Which leads to
CREATE OR REPLACE FUNCTION not_found (
in_reason TEXT
) RETURNS VOID as $body$
SELECT exceptaple.raise(
'NotFoundException',
$1
);
$body$ LANGUAGE SQL;
Tuesday, October 20, 2009
37. Application Level
• Easy to Query the Exception tables
Tuesday, October 20, 2009
38. Application Level
• Easy to Query the Exception tables
• Easy to set up a new library
• Python took 50 lines
Tuesday, October 20, 2009
39. Our Python Example
• The Exceptable decorator is easy to set up
• Designed for DB-API integration
Tuesday, October 20, 2009
40. In the Application
base = Except(InternalError, {
'Exception': Exception,
'NotFoundException': NotFoundError,
})
Tuesday, October 20, 2009
41. Our Python Example
• The Exceptable decorator is easy to set up
• Designed for DB-API integration
• User-defined
Tuesday, October 20, 2009
42. User Definitions
base = Except(InternalError, {
'PermissionError': PermissionError,
'UnknownUser': UnknownUserError,
'NotFoundException': NotFoundError,
})
Tuesday, October 20, 2009
43. Our Python Example
• The Exceptable decorator is easy to set up
• Designed for DB-API integration
• User-defined, and soon, table introspection
Tuesday, October 20, 2009
44. base is a decorator
@base
def db_api(query):
con = db.connect(conn_string)
cur = con.cursor()
return cur(query)
Tuesday, October 20, 2009
45. Which leads to
try:
rs = db_api(‘select * from test_api()’)
except NotFoundError, e:
# A hah! A usable error!
pass
Tuesday, October 20, 2009
46. As Opposed To
try:
rs = db_api(‘select * from test_api()’)
except InternalError, e:
if “NotFoundException” in str(e):
raise NotFoundError(str(e))
elif “PermissionsError” in str(e):
raise PermissionsError(str(e))
Tuesday, October 20, 2009
47. Our Python Example
• The Exceptable decorator is easy to set up
• Designed for DB-API integration
• User-defined, and soon, table introspection
• Existing decorators can be expanded easily
Tuesday, October 20, 2009
48. Grow, my Pretties!
class NotNullError(BaseException):
pass
class SCE(BaseException):
pass
base.add({
‘NotNullException’: NotNullError,
‘SufficientCoffeeException’: SCE
})
Tuesday, October 20, 2009
49. Permission Denied
• Exceptable.py also wraps the base permission
denied
• DB permissions violations work at the app level
Tuesday, October 20, 2009
50. Vertically Challenged
a = c.Function(‘test_auth’);
try:
result = a()
except PermissionDenied, e:
abort(403)
except NoSuchUser, e:
abort(401)
Tuesday, October 20, 2009