diff --git a/edb/pgsql/metaschema.py b/edb/pgsql/metaschema.py index f65182c2d0d..4e8795dca01 100644 --- a/edb/pgsql/metaschema.py +++ b/edb/pgsql/metaschema.py @@ -191,6 +191,61 @@ def __init__(self) -> None: ) +class EvictQueryCacheFunction(dbops.Function): + + text = f''' + DECLARE + evict_sql text; + BEGIN + DELETE FROM "edgedb"."_query_cache" + WHERE "key" = cache_key + RETURNING "evict" INTO evict_sql; + IF evict_sql IS NOT NULL THEN + EXECUTE evict_sql; + END IF; + END; + ''' + + def __init__(self) -> None: + super().__init__( + name=('edgedb', '_evict_query_cache'), + args=[("cache_key", ("uuid",))], + returns=("void",), + language='plpgsql', + volatility='volatile', + text=self.text, + ) + + +class ClearQueryCacheFunction(dbops.Function): + + # TODO(fantix): this may consume a lot of memory in Postgres + text = f''' + DECLARE + row record; + BEGIN + FOR row IN + DELETE FROM "edgedb"."_query_cache" + RETURNING "input", "evict" + LOOP + EXECUTE row."evict"; + RETURN NEXT row."input"; + END LOOP; + END; + ''' + + def __init__(self) -> None: + super().__init__( + name=('edgedb', '_clear_query_cache'), + args=[], + returns=('bytea',), + set_returning=True, + language='plpgsql', + volatility='volatile', + text=self.text, + ) + + class BigintDomain(dbops.Domain): """Bigint: a variant of numeric that enforces zero digits after the dot. @@ -4553,6 +4608,8 @@ async def bootstrap( dbops.CreateTable(DMLDummyTable()), dbops.CreateTable(QueryCacheTable()), dbops.Query(DMLDummyTable.SETUP_QUERY), + dbops.CreateFunction(EvictQueryCacheFunction()), + dbops.CreateFunction(ClearQueryCacheFunction()), dbops.CreateFunction(UuidGenerateV1mcFunction('edgedbext')), dbops.CreateFunction(UuidGenerateV4Function('edgedbext')), dbops.CreateFunction(UuidGenerateV5Function('edgedbext')), diff --git a/edb/server/dbview/dbview.pyx b/edb/server/dbview/dbview.pyx index 4e7d711b74e..0391abdb6fa 100644 --- a/edb/server/dbview/dbview.pyx +++ b/edb/server/dbview/dbview.pyx @@ -28,6 +28,7 @@ import pickle import struct import time import typing +import uuid import weakref import immutables @@ -889,6 +890,11 @@ cdef class DatabaseConnectionView: self._reset_tx_state() return side_effects + async def clear_cache_keys(self, conn) -> list[bytes]: + rows = await conn.sql_fetch(b'SELECT "edgedb"."_clear_query_cache"()') + self._db._eql_to_compiled.clear() + return [row[0] for row in rows or []] + async def apply_config_ops(self, conn, ops): settings = self.get_config_spec() diff --git a/edb/server/protocol/execute.pyx b/edb/server/protocol/execute.pyx index 4d5833b89f6..4695d3fc6ec 100644 --- a/edb/server/protocol/execute.pyx +++ b/edb/server/protocol/execute.pyx @@ -168,7 +168,9 @@ async def execute( await persist_cache(be_conn, dbv, compiled) if query_unit.sql: - if query_unit.ddl_stmt_id: + if query_unit.user_schema: + # TODO(fantix): do this in the same transaction + _recompile_requests = await dbv.clear_cache_keys(be_conn) ddl_ret = await be_conn.run_ddl(query_unit, state) if ddl_ret and ddl_ret['new_types']: new_types = ddl_ret['new_types'] @@ -320,6 +322,10 @@ async def execute_script( data = None try: + if any(query_unit.user_schema for query_unit in unit_group): + # TODO(fantix): do this in the same transaction + _recompile_requests = await dbv.clear_cache_keys(conn) + if conn.last_state == state: # the current status in be_conn is in sync with dbview, skip the # state restoring