Issues (253)

psqlextra/compiler.py (36 issues)

1 1
from django.core.exceptions import SuspiciousOperation
0 ignored issues
show
The import django.core.exceptions could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
2 1
from django.db.models.sql.compiler import SQLInsertCompiler, SQLUpdateCompiler
0 ignored issues
show
The import django.db.models.sql.compiler could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
3
4 1
from psqlextra.expressions import HStoreValue
5
6
7 1
class PostgresReturningUpdateCompiler(SQLUpdateCompiler):
0 ignored issues
show
This class has no __init__ method.
Loading history...
8
    """Compiler for SQL UPDATE statements that return
9
    the primary keys of the affected rows."""
10
11 1
    def execute_sql(self, _result_type):
0 ignored issues
show
The argument _result_type seems to be unused.
Loading history...
12 1
        sql, params = self.as_sql()
13 1
        sql += self._form_returning()
14
15 1
        with self.connection.cursor() as cursor:
0 ignored issues
show
The Instance of PostgresReturningUpdateCompiler does not seem to have a member named connection.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
16 1
            cursor.execute(sql, params)
17 1
            primary_keys = cursor.fetchall()
18
19 1
        return primary_keys
20
21 1
    def as_sql(self):
22 1
        self._prepare_query_values()
23 1
        return super().as_sql()
24
25 1
    def _prepare_query_values(self):
26
        """Extra prep on query values by converting
27
        dictionaries into :see:HStoreValue expressions.
28
29
        This allows putting expressions in a dictionary.
30
        The :see:HStoreValue will take care of resolving
31
        the expressions inside the dictionary."""
32
33 1
        new_query_values = []
34 1
        for field, model, val in self.query.values:
0 ignored issues
show
The Instance of PostgresReturningUpdateCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
35 1
            if isinstance(val, dict):
36 1
                val = HStoreValue(val)
37
38 1
            new_query_values.append((
39
                field,
40
                model,
41
                val
42
            ))
43
44 1
        self.query.values = new_query_values
0 ignored issues
show
The Instance of PostgresReturningUpdateCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
45
46 1
    def _form_returning(self):
47
        """Builds the RETURNING part of the query."""
48
49 1
        qn = self.connection.ops.quote_name
0 ignored issues
show
The Instance of PostgresReturningUpdateCompiler does not seem to have a member named connection.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
50 1
        return ' RETURNING %s' % qn(self.query.model._meta.pk.attname)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _meta was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
The Instance of PostgresReturningUpdateCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
51
52
53 1
class PostgresInsertCompiler(SQLInsertCompiler):
54
    """Compiler for SQL INSERT statements."""
55
56 1
    def __init__(self, *args, **kwargs):
57
        """Initializes a new instance of :see:PostgresInsertCompiler."""
58
59 1
        super().__init__(*args, **kwargs)
60 1
        self.qn = self.connection.ops.quote_name
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named connection.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
61
62 1
    def as_sql(self, return_id=False):
63
        """Builds the SQL INSERT statement."""
64
65 1
        queries = [
66
            self._rewrite_insert(sql, params, return_id)
67
            for sql, params in super().as_sql()
68
        ]
69
70 1
        return queries
71
72 1
    def execute_sql(self, return_id=False):
73
        # execute all the generate queries
74 1
        with self.connection.cursor() as cursor:
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named connection.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
75 1
            rows = []
76 1
            for sql, params in self.as_sql(return_id):
77 1
                cursor.execute(sql, params)
78 1
                rows.extend(cursor.fetchall())
79
80
        # create a mapping between column names and column value
81 1
        return [
82
            {
83
                column.name: row[column_index]
84
                for column_index, column in enumerate(cursor.description) if row
85
            }
86
            for row in rows
87
        ]
88
89 1
    def _rewrite_insert(self, sql, params, return_id=False):
90
        """Rewrites a formed SQL INSERT query to include
91
        the ON CONFLICT clause.
92
93
        Arguments:
94
            sql:
95
                The SQL INSERT query to rewrite.
96
97
            params:
98
                The parameters passed to the query.
99
100
            returning:
101
                What to put in the `RETURNING` clause
102
                of the resulting query.
103
104
        Returns:
105
            A tuple of the rewritten SQL query and new params.
106
        """
107
108 1
        returning = self.qn(self.query.model._meta.pk.attname) if return_id else '*'
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _meta was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
109
110 1
        if self.query.conflict_action.value == 'UPDATE':
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
111 1
            return self._rewrite_insert_update(sql, params, returning)
112 1
        elif self.query.conflict_action.value == 'NOTHING':
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
113 1
            return self._rewrite_insert_nothing(sql, params, returning)
114
115
        raise SuspiciousOperation((
116
            '%s is not a valid conflict action, specify '
117
            'ConflictAction.UPDATE or ConflictAction.NOTHING.'
118
        ) % str(self.query.conflict_action))
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
119
120 1
    def _rewrite_insert_update(self, sql, params, returning):
121
        """Rewrites a formed SQL INSERT query to include
122
        the ON CONFLICT DO UPDATE clause."""
123
124 1
        update_columns = ', '.join([
125
            '{0} = EXCLUDED.{0}'.format(self.qn(field.column))
126
            for field in self.query.update_fields
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
127
        ])
128
129
        # build the conflict target, the columns to watch
130
        # for conflicts
131 1
        conflict_target = self._build_conflict_target()
132
133 1
        index_predicate = self.query.index_predicate
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
134
135 1
        sql_template = (
136
            '{insert} ON CONFLICT {conflict_target} DO UPDATE '
137
            'SET {update_columns} RETURNING {returning}'
138
        )
139
140 1
        if index_predicate:
141 1
            sql_template = (
142
                '{insert} ON CONFLICT {conflict_target} WHERE {index_predicate} DO UPDATE '
143
                'SET {update_columns} RETURNING {returning}'
144
            )
145
146 1
        return (
147
            sql_template.format(
148
                insert=sql,
149
                conflict_target=conflict_target,
150
                update_columns=update_columns,
151
                returning=returning,
152
                index_predicate=index_predicate,
153
            ),
154
            params
155
        )
156
157 1
    def _rewrite_insert_nothing(self, sql, params, returning):
158
        """Rewrites a formed SQL INSERT query to include
159
        the ON CONFLICT DO NOTHING clause."""
160
161
        # build the conflict target, the columns to watch
162
        # for conflicts
163 1
        conflict_target = self._build_conflict_target()
164
165 1
        where_clause = ' AND '.join([
166
            '{0} = %s'.format(self._format_field_name(field_name))
167
            for field_name in self.query.conflict_target
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
168
        ])
169
170 1
        where_clause_params = [
171
            self._format_field_value(field_name)
172
            for field_name in self.query.conflict_target
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
173
        ]
174
175 1
        params = params + tuple(where_clause_params)
176
177
        # this looks complicated, and it is, but it is for a reason... a normal
178
        # ON CONFLICT DO NOTHING doesn't return anything if the row already exists
179
        # so we do DO UPDATE instead that never executes to lock the row, and then
180
        # select from the table in case we're dealing with an existing row..
181 1
        return (
182
            (
183
                'WITH insdata AS ('
184
                '{insert} ON CONFLICT {conflict_target} DO UPDATE'
185
                ' SET {pk_column} = NULL WHERE FALSE RETURNING {returning})'
186
                ' SELECT * FROM insdata UNION ALL'
187
                ' SELECT {returning} FROM {table} WHERE {where_clause} LIMIT 1;'
188
            ).format(
189
                insert=sql,
190
                conflict_target=conflict_target,
191
                pk_column=self.qn(self.query.model._meta.pk.column),
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _meta was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
192
                returning=returning,
193
                table=self.query.objs[0]._meta.db_table,
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _meta was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
194
                where_clause=where_clause
195
            ),
196
            params
197
        )
198
199 1
    def _build_conflict_target(self):
200
        """Builds the `conflict_target` for the ON CONFLICT
201
        clause."""
202
203 1
        conflict_target = []
204
205 1
        if not isinstance(self.query.conflict_target, list):
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
206
            raise SuspiciousOperation((
207
                '%s is not a valid conflict target, specify '
208
                'a list of column names, or tuples with column '
209
                'names and hstore key.'
210
            ) % str(self.query.conflict_target))
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
211
212 1
        def _assert_valid_field(field_name):
213 1
            field_name = self._normalize_field_name(field_name)
214 1
            if self._get_model_field(field_name):
215 1
                return
216
217 1
            raise SuspiciousOperation((
218
                '%s is not a valid conflict target, specify '
219
                'a list of column names, or tuples with column '
220
                'names and hstore key.'
221
            ) % str(field_name))
222
223 1
        for field_name in self.query.conflict_target:
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
224 1
            _assert_valid_field(field_name)
225
226
            # special handling for hstore keys
227 1
            if isinstance(field_name, tuple):
228 1
                conflict_target.append(
229
                    '(%s->\'%s\')' % (
230
                        self._format_field_name(field_name),
231
                        field_name[1]
232
                    )
233
                )
234
            else:
235 1
                conflict_target.append(
236
                    self._format_field_name(field_name))
237
238 1
        return '(%s)' % ','.join(conflict_target)
239
240 1
    def _get_model_field(self, name: str):
241
        """Gets the field on a model with the specified name.
242
243
        Arguments:
244
            name:
245
                The name of the field to look for.
246
247
                This can be both the actual field name, or
248
                the name of the column, both will work :)
249
250
        Returns:
251
            The field with the specified name or None if
252
            no such field exists.
253
        """
254
255 1
        field_name = self._normalize_field_name(name)
256
257
        # 'pk' has special meaning and always refers to the primary
258
        # key of a model, we have to respect this de-facto standard behaviour
259 1
        if field_name == 'pk' and self.query.model._meta.pk:
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _meta was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
260 1
            return self.query.model._meta.pk
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _meta was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
261
262 1
        for field in self.query.model._meta.local_concrete_fields:
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _meta was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
263 1
            if field.name == field_name or field.column == field_name:
264 1
                return field
265
266 1
        return None
267
268 1
    def _format_field_name(self, field_name) -> str:
269
        """Formats a field's name for usage in SQL.
270
271
        Arguments:
272
            field_name:
273
                The field name to format.
274
275
        Returns:
276
            The specified field name formatted for
277
            usage in SQL.
278
        """
279
280 1
        field = self._get_model_field(field_name)
281 1
        return self.qn(field.column)
282
283 1
    def _format_field_value(self, field_name) -> str:
284
        """Formats a field's value for usage in SQL.
285
286
        Arguments:
287
            field_name:
288
                The name of the field to format
289
                the value of.
290
291
        Returns:
292
            The field's value formatted for usage
293
            in SQL.
294
        """
295
296 1
        field_name = self._normalize_field_name(field_name)
297 1
        field = self._get_model_field(field_name)
298
299 1
        return SQLInsertCompiler.prepare_value(
300
            self,
301
            field,
302
            # Note: this deliberately doesn't use `pre_save_val` as we don't
303
            # want things like auto_now on DateTimeField (etc.) to change the
304
            # value. We rely on pre_save having already been done by the
305
            # underlying compiler so that things like FileField have already had
306
            # the opportunity to save out their data.
307
            getattr(self.query.objs[0], field.attname)
0 ignored issues
show
The Instance of PostgresInsertCompiler does not seem to have a member named query.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
308
        )
309
310 1
    def _normalize_field_name(self, field_name) -> str:
0 ignored issues
show
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
311
        """Normalizes a field name into a string by
312
        extracting the field name if it was specified
313
        as a reference to a HStore key (as a tuple).
314
315
        Arguments:
316
            field_name:
317
                The field name to normalize.
318
319
        Returns:
320
            The normalized field name.
321
        """
322
323 1
        if isinstance(field_name, tuple):
324 1
            field_name, _ = field_name
325
326
        return field_name
327