# Path: src/infi/clickhouse_orm/utils.py
# def comma_join(items, stringify=False):
#     """
#     Joins an iterable of strings with commas.
#     """
#     if stringify:
#         return ', '.join(str(item) for item in items)
#     else:
#         return ', '.join(items)
# 
# def string_or_func(obj):
#     return obj.to_sql() if hasattr(obj, 'to_sql') else obj
# 
# def arg_to_sql(arg):
#     """
#     Converts a function argument to SQL string according to its type.
#     Supports functions, model fields, strings, dates, datetimes, timedeltas, booleans,
#     None, numbers, timezones, arrays/iterables.
#     """
#     from infi.clickhouse_orm import Field, StringField, DateTimeField, DateField, F, QuerySet
#     if isinstance(arg, F):
#         return arg.to_sql()
#     if isinstance(arg, Field):
#         return "`%s`" % arg
#     if isinstance(arg, str):
#         return StringField().to_db_string(arg)
#     if isinstance(arg, datetime):
#         return "toDateTime(%s)" % DateTimeField().to_db_string(arg)
#     if isinstance(arg, date):
#         return "toDate('%s')" % arg.isoformat()
#     if isinstance(arg, timedelta):
#         return "toIntervalSecond(%d)" % int(arg.total_seconds())
#     if isinstance(arg, bool):
#         return str(int(arg))
#     if isinstance(arg, tzinfo):
#         return StringField().to_db_string(arg.tzname(None))
#     if arg is None:
#         return 'NULL'
#     if isinstance(arg, QuerySet):
#         return "(%s)" % arg
#     if isinstance(arg, tuple):
#         return '(' + comma_join(arg_to_sql(x) for x in arg) + ')'
#     if is_iterable(arg):
#         return '[' + comma_join(arg_to_sql(x) for x in arg) + ']'
#     return str(arg)


import pytz
from copy import copy, deepcopy
from math import ceil
from datetime import date, datetime
from .utils import comma_join, string_or_func, arg_to_sql
        from infi.clickhouse_orm.funcs import F
        from .funcs import F
        from .database import Page
        from .engines import CollapsingMergeTree, ReplacingMergeTree

    def as_sql(self):
        """
        Returns the whole query as a SQL string.
        """
        distinct = 'DISTINCT ' if self._distinct else ''
        final = ' FINAL' if self._final else ''
        table_name = '`%s`' % self._model_cls.table_name()
        if self._model_cls.is_system_model():
            table_name = '`system`.' + table_name
        params = (distinct, self.select_fields_as_sql(), table_name, final)
        sql = u'SELECT %s%s\nFROM %s%s' % params

        if self._prewhere_q and not self._prewhere_q.is_empty:
            sql += '\nPREWHERE ' + self.conditions_as_sql(prewhere=True)

        if self._where_q and not self._where_q.is_empty:
            sql += '\nWHERE ' + self.conditions_as_sql(prewhere=False)

        if self._grouping_fields:
            sql += '\nGROUP BY %s' % comma_join('`%s`' % field for field in self._grouping_fields)

            if self._grouping_with_totals:
                sql += ' WITH TOTALS'

        if self._order_by:
            sql += '\nORDER BY ' + self.order_by_as_sql()

        if self._limit_by:
            sql += '\nLIMIT %d, %d' % self._limit_by

# Path: src/infi/clickhouse_orm/utils.py
# def comma_join(items, stringify=False):
#     """
#     Joins an iterable of strings with commas.
#     """
#     if stringify:
#         return ', '.join(str(item) for item in items)
#     else:
#         return ', '.join(items)
# 
# def string_or_func(obj):
#     return obj.to_sql() if hasattr(obj, 'to_sql') else obj
# 
# def arg_to_sql(arg):
#     """
#     Converts a function argument to SQL string according to its type.
#     Supports functions, model fields, strings, dates, datetimes, timedeltas, booleans,
#     None, numbers, timezones, arrays/iterables.
#     """
#     from infi.clickhouse_orm import Field, StringField, DateTimeField, DateField, F, QuerySet
#     if isinstance(arg, F):
#         return arg.to_sql()
#     if isinstance(arg, Field):
#         return "`%s`" % arg
#     if isinstance(arg, str):
#         return StringField().to_db_string(arg)
#     if isinstance(arg, datetime):
#         return "toDateTime(%s)" % DateTimeField().to_db_string(arg)
#     if isinstance(arg, date):
#         return "toDate('%s')" % arg.isoformat()
#     if isinstance(arg, timedelta):
#         return "toIntervalSecond(%d)" % int(arg.total_seconds())
#     if isinstance(arg, bool):
#         return str(int(arg))
#     if isinstance(arg, tzinfo):
#         return StringField().to_db_string(arg.tzname(None))
#     if arg is None:
#         return 'NULL'
#     if isinstance(arg, QuerySet):
#         return "(%s)" % arg
#     if isinstance(arg, tuple):
#         return '(' + comma_join(arg_to_sql(x) for x in arg) + ')'
#     if is_iterable(arg):
#         return '[' + comma_join(arg_to_sql(x) for x in arg) + ']'
#     return str(arg)


# Path: src/infi/clickhouse_orm/query.py
import pytz
from copy import copy, deepcopy
from math import ceil
from datetime import date, datetime
from .utils import comma_join, string_or_func, arg_to_sql
        from infi.clickhouse_orm.funcs import F
        from .funcs import F
        from .database import Page
        from .engines import CollapsingMergeTree, ReplacingMergeTree


    def as_sql(self):
        """
        Returns the whole query as a SQL string.
        """
        distinct = 'DISTINCT ' if self._distinct else ''
        final = ' FINAL' if self._final else ''
        table_name = '`%s`' % self._model_cls.table_name()
        if self._model_cls.is_system_model():
            table_name = '`system`.' + table_name
        params = (distinct, self.select_fields_as_sql(), table_name, final)
        sql = u'SELECT %s%s\nFROM %s%s' % params

        if self._prewhere_q and not self._prewhere_q.is_empty:
            sql += '\nPREWHERE ' + self.conditions_as_sql(prewhere=True)

        if self._where_q and not self._where_q.is_empty:
            sql += '\nWHERE ' + self.conditions_as_sql(prewhere=False)

        if self._grouping_fields:
            sql += '\nGROUP BY %s' % comma_join('`%s`' % field for field in self._grouping_fields)

            if self._grouping_with_totals:
                sql += ' WITH TOTALS'

        if self._order_by:
            sql += '\nORDER BY ' + self.order_by_as_sql()

        if self._limit_by:
            sql += '\nLIMIT %d, %d' % self._limit_by

            sql += ' BY %s' % comma_join(string_or_func(field) for field in self._limit_by_fields)