Repository: elastic/apm-agent-python

Issue:
Incompatible with asyncpg 0.29.0
**Describe the bug**: 
![image](https://github.com/elastic/apm-agent-python/assets/9943520/a86e2bf6-c3a8-4370-a7e7-ded81206ac74)

```
IndexError: tuple index out of range
  File "app/core/repositories/user.py", line 155, in find_all
    async with self.engine.begin() as conn:
  File "contextlib.py", line 199, in __aenter__
    return await anext(self.gen)
  File "sqlalchemy/ext/asyncio/engine.py", line 1061, in begin
    async with conn:
  File "sqlalchemy/ext/asyncio/base.py", line 125, in __aenter__
    return await self.start(is_ctxmanager=True)
  File "sqlalchemy/ext/asyncio/engine.py", line 270, in start
    await greenlet_spawn(self.sync_engine.connect)
  File "sqlalchemy/util/_concurrency_py3k.py", line 190, in greenlet_spawn
    result = context.throw(*sys.exc_info())
  File "sqlalchemy/engine/base.py", line 3268, in connect
    return self._connection_cls(self)
  File "sqlalchemy/engine/base.py", line 145, in __init__
    self._dbapi_connection = engine.raw_connection()
  File "sqlalchemy/engine/base.py", line 3292, in raw_connection
    return self.pool.connect()
  File "sqlalchemy/pool/base.py", line 452, in connect
    return _ConnectionFairy._checkout(self)
  File "sqlalchemy/pool/base.py", line 1269, in _checkout
    fairy = _ConnectionRecord.checkout(pool)
  File "sqlalchemy/pool/base.py", line 716, in checkout
    rec = pool._do_get()
  File "sqlalchemy/pool/impl.py", line 169, in _do_get
    with util.safe_reraise():
  File "sqlalchemy/util/langhelpers.py", line 146, in __exit__
    raise exc_value.with_traceback(exc_tb)
  File "sqlalchemy/pool/impl.py", line 167, in _do_get
    return self._create_connection()
  File "sqlalchemy/pool/base.py", line 393, in _create_connection
    return _ConnectionRecord(self)
  File "sqlalchemy/pool/base.py", line 678, in __init__
    self.__connect()
  File "sqlalchemy/pool/base.py", line 916, in __connect
    )._exec_w_sync_on_first_run(self.dbapi_connection, self)
  File "sqlalchemy/event/attr.py", line 473, in _exec_w_sync_on_first_run
    self(*args, **kw)
  File "sqlalchemy/event/attr.py", line 487, in __call__
    fn(*args, **kw)
  File "sqlalchemy/engine/create.py", line 704, in on_connect
    do_on_connect(dbapi_connection)
  File "sqlalchemy/dialects/postgresql/asyncpg.py", line 1247, in connect
    conn.await_(self.setup_asyncpg_json_codec(conn))
  File "sqlalchemy/util/_concurrency_py3k.py", line 125, in await_only
    return current.driver.switch(awaitable)  # type: ignore[no-any-return]
  File "sqlalchemy/util/_concurrency_py3k.py", line 185, in greenlet_spawn
    value = await result
  File "sqlalchemy/dialects/postgresql/asyncpg.py", line 1173, in setup_asyncpg_json_codec
    await asyncpg_connection.set_type_codec(
  File "asyncpg/connection.py", line 1339, in set_type_codec
    typeinfo = await self._introspect_type(typename, schema)
  File "asyncpg/connection.py", line 543, in _introspect_type
    rows = await self._execute(
  File "asyncpg/connection.py", line 1794, in _execute
    result, _ = await self.__execute(
  File "asyncpg/connection.py", line 1892, in __execute
    result, stmt = await self._do_execute(
  File "asyncpg/connection.py", line 1945, in _do_execute
    result = await executor(stmt, None)
  File "elasticapm/instrumentation/packages/asyncio/asyncpg.py", line 65, in call
    query = self.get_query(method, args)
  File "elasticapm/instrumentation/packages/asyncio/asyncpg.py", line 62, in get_query
    return args[0].query
```

**To Reproduce**

- install asyncpg==0.29.0 and elastic-apm==6.19.0 together
- perform any request to postgresql without params

**Environment (please complete the following information)**
- OS: Linux 5.16
- Python version: 3.10
- Agent version: 6.19.0
- asyncpg version: 0.29.0
- sqlalchemy version: 2.0.23







Discussion:

Resolving patch:
diff --git a/elasticapm/contrib/asgi.py b/elasticapm/contrib/asgi.py
--- a/elasticapm/contrib/asgi.py
+++ b/elasticapm/contrib/asgi.py
@@ -92,13 +92,15 @@ async def __call__(self, scope: "Scope", receive: "ASGIReceiveCallable", send: "
                 body = str(body_raw, errors="ignore")
 
                 # Dispatch to the ASGI callable
-                async def wrapped_receive():
+                async def new_wrapped_receive():
                     if messages:
                         return messages.pop(0)
 
                     # Once that's done we can just await any other messages.
                     return await receive()
 
+                wrapped_receive = new_wrapped_receive
+
             await set_context(lambda: self.get_data_from_request(scope, constants.TRANSACTION, body), "request")
 
         try:
diff --git a/elasticapm/instrumentation/packages/asyncio/asyncpg.py b/elasticapm/instrumentation/packages/asyncio/asyncpg.py
--- a/elasticapm/instrumentation/packages/asyncio/asyncpg.py
+++ b/elasticapm/instrumentation/packages/asyncio/asyncpg.py
@@ -55,14 +55,17 @@ class AsyncPGInstrumentation(AsyncAbstractInstrumentedModule):
         ("asyncpg.protocol.protocol", "Protocol.copy_out"),
     ]
 
-    def get_query(self, method, args):
+    def get_query(self, method, args, kwargs=None):
         if method in ["Protocol.query", "Protocol.copy_in", "Protocol.copy_out"]:
             return args[0]
-        else:
+        elif args:
             return args[0].query
+        else:
+            # asyncpg>=0.29 pass data as kwargs
+            return kwargs["state"].query
 
     async def call(self, module, method, wrapped, instance, args, kwargs):
-        query = self.get_query(method, args)
+        query = self.get_query(method, args, kwargs)
         name = extract_signature(query)
         sql_string = shorten(query, string_length=10000)
         context = {"db": {"type": "sql", "statement": sql_string}}


Test patch:
diff --git a/tests/requirements/reqs-asyncpg-0.28.txt b/tests/requirements/reqs-asyncpg-0.28.txt
new file mode 100644
--- /dev/null
+++ b/tests/requirements/reqs-asyncpg-0.28.txt
@@ -0,0 +1,2 @@
+asyncpg==0.28
+-r reqs-base.txt