Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ошибка компиляции запроса? #8

Closed
antonio-antuan opened this issue Oct 20, 2017 · 9 comments
Closed

Ошибка компиляции запроса? #8

antonio-antuan opened this issue Oct 20, 2017 · 9 comments

Comments

@antonio-antuan
Copy link
Contributor

Натолкнулся на проблему с компиляцией запросов

engine_clickhouse = create_engine(config['sqlalchemy']['conn_string_clickhouse'],
                                          convert_unicode=True,
                                          echo=config.getboolean('sqlalchemy', 'ECHO_DB'))
SessionClickhouse = ScopedSession(sessionmaker(bind=engine_clickhouse))

from clickhouse_sqlalchemy import types, engines
from clickhouse_sqlalchemy.ext.declarative import get_declarative_base
from sqlalchemy import Column

CHBase = get_declarative_base()

# кусок модельки, ничего необычного
class PixelStats(CHBase):

    ...
    ts_spawn = Column(types.UInt32, primary_key=True)
    ...

Вот такая петрушка в консоли:

>>> from proj.core.model.meta import SessionClickHouse
>>> from proj.core.model_clickhouse import PixelStats
>>> from proj.core.orm_extensions.compiler import Compiler
>>> SessionClickHouse.bind.dialect.name
'clickhouse'

>>> print Compiler.compile_query(SessionClickHouse.query(PixelStats.ts_spawn - PixelStats.ts_spawn % 3600), dialect=SessionClickHouse.bind.dialect)
SELECT ts_spawn - ts_spawn %(ts_spawn_1)s AS anon_1 
FROM pixel_stats
>>> SessionClickHouse.query(PixelStats.ts_spawn - PixelStats.ts_spawn % 3600).first()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2700, in first
    ret = list(self[0:1])
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2498, in __getitem__
    return list(res)
  File "/home/anton/Projects/proj/core/proj/core/model/cachingquery.py", line 106, in __iter__
    return Query.__iter__(self)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2802, in __iter__
    return self._execute_and_instances(context)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2817, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 914, in execute
    return meth(self, multiparams, params)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/sqlalchemy/sql/elements.py", line 323, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1010, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1146, in _execute_context
    context)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1344, in _handle_dbapi_exception
    util.reraise(*exc_info)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1139, in _execute_context
    context)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/clickhouse_sqlalchemy/drivers/base.py", line 435, in do_execute
    cursor.execute(statement, parameters, context=context)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/clickhouse_sqlalchemy/drivers/http/connector.py", line 105, in execute
    self._process_response(response_gen)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/clickhouse_sqlalchemy/drivers/http/connector.py", line 197, in _process_response
    self._columns = next(response, None)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/clickhouse_sqlalchemy/drivers/http/transport.py", line 39, in execute
    r = self._send(query, params=params, stream=True)
  File "/home/anton/Projects/proj/.venv/lib/python2.7/site-packages/clickhouse_sqlalchemy/drivers/http/transport.py", line 77, in _send
    raise DatabaseException(orig)
DatabaseException: Orig exception: Code: 62, e.displayText() = DB::Exception: Syntax error: failed at position 43 (line 1, col 43): AS anon_1 
FROM pixel_stats 
 LIMIT 1 FORMAT TabSeparatedWithNamesAndTypes. Expected one of: Comma, INTO OUTFILE, FROM, WITH, HAVING, SETTINGS, GROUP BY, ORDER BY, UNION ALL, PREWHERE, WHERE, token, LIMIT, FORMAT, e.what() = DB::Exception

Ручной "компилятор":


from psycopg2.extensions import adapt
from sqlalchemy.dialects import postgresql

_pg_dialect = postgresql.dialect()


class Compiler(object):
    encoding = 'utf8'

    @classmethod
    def compile_statement(cls, statement, dialect=_pg_dialect):
        comp = statement.compile(dialect=dialect)
        params = {}
        for k, v in comp.params.items():
            if isinstance(v, unicode):
                v = v.encode(cls.encoding)
            if dialect.name == _pg_dialect.name:
                params[k] = adapt(v)
        return (comp.string.encode(cls.encoding) % params).decode(cls.encoding)

    @classmethod
    def compile_query(cls, query, dialect=_pg_dialect):
        return cls.compile_statement(query.statement, dialect=dialect)

Аналогичная операция прекрасно работает с пг (с другой моделью, со стандартным алхимичный declarative_base() и с пг-шным же engine):

>>> from proj.core.model import Stat
>>> from proj.core.model.meta import Session
>>> Session.bind.dialect.name
'postgresql' 
>>> print Compiler.compile_query(Session.query(Stat.ts_spawn - Stat.ts_spawn % 3600), dialect=Session.bind.dialect)
SELECT stats.ts_spawn - stats.ts_spawn % 3600 AS anon_1 
FROM stats
>>> Session.query(Stat.ts_spawn - Stat.ts_spawn % 3600).first()
(1508162400,)

Понимаю, что есть функции mod в пг и modulo в кликхаусе, но хотелось бы использовать оператор %, именно за счет того, что он одинаков в разных СУБД.

@antonio-antuan
Copy link
Contributor Author

Вывод при echo=True:

2017-10-20 14:03:01,724 INFO sqlalchemy.engine.base.Engine SELECT ts_spawn - ts_spawn % %(ts_spawn_1)s AS anon_1 
FROM pixel_stats 
 LIMIT %(param_1)s FORMAT TabSeparatedWithNamesAndTypes
2017-10-20 14:03:01,724 INFO sqlalchemy.engine.base.Engine {u'param_1': 1, u'ts_spawn_1': 3600}

@xzkostyan
Copy link
Owner

Можно увидеть connect string без логинов и паролей? Вы HTTP или Native-протокол используете?

@antonio-antuan
Copy link
Contributor Author

Что-то типа clickhouse://log-pas@ip:9000 :)
очевидно, http

@xzkostyan
Copy link
Owner

Порт 9000 используется для Native интерфейса по умолчанию, если вы ничего не меняли.

Можете попробовать тот же запрос стрельнуть по clickhouse+native://...? Честно говоря, я планирую в скором времени отказаться от полноценной поддержки HTTP в пользу Native, потому как второй более эффективнее с точки зрения передачи данных. Кроме того там меньше головной боли с парсингом и эскейпингом. Сначала оно будет deprecated, конечно же.

Тем более, что родном протоколе скоро запилят SSL.

@xzkostyan
Copy link
Owner

Зафиксил: 552100e.

В качестве workaround можно пока использовать функцию modulo или поставить из master-а:

pip install -I git+https://github.com/xzkostyan/clickhouse-sqlalchemy@master#egg=clickhouse-sqlalchemy

@antonio-antuan
Copy link
Contributor Author

С нативным клиентом та же беда :(
С фиксом всё робит, спасибо.
Про modulo отписал раньше.

Когда в pypy появится новая версия?

@xzkostyan
Copy link
Owner

Да, это общая проблема, к сожалению.

В PyPI исправление думаю будет в течение недели-двух. Мне хотелось ещё порефакторить кое-что. Да и вообще более плотно заняться проектом.

Кстати, в requirements.txt можно прямо прописать:

git+https://github.com/xzkostyan/clickhouse-sqlalchemy@master#egg=clickhouse-sqlalchemy

Или же ссылку на коммит:

git+https://github.com/xzkostyan/clickhouse-sqlalchemy@552100e346549011d7b1bc9a2adab6e53bb673d1#egg=clickhouse-sqlalchemy

@xzkostyan
Copy link
Owner

xzkostyan commented Nov 6, 2017

Добрый вечер.

Зарелизил версию 0.0.5. Она включает в себя данный фикс.

@antonio-antuan
Copy link
Contributor Author

Спасибо, ждал.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants