Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1q9kUT-0006gu-MU for pgsql-hackers@arkaria.postgresql.org; Thu, 15 Jun 2023 10:46:09 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1q9kUS-0002Tj-KA for pgsql-hackers@arkaria.postgresql.org; Thu, 15 Jun 2023 10:46:08 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1q9kUS-0002TZ-AX for pgsql-hackers@lists.postgresql.org; Thu, 15 Jun 2023 10:46:08 +0000 Received: from mail.postgrespro.ru ([93.174.131.139]) by magus.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1q9kUN-002VwK-F2 for pgsql-hackers@lists.postgresql.org; Thu, 15 Jun 2023 10:46:07 +0000 Received: from [10.4.14.209] (unknown [93.174.131.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client did not present a certificate) (Authenticated sender: y.sokolov@postgrespro.ru) by mail.postgrespro.ru (Postfix/465) with ESMTPSA id D947CE20E1F for ; Thu, 15 Jun 2023 13:46:02 +0300 (MSK) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=postgrespro.ru; s=mx2023; t=1686825962; bh=lA9eqOi3ElzDgCa/RKLXeQYob4Uhft/elCZ1tDXlOTk=; h=Message-ID:Date:User-Agent:Subject:To:References:From:In-Reply-To: From; b=PLqW25NB8q0ov/Tc7pvhonEvJW6n3n4kNsqk586EP8fBiWvAXNooQ7kO7DhoegOtJ XeLspyFVegmp654mq6ElKRz3FJjnl4T78SFHKI/Qi7KJOQpUTI/YN3HjJ+emrEjQfE cuqD307++8HaPPFSOrLFt2WM2b3nRdpNY2v8/0ew6f5UUyUfqYiliuTajYROjFua1P pi+pNHIV8ysEF57YChm+tYfOAhiwbalKMxO1F2kqKSWSZzIpTyZn+EEBTDoicpm5W+ sw/6PdRu9EvemLPj5yN3lId/sfLnujCFLP6AYyvJ0tQnJEi1ABgeTM9lqcGUZ/SS8w u4hLMCaxtmnDQ== Message-ID: <4de6a4f5-1c1e-2dbd-a650-38f90baa12e9@postgrespro.ru> Date: Thu, 15 Jun 2023 13:46:02 +0300 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.11.0 Subject: Re: When IMMUTABLE is not. Content-Language: en-US To: pgsql-hackers@lists.postgresql.org References: <389c986d-fbb4-c644-9280-db7836af7ca9@postgrespro.ru> From: Yura Sokolov In-Reply-To: <389c986d-fbb4-c644-9280-db7836af7ca9@postgrespro.ru> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Sorry, previous message were smashed for some reason. I'll try to repeat I found, than declaration of function as IMMUTABLE/STABLE is not enough to be sure function doesn't manipulate data. In fact, SPI checks only direct function kind, but fails to check indirect call. Attached immutable_not.sql creates 3 functions: - `immutable_direct` is IMMUTABLE and tries to insert into table directly.   PostgreSQL correctly detects and forbids this action. - `volatile_direct` is VOLATILE and inserts into table directly.   It is allowed and executed well. - `immutable_indirect` is IMMUTABLE and calls `volatile_direct`.   PostgreSQL failed to detect and prevent this DML manipulation. Output: select immutable_direct('immutable_direct'); psql:immutable_not.sql:28: ERROR:  INSERT is not allowed in a non-volatile function CONTEXT:  SQL statement "insert into xxx values(j)" PL/pgSQL function immutable_direct(character varying) line 3 at SQL statement select volatile_direct('volatile_direct'); volatile_direct ----------------- volatile_direct (1 row) select immutable_indirect('immutable_indirect'); immutable_indirect -------------------- immutable_indirect (1 row) select * from xxx;         i -------------------- volatile_direct immutable_indirect (2 rows) Attached forbid-non-volatile-mutations.diff add checks readonly function didn't made data manipulations. Output for patched version: select immutable_indirect('immutable_indirect'); psql:immutable_not.sql:32: ERROR:  Damn2! Update were done in a non-volatile function CONTEXT:  SQL statement "SELECT volatile_direct(j)" PL/pgSQL function immutable_indirect(character varying) line 3 at PERFORM I doubt check should be done this way. This check is necessary, but it should be FATAL instead of ERROR. And ERROR should be generated at same place, when it is generated for `immutable_direct`, but with check of "read_only" status through whole call stack instead of just direct function kind. ----- regards, Yura Sokolov Postgres Professional