dimagi / django-cte

Common Table Expressions (CTE) for Django
Other
334 stars 46 forks source link

CTEUpdateQueryCompiler as_sql passes extraneous arguments to superclass method #7

Open MisterNando opened 6 years ago

MisterNando commented 6 years ago

CTEUpdateQueryCompiler inherits from SQLUpdateCompiler, and overrides its as_sql method. However, the overridden method accepts arguments, whereas the parent method does not*, and the child method tries to pass those arguments to the parent method, resulting in a TypeError (see below).

*at least not in the current version of Django, and as far back as 1.11.4

Child method:

class CTEUpdateQueryCompiler(SQLUpdateCompiler):

    def as_sql(self, *args, **kwargs):
        def _as_sql():
            return super(CTEUpdateQueryCompiler, self).as_sql(*args, **kwargs)
        return CTECompiler.generate_sql(self.connection, self.query, _as_sql)

Parent method:

class SQLUpdateCompiler(SQLCompiler):
    def as_sql(self):
        """
        Creates the SQL for this query. Returns the SQL string and list of
        parameters.
        """
        ...

Observed error:

[07/Sep/2018 16:22:22]::ERROR::exception_logger.py:6::as_sql() takes exactly 1 argument (3 given) 
  File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 185, in _get_response 
     response = wrapped_callback(request, *callback_args, **callback_kwargs) 
   File "/usr/local/lib/python2.7/dist-packages/newrelic/hooks/framework_django.py", line 544, in wrapper 
     return wrapped(*args, **kwargs) 
   File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py", line 58, in wrapped_view 
     return view_func(*args, **kwargs) 
   File "/usr/local/lib/python2.7/dist-packages/django/views/generic/base.py", line 68, in view 
     return self.dispatch(request, *args, **kwargs) 
   File "/usr/local/lib/python2.7/dist-packages/newrelic/hooks/component_djangorestframework.py", line 46, in _nr_wrapper_APIView_dispatch_ 
     return wrapped(*args, **kwargs) 
   File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py", line 489, in dispatch 
     response = self.handle_exception(exc) 
   File "/usr/local/lib/python2.7/dist-packages/newrelic/hooks/component_djangorestframework.py", line 53, in _handle_exception_wrapper 
     return wrapped(*args, **kwargs) 
   File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py", line 449, in handle_exception 
     self.raise_uncaught_exception(exc) 
   File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py", line 486, in dispatch 
     response = handler(request, *args, **kwargs) 
   File "./asset_api/api/asset_checkin/api.py", line 35, in post 
     raise e 
MisterNando commented 6 years ago

Tagging @millerdev , per @czue's recommendation.

millerdev commented 6 years ago

@MisterNando this is a strange error. I'm unable to tell from the given traceback how as_sql() is being called with extra arguments. Can you find out what code is calling as_sql() when the error occurs?

If you cannot do that, can you find out what the values of args and kwargs are when the error occurs? Maybe temporarily add a print statement to the method and capture the output:

class CTEUpdateQueryCompiler(SQLUpdateCompiler):

    def as_sql(self, *args, **kwargs):
        print(args, kwargs)  # <---- capture the output of this when the error occurs
        def _as_sql():
            return super(CTEUpdateQueryCompiler, self).as_sql(*args, **kwargs)
        return CTECompiler.generate_sql(self.connection, self.query, _as_sql)
MisterNando commented 6 years ago

Thanks for responding, @millerdev ! I can no longer reproduce the conditions that resulted in the error; however, I was able to dig up a more detailed stacktrace from our log archives:

[07/Sep/2018 09:00:00]::ERROR::api.py:34::as_sql() takes exactly 1 argument (3 given)
  File "./asset_api/api/asset_checkin/api.py", line 27, in post
    handler.resend_jobs_on_agent_request()
  File "./asset_api/api/asset_checkin/utils.py", line 95, in resend_jobs_on_agent_request
    ).update(outcome_id=Globals.Outcome.Errored)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 647, in update
    rows = query.get_compiler(self.db).execute_sql(CURSOR)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 1199, in execute_sql
    cursor = super(SQLUpdateCompiler, self).execute_sql(result_type)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 871, in execute_sql
    sql, params = self.as_sql()
  File "/usr/local/lib/python2.7/dist-packages/django_cte/query.py", line 113, in as_sql
    return CTECompiler.generate_sql(self.connection, self.query, _as_sql)
  File "/usr/local/lib/python2.7/dist-packages/django_cte/query.py", line 80, in generate_sql
    base_sql, base_params = as_sql()
  File "/usr/local/lib/python2.7/dist-packages/django_cte/query.py", line 112, in _as_sql
    return super(CTEUpdateQueryCompiler, self).as_sql(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 1187, in as_sql
    where, params = self.compile(self.query.where)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 373, in compile
    sql, params = node.as_sql(self, self.connection)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/where.py", line 79, in as_sql
    sql, params = compiler.compile(child)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 373, in compile
    sql, params = node.as_sql(self, self.connection)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/lookups.py", line 381, in as_sql
    return super(In, self).as_sql(compiler, connection)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/lookups.py", line 170, in as_sql
    rhs_sql, rhs_params = self.process_rhs(compiler, connection)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/lookups.py", line 372, in process_rhs
    return super(In, self).process_rhs(compiler, connection)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/lookups.py", line 229, in process_rhs
    return super(FieldGetDbPrepValueIterableMixin, self).process_rhs(compiler, connection)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/lookups.py", line 100, in process_rhs
    sql, params = compiler.compile(value)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 373, in compile
    sql, params = node.as_sql(self, self.connection)
  File "/usr/local/lib/python2.7/dist-packages/django_cte/query.py", line 113, in as_sql
    return CTECompiler.generate_sql(self.connection, self.query, _as_sql)
  File "/usr/local/lib/python2.7/dist-packages/django_cte/query.py", line 80, in generate_sql
    base_sql, base_params = as_sql()
  File "/usr/local/lib/python2.7/dist-packages/django_cte/query.py", line 112, in _as_sql
    return super(CTEUpdateQueryCompiler, self).as_sql(*args, **kwargs)
TypeError: as_sql() takes exactly 1 argument (3 given)

It looks like the Django SQLCompiler is calling as_sql() with itself and the DB connection as arguments (line 373 in compiler.py).

Does this provide you with the information that you need to proceed with your debugging?