Skip to content

Commit

Permalink
[#718] Use error codes instead of relying on english error messages i…
Browse files Browse the repository at this point in the history
…n datastore
  • Loading branch information
domoritz committed Mar 28, 2013
1 parent 8075070 commit 4d69dfe
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 30 deletions.
56 changes: 26 additions & 30 deletions ckanext/datastore/db.py
Expand Up @@ -11,7 +11,7 @@
import logging
import pprint
import sqlalchemy
from sqlalchemy.exc import ProgrammingError, IntegrityError
from sqlalchemy.exc import ProgrammingError, IntegrityError, DBAPIError
import psycopg2.extras

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -156,15 +156,8 @@ def _is_valid_pg_type(context, type_name):
return True
else:
connection = context['connection']
try:
connection.execute('SELECT %s::regtype', type_name)
except ProgrammingError, e:
if 'invalid type name' in str(e) or 'does not exist' in str(e):
return False
else:
raise
else:
return True
return connection.execute('SELECT is_valid_type(%s)',
type_name).first()[0]


def _get_type(context, oid):
Expand Down Expand Up @@ -963,23 +956,24 @@ def create(context, data_dict):
trans.commit()
return _unrename_json_field(data_dict)
except IntegrityError, e:
if ('duplicate key value violates unique constraint' in str(e)
or 'could not create unique index' in str(e)):
if int(e.orig.pgcode) == 23505:
raise ValidationError({
'constraints': ['Cannot insert records or create index because of uniqueness constraint'],
'constraints': ['Cannot insert records or create index because '
'of uniqueness constraint'],
'info': {
'details': str(e)
}
})
else:
raise
except Exception, e:
trans.rollback()
if 'due to statement timeout' in str(e):
raise
except DBAPIError, e:
if int(e.orig.pgcode) == 57014:
raise ValidationError({
'query': ['Query took too long']
})
raise
except Exception, e:
trans.rollback()
raise
finally:
context['connection'].close()

Expand All @@ -1005,22 +999,24 @@ def upsert(context, data_dict):
trans.commit()
return _unrename_json_field(data_dict)
except IntegrityError, e:
if 'duplicate key value violates unique constraint' in str(e):
if int(e.orig.pgcode) == 23505:
raise ValidationError({
'constraints': ['Cannot insert records because of uniqueness constraint'],
'constraints': ['Cannot insert records or create index because '
'of uniqueness constraint'],
'info': {
'details': str(e)
}
})
else:
raise
except Exception, e:
trans.rollback()
if 'due to statement timeout' in str(e):
raise
except DBAPIError, e:
if int(e.orig.pgcode) == 57014:
raise ValidationError({
'query': ['Query took too long']
})
raise
except Exception, e:
trans.rollback()
raise
finally:
context['connection'].close()

Expand Down Expand Up @@ -1079,8 +1075,8 @@ def search(context, data_dict):
data_dict['resource_id'])]
})
return search_data(context, data_dict)
except Exception, e:
if 'due to statement timeout' in str(e):
except DBAPIError, e:
if int(e.orig.pgcode) == 57014:
raise ValidationError({
'query': ['Search took too long']
})
Expand Down Expand Up @@ -1112,10 +1108,10 @@ def search_sql(context, data_dict):
'orig': [str(e.orig)]
}
})
except Exception, e:
if 'due to statement timeout' in str(e):
except DBAPIError, e:
if int(e.orig.pgcode) == 57014:
raise ValidationError({
'query': ['Search took too long']
'query': ['Query took too long']
})
raise
finally:
Expand Down
20 changes: 20 additions & 0 deletions ckanext/datastore/plugin.py
Expand Up @@ -105,6 +105,7 @@ def new_resource_show(context, data_dict):
if not hasattr(resource_show, '_datastore_wrapped'):
new_resource_show._datastore_wrapped = True
logic._actions['resource_show'] = new_resource_show
self._add_is_valid_type_function()

def _is_read_only_database(self):
for url in [self.ckan_url, self.write_url, self.read_url]:
Expand Down Expand Up @@ -205,6 +206,25 @@ def _create_alias_table(self):
{'connection_url': pylons.config['ckan.datastore.write_url']}).connect()
connection.execute(create_alias_table_sql)

def _add_is_valid_type_function(self):
# syntax_error - may occur if someone provides a keyword as a type
# undefined_object - is raised if the type does not exist
create_func_sql = '''
CREATE OR REPLACE FUNCTION is_valid_type(v_type text)
RETURNS boolean
AS $$
BEGIN
PERFORM v_type::regtype;
RETURN true;
EXCEPTION WHEN undefined_object OR syntax_error THEN
RETURN false;
END;
$$ LANGUAGE plpgsql stable;
'''
connection = db._get_engine(None,
{'connection_url': pylons.config['ckan.datastore.write_url']}).connect()
connection.execute(create_func_sql)

def get_actions(self):
actions = {'datastore_create': action.datastore_create,
'datastore_upsert': action.datastore_upsert,
Expand Down

0 comments on commit 4d69dfe

Please sign in to comment.