def get_SQL(self, schema, query, **sql_options):
        """
        convert the query instance into SQL

        this is the glue method that translates the generic Query() instance to
        the SQL specific query, this is where the magic happens

        **sql_options -- dict
            count_query -- boolean -- true if this is a count query SELECT
            only_where_clause -- boolean -- true to only return after WHERE ...
        """
        only_where_clause = sql_options.get('only_where_clause', False)
        symbol_map = {
            'in': {'symbol': 'IN', 'list': True},
            'nin': {'symbol': 'NOT IN', 'list': True},
            'is': {'symbol': '=', 'none_symbol': 'IS'},
            'not': {'symbol': '!=', 'none_symbol': 'IS NOT'},
            'gt': {'symbol': '>'},
            'gte': {'symbol': '>='},
            'lt': {'symbol': '<'},
            'lte': {'symbol': '<='},
            # https://www.tutorialspoint.com/postgresql/postgresql_like_clause.htm
            # https://www.tutorialspoint.com/sqlite/sqlite_like_clause.htm
            'like': {'symbol': 'LIKE'},
            'nlike': {'symbol': 'NOT LIKE'},
        }

        query_args = []
        query_str = []

        if not only_where_clause:
            query_str.append('SELECT')
            select_fields = query.fields_select
            if select_fields:
                distinct = "DISTINCT " if select_fields.options.get("unique", False) else ""
                select_fields_str = distinct + ',{}'.format(os.linesep).join(
                    (self._normalize_name(f) for f in select_fields.names())
                )
            else:
                select_fields_str = "*"

            if sql_options.get('count_query', False):
                query_str.append('  count({}) as ct'.format(select_fields_str))

            else:
                query_str.append('  {}'.format(select_fields_str))

            query_str.append('FROM')
            query_str.append("  {}".format(self._normalize_table_name(schema)))

        if query.fields_where:
            query_str.append('WHERE')

            for i, field in enumerate(query.fields_where):
                if i > 0: query_str.append('AND')

                field_str = ''
                field_args = []
                sd = symbol_map[field[0]]

                # field[0], field[1], field[2], field[3]
                _, field_name, field_val, field_kwargs = field
                field_str, field_args = self._normalize_val_SQL(
                    schema,
                    sd,
                    field_name,
                    field_val,
                    field_kwargs
                )

                query_str.append('  {}'.format(field_str))
                query_args.extend(field_args)

        if query.fields_sort:
            query_sort_str = []
            query_str.append('ORDER BY')
            for field in query.fields_sort:
                sort_dir_str = 'ASC' if field[0] > 0 else 'DESC'
                if field[2]:
                    field_sort_str, field_sort_args = self._normalize_sort_SQL(field[1], field[2], sort_dir_str)
                    query_sort_str.append(field_sort_str)
                    query_args.extend(field_sort_args)

                else:
                    query_sort_str.append('  {} {}'.format(field[1], sort_dir_str))

            query_str.append(',{}'.format(os.linesep).join(query_sort_str))

        if query.bounds:
            offset = query.bounds.offset
            limit = 1 if sql_options.get('one_query', False) else query.bounds.limit
            query_str.append('LIMIT {} OFFSET {}'.format(
                limit,
                offset
            ))

        query_str = os.linesep.join(query_str)
        return query_str, query_args