mirror of
https://github.com/Infinidat/infi.clickhouse_orm.git
synced 2025-02-21 10:40:34 +03:00
add ServerError exception
This commit is contained in:
parent
92ab21c99a
commit
57112f9de6
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
import requests
|
import requests
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from .models import ModelBase
|
from .models import ModelBase
|
||||||
|
@ -24,6 +25,46 @@ class DatabaseException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ServerError(DatabaseException):
|
||||||
|
"""
|
||||||
|
Raised when a server returns an error.
|
||||||
|
"""
|
||||||
|
def __init__(self, message):
|
||||||
|
self.code = None
|
||||||
|
processed = self.get_error_code_msg(message)
|
||||||
|
if processed:
|
||||||
|
self.code, self.message = processed
|
||||||
|
else:
|
||||||
|
# just skip custom init
|
||||||
|
# if non-standard message format
|
||||||
|
super(ServerError, self).__init__(message)
|
||||||
|
|
||||||
|
ERROR_PATTERN = re.compile(r'''
|
||||||
|
Code:\ (?P<code>\d+),
|
||||||
|
\ e\.displayText\(\)\ =\ (?P<type1>[^ \n]+):\ (?P<msg>.+?),
|
||||||
|
\ e.what\(\)\ =\ (?P<type2>[^ \n]+)
|
||||||
|
''', re.VERBOSE | re.DOTALL)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_error_code_msg(cls, full_error_message):
|
||||||
|
"""
|
||||||
|
Extract the code and message of the exception that clickhouse-server generated.
|
||||||
|
|
||||||
|
See the list of error codes here:
|
||||||
|
https://github.com/yandex/ClickHouse/blob/master/dbms/src/Common/ErrorCodes.cpp
|
||||||
|
"""
|
||||||
|
match = cls.ERROR_PATTERN.match(full_error_message)
|
||||||
|
if match:
|
||||||
|
# assert match.group('type1') == match.group('type2')
|
||||||
|
return int(match.group('code')), match.group('msg')
|
||||||
|
|
||||||
|
return 0, full_error_message
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.code is not None:
|
||||||
|
return "{} ({})".format(self.message, self.code)
|
||||||
|
|
||||||
|
|
||||||
class Database(object):
|
class Database(object):
|
||||||
'''
|
'''
|
||||||
Database instances connect to a specific ClickHouse database for running queries,
|
Database instances connect to a specific ClickHouse database for running queries,
|
||||||
|
@ -250,7 +291,7 @@ class Database(object):
|
||||||
params = self._build_params(settings)
|
params = self._build_params(settings)
|
||||||
r = requests.post(self.db_url, params=params, data=data, stream=stream)
|
r = requests.post(self.db_url, params=params, data=data, stream=stream)
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
raise DatabaseException(r.text)
|
raise ServerError(r.text)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def _build_params(self, settings):
|
def _build_params(self, settings):
|
||||||
|
@ -281,8 +322,8 @@ class Database(object):
|
||||||
try:
|
try:
|
||||||
r = self._send('SELECT timezone()')
|
r = self._send('SELECT timezone()')
|
||||||
return pytz.timezone(r.text.strip())
|
return pytz.timezone(r.text.strip())
|
||||||
except DatabaseException:
|
except ServerError as e:
|
||||||
logger.exception('Cannot determine server timezone, assuming UTC')
|
logger.exception('Cannot determine server timezone (%s), assuming UTC', e)
|
||||||
return pytz.utc
|
return pytz.utc
|
||||||
|
|
||||||
def _is_connection_readonly(self):
|
def _is_connection_readonly(self):
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from infi.clickhouse_orm.database import Database, DatabaseException
|
from infi.clickhouse_orm.database import ServerError
|
||||||
from .base_test_with_data import *
|
from .base_test_with_data import *
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,14 +131,22 @@ class DatabaseTestCase(TestCaseWithData):
|
||||||
self.assertEqual(results, "Whitney\tDurham\t1977-09-15\t1.72\nWhitney\tScott\t1971-07-04\t1.7\n")
|
self.assertEqual(results, "Whitney\tDurham\t1977-09-15\t1.72\nWhitney\tScott\t1971-07-04\t1.7\n")
|
||||||
|
|
||||||
def test_invalid_user(self):
|
def test_invalid_user(self):
|
||||||
with self.assertRaises(DatabaseException):
|
with self.assertRaises(ServerError) as cm:
|
||||||
Database(self.database.db_name, username='default', password='wrong')
|
Database(self.database.db_name, username='default', password='wrong')
|
||||||
|
|
||||||
|
exc = cm.exception
|
||||||
|
self.assertEqual(exc.code, 193)
|
||||||
|
self.assertEqual(exc.message, 'Wrong password for user default')
|
||||||
|
|
||||||
def test_nonexisting_db(self):
|
def test_nonexisting_db(self):
|
||||||
db = Database('db_not_here', autocreate=False)
|
db = Database('db_not_here', autocreate=False)
|
||||||
with self.assertRaises(DatabaseException):
|
with self.assertRaises(ServerError) as cm:
|
||||||
db.create_table(Person)
|
db.create_table(Person)
|
||||||
|
|
||||||
|
exc = cm.exception
|
||||||
|
self.assertEqual(exc.code, 81)
|
||||||
|
self.assertEqual(exc.message, "Database db_not_here doesn't exist")
|
||||||
|
|
||||||
def test_preexisting_db(self):
|
def test_preexisting_db(self):
|
||||||
db = Database(self.database.db_name, autocreate=False)
|
db = Database(self.database.db_name, autocreate=False)
|
||||||
db.count(Person)
|
db.count(Person)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from infi.clickhouse_orm.database import DatabaseException
|
from infi.clickhouse_orm.database import DatabaseException, ServerError
|
||||||
from .base_test_with_data import *
|
from .base_test_with_data import *
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,22 +12,31 @@ class ReadonlyTestCase(TestCaseWithData):
|
||||||
orig_database = self.database
|
orig_database = self.database
|
||||||
try:
|
try:
|
||||||
self.database = Database(orig_database.db_name, username=username, readonly=True)
|
self.database = Database(orig_database.db_name, username=username, readonly=True)
|
||||||
with self.assertRaises(DatabaseException):
|
with self.assertRaises(ServerError) as cm:
|
||||||
self._insert_and_check(self._sample_data(), len(data))
|
self._insert_and_check(self._sample_data(), len(data))
|
||||||
|
self._check_db_readonly_err(cm.exception)
|
||||||
|
|
||||||
self.assertEquals(self.database.count(Person), 100)
|
self.assertEquals(self.database.count(Person), 100)
|
||||||
list(self.database.select('SELECT * from $table', Person))
|
list(self.database.select('SELECT * from $table', Person))
|
||||||
with self.assertRaises(DatabaseException):
|
with self.assertRaises(ServerError) as cm:
|
||||||
self.database.drop_table(Person)
|
self.database.drop_table(Person)
|
||||||
with self.assertRaises(DatabaseException):
|
self._check_db_readonly_err(cm.exception)
|
||||||
|
|
||||||
|
with self.assertRaises(ServerError) as cm:
|
||||||
self.database.drop_database()
|
self.database.drop_database()
|
||||||
except DatabaseException as e:
|
self._check_db_readonly_err(cm.exception)
|
||||||
if 'Unknown user' in six.text_type(e):
|
except ServerError as e:
|
||||||
|
if e.code == 192 and e.message.startswith('Unknown user'):
|
||||||
raise unittest.SkipTest('Database user "%s" is not defined' % username)
|
raise unittest.SkipTest('Database user "%s" is not defined' % username)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
self.database = orig_database
|
self.database = orig_database
|
||||||
|
|
||||||
|
def _check_db_readonly_err(self, exc):
|
||||||
|
self.assertEqual(exc.code, 164)
|
||||||
|
self.assertEqual(exc.message, 'Cannot execute query in readonly mode')
|
||||||
|
|
||||||
def test_readonly_db_with_default_user(self):
|
def test_readonly_db_with_default_user(self):
|
||||||
self._test_readonly_db('default')
|
self._test_readonly_db('default')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user