コード例 #1
0
ファイル: test_tokens.py プロジェクト: prawn-cake/pg_requests
    def test_token_with_filter_value(self):
        token = Token(template='WHERE {}', value_type=FilterValue)
        token.value = {'a': 1, 'b__gt': 2, 'c__lt': 3, 'd__gte': 4,
                       'e__lte': 5, 'f__eq': 'test_eq', 'g__neq': 'test_neq',
                       'h__in': ['p1', 2], 'i__is': True, 'j__is_not': None}

        self.assertIsInstance(token.value.value, And)
        sql_template, values = token.eval()
        expected_parts = (
            "WHERE",
            "AND",
            "a = %s",
            "b > %s",
            "c < %s",
            "d >= %s",
            "e <= %s",
            "f = %s",
            "g != %s",
            "h IN %s",
            "i IS %s",
            "j IS NOT %s",
        )
        expected_values = (
            1, 2, 3, 4, 5, 'test_eq', 'test_neq', ['p1', 2], True, None)
        for part in expected_parts:
            self.assertIn(part, sql_template,
                          '%s is not found in\n%s' % (part, sql_template))
        for val in expected_values:
            self.assertIn(val, values,
                          '%s is not found in\n%s' % (val, expected_values))
コード例 #2
0
ファイル: test_tokens.py プロジェクト: prawn-cake/pg_requests
 def test_subtoken_eval(self):
     t = Token(template='FROM {}', value_type=StringValue,
               subtoken=Token(template='({})', value_type=TupleValue))
     t.value = 'my_fn'
     t.subtoken.value = ('test', 2, True, )
     result = t.eval()
     self.assertEqual(result, ('FROM my_fn(%s, %s, %s)', ('test', 2, True)))
コード例 #3
0
ファイル: test_tokens.py プロジェクト: prawn-cake/pg_requests
 def test_token_with_iterable_value(self):
     token = Token(template='({})', value_type=CommaValue)
     with self.assertRaises(ValueError) as err:
         token.value = 'a'
         self.assertIn('must be list or tuple', str(err))
     token.value = ['a', 'b', 'c']
     self.assertEqual(token.eval(), "(a, b, c)")
コード例 #4
0
ファイル: test_tokens.py プロジェクト: prawn-cake/pg_requests
    def test_token_with_tuple_value_type(self):
        token = Token(template='VALUES ({})', value_type=TupleValue)
        with self.assertRaises(ValueError) as err:
            token.value = 'a'
            self.assertIn('must be list or tuple', str(err))

        token.value = ('a', 3)
        self.assertEqual(token.eval(), ("VALUES (%s, %s)", ('a', 3)),)
コード例 #5
0
ファイル: test_tokens.py プロジェクト: prawn-cake/pg_requests
 def test_token_with_dict_value_and_join_types(self):
     token = Token(template='{join_type} {table_name}',
                   value_type=DictValue)
     # Check correctness of all join types
     for join_type in ('INNER', 'CROSS', 'LEFT_OUTER', 'RIGHT_OUTER',
                       'FULL_OUTER'):
         token.value = dict(join_type=getattr(JOIN, join_type),
                            table_name='MyTable')
         self.assertIsInstance(token.value, DictValue, token.value)
         self.assertEqual(
             token.eval(), "{} MyTable".format(getattr(JOIN, join_type)))
コード例 #6
0
ファイル: test_tokens.py プロジェクト: prawn-cake/pg_requests
 def test_subtoken_eval_with_multiple_tuple_values(self):
     """Check correctness of composing tuple str and tuple values
     This is not a real case
     """
     t = Token(template='({})', value_type=TupleValue,
               subtoken=Token(template='({})', value_type=TupleValue))
     t.value = ('val', 1, False, )
     t.subtoken.value = ('subtoken_val', 2, True, )
     result = t.eval()
     expected = ('(%s, %s, %s)(%s, %s, %s)',
                 ('val', 1, False, 'subtoken_val', 2, True,))
     self.assertEqual(result, expected)
コード例 #7
0
ファイル: test_tokens.py プロジェクト: prawn-cake/pg_requests
    def test_token_required(self):
        token = Token(template='INSERT INTO {}', value_type=StringValue,
                      required=True)
        with self.assertRaises(TokenError) as err:
            token.eval()
            self.assertIn('is not set and required', str(err))

        token.value = 'reports'
        val = token.eval()
        self.assertIsInstance(val, str)
コード例 #8
0
ファイル: query.py プロジェクト: prawn-cake/pg_requests
class UpdateQuery(QueryBuilder):
    TOKENS = OrderedDict([
        ('UPDATE',
         Token(template='UPDATE {}', value_type=StringValue, required=True)),
        ('SET',
         Token(template='SET {}', value_type=CommaDictValue, required=True)),
        ('FROM', Token(template='FROM {}', value_type=StringValue)),
        ('WHERE', Token(template='WHERE {}', value_type=FilterValue)),
    ])

    # NOTE: this is a full copy from SelectQuery
    def filter(self, *args, **kwargs):
        token = self._get_token('WHERE')
        new_value = None
        if args:
            # In case of QueryOperators
            new_value = args[0]
        elif kwargs:
            # In case of simple key-value filters
            new_value = kwargs

        # Stub to prevent errors
        if new_value is None:
            return self

        if token.value:
            token.value.update(new_value)
        else:
            self._set_token_value('WHERE', new_value)
        return self

    def update(self, table_name):
        """Update a table

        :param table_name: str
        :return: self
        """
        sanitized_tn = self._sanitize_table_name(table_name)
        self._set_token_value('UPDATE', sanitized_tn)
        return self

    def data(self, **kwargs):
        self._set_token_value('SET', kwargs)
        return self

    def _from(self, table_name):
        """Update FROM (JOIN in fact) postgres syntax

        SQL representation:
            UPDATE accounts
                SET contact_first_name = first_name,
                    contact_last_name = last_name
            FROM salesmen
            WHERE salesmen.id = accounts.sales_id;

        :param table_name: str
        :return: self
        """
        sanitized_tn = self._sanitize_table_name(table_name)
        self._set_token_value('FROM', sanitized_tn)
        return self
コード例 #9
0
ファイル: query.py プロジェクト: prawn-cake/pg_requests
class InsertQuery(QueryBuilder):
    """Insert query builder.

    Query example:

    >>> insert('users')\
        .data(name='Alex', gender='M')\
        .returning('id')\
        .execute(cursor)

    """

    TOKENS = OrderedDict([
        ('INSERT', Token(template='INSERT INTO {}', value_type=StringValue)),
        ('DEFAULT', Token(template='DEFAULT VALUES', value_type=NullValue)),

        # part of: INSERT INTO table ({fields})
        ('fields', Token(template='({})', value_type=CommaValue)),
        ('VALUES', Token(template='VALUES ({})', value_type=TupleValue)),
        # ('values_multi', Token(template='VALUES %s')),  # for multiple rows
        ('RETURNING', Token(template='RETURNING {}', value_type=CommaValue))
    ])

    def insert(self, table_name):
        self._set_table_name('INSERT', table_name)
        return self

    def data(self, **kwargs):
        """Insert values data

        :return:
        """
        self._set_token_value('fields', tuple(kwargs.keys()))
        self._set_token_value('VALUES', tuple(kwargs.values()))
        return self

    def values_multi(self, list_of_values):
        """Method allows to build multiple rows insert

        :param list_of_values: Iterable: list of multiple rows values
        For example:
            [('Alex', 'M'), ('Jane', 'F')]

        """
        if isinstance(list_of_values, Iterable):
            prepared_values = [', '.join(item) for item in list_of_values]
            self._set_token_value(
                'values_multi',
                # prepare rows by join of prepared_values
                ', '.join(['({})'.format(item) for item in prepared_values]))
        return self

    def defaults(self):
        """Allows to insert row with all defaults values
        Simulate the following: INSERT INTO {table_name} DEFAULT VALUES'
        """
        # reset tokens exclude table name
        self._reset_tokens(exclude=('INSERT', ))
        self._set_token_value('DEFAULT', True)
        return self

    def returning(self, *fields):
        fields = list(filter(None, fields))
        self._set_token_value('RETURNING', fields)
        return self
コード例 #10
0
ファイル: query.py プロジェクト: prawn-cake/pg_requests
class SelectQuery(QueryBuilder):
    """Select query builder.
    The idea is to use builder pattern to make select query to database with
    query tokens.

    Example:
    >>> from pg_requests import query_facade as qf
    >>> qf.select('MyTable').fields('a', 'b').filter(score__gt=0).order_by('a').desc()
    """
    TOKENS = OrderedDict([
        ('SELECT',
         Token(template='SELECT {}', value_type=CommaValue, required=True)),
        # Table tokens
        ('FROM', Token(template='FROM {}', value_type=StringValue)),
        ('FROM__ALIAS', Token(template="AS '{}' ", value_type=StringValue)),

        # User-function tokens
        # Use sub-token to glue token str value + sub-token value without space
        ('FROM__FN',
         Token(template='FROM {}',
               value_type=StringValue,
               subtoken=Token(template='({})', value_type=TupleValue))),
        ('FROM__FN_NAME', Token(template='FROM {}', value_type=StringValue)),
        ('FROM__FN_ARGS', Token(template='({})', value_type=TupleValue)),
        ('JOIN',
         Token(template='{join_type} {table_name}', value_type=DictValue)),
        # NOTE: JOIN__ON and JOIN__USING mutual exclusive
        # FIXME: JOIN__ON must be conditional type
        ('JOIN__ON', Token(template='ON ({})', value_type=StringValue)),
        ('JOIN__USING', Token(template='USING ({})', value_type=CommaValue)),

        # NOTE: here is quite complex logic, see ConditionalValue imp
        ('WHERE', Token(template='WHERE {}', value_type=FilterValue)),
        ('GROUP_BY', Token(template='GROUP BY {}', value_type=CommaValue)),

        # TODO: add tests for having
        ('GROUP_BY__HAVING', Token(template='HAVING {}',
                                   value_type=FilterValue)),
        ('ORDER_BY', Token(template='ORDER BY {}', value_type=CommaValue)),
        ('DESC', Token(template='DESC', value_type=NullValue)),
        ('LIMIT', Token(template='LIMIT {}', value_type=StringValue)),
        ('OFFSET', Token(template='OFFSET {}', value_type=StringValue)),
    ])

    def fields(self, *fields):
        """Select fields to fetch

        :param fields: list of str
        :return: self
        """

        # Filter False parameters
        fields = list(filter(None, fields))
        if fields:
            # Substitute as SELECT %s, %s...
            self._set_token_value('SELECT', fields)
        else:
            self._set_token_value('SELECT', '*')
        return self

    def call_fn(self, fn_name, args):
        """Call user-defined function, like
        SELECT * FROM my_function('param1', 2, True)

        :param fn_name: str: function name
        :param args: tuple: function arguments tuple
        :return: function result
        """
        self.fields('*')._set_token_value('FROM__FN', fn_name)
        # Custom setter for sub-token
        token = self._get_token('FROM__FN')
        # NOTE: subtoken value will be glued with the main token value without
        # a space
        token.subtoken.value = args
        return self

    def select(self, table_name, alias=None):
        """Select from a table. It means SQL FROM operator.

        :param table_name: str: table name
        :param alias: alias for a table - 'AS' keyword
        """
        # TODO: add sub-query feature + custom frontend function

        # Set default selection fields as '*'
        sanitized_tn = self._sanitize_table_name(table_name)
        self.fields('*')._set_token_value('FROM', sanitized_tn)

        # NOTE: SELECT * FROM <table_name> AS <alias>
        if alias is not None:
            self._set_token_value('FROM__ALIAS', alias)
        return self

    def join(self, table_name, join_type=JOIN.INNER, on=None, using=None):
        if join_type not in JOIN:
            raise ValueError("Wrong join type '%r', must be '%r'" %
                             (join_type, JOIN))
        self._set_token_value('JOIN',
                              dict(join_type=join_type, table_name=table_name))

        # FIXME: JOIN__ON must be conditional type or not ???
        if on is not None:
            self._set_token_value('JOIN__ON', on)
        elif using is not None:
            self._set_token_value('JOIN__USING', using)

        return self

    def filter(self, *args, **kwargs):
        token = self._get_token('WHERE')
        new_value = None
        if args:
            # In case of QueryOperators
            new_value = args[0]
        elif kwargs:
            # In case of simple key-value filters
            new_value = kwargs

        # Stub to prevent errors
        if new_value is None:
            return self

        if token.value:
            token.value.update(new_value)
        else:
            self._set_token_value('WHERE', new_value)
        return self

    # NOTE: python 3 syntax only
    # def order_by(self, *args, desc=False):
    def order_by(self, *args, **kwargs):
        args = list(filter(None, args))
        if args:
            self._set_token_value('ORDER_BY', args)

        # Descending order option
        if kwargs.get('desc'):
            self._set_token_value('DESC', True)
        return self

    def desc(self):
        self._set_token_value('DESC', True)
        return self

    def limit(self, value):
        self._set_token_value('LIMIT', int(value))
        return self

    def offset(self, value):
        self._set_token_value('OFFSET', int(value))
        return self

    def group_by(self, *args):
        args = list(filter(None, args))
        if args:
            self._set_token_value('GROUP_BY', args)
        return self

    def having(self, *args, **kwargs):
        if args:
            self._set_token_value('GROUP_BY__HAVING', args[0])
        elif kwargs:
            self._set_token_value('GROUP_BY__HAVING', kwargs)
        return self
コード例 #11
0
ファイル: test_tokens.py プロジェクト: prawn-cake/pg_requests
 def test_token_with_null_value(self):
     token = Token(template='DEFAULT VALUES', value_type=NullValue)
     token.value = "any value shouldn't be appeared"
     self.assertIsInstance(token.value, NullValue)
     self.assertIsInstance(token.value, NullValue)
     self.assertEqual(token.eval(), "DEFAULT VALUES")
コード例 #12
0
ファイル: test_tokens.py プロジェクト: prawn-cake/pg_requests
 def test_token_with_string_value(self):
     token = Token(template='INSERT INTO {}', value_type=StringValue)
     token.value = 'MyTable'
     self.assertIsInstance(token.value, StringValue, token.value)
     self.assertEqual(token.eval(), "INSERT INTO MyTable")