Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1x4iTF-007Koj-35 for pgsql-hackers@arkaria.postgresql.org; Thu, 10 Sep 2026 17:21:58 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1x4iTE-007yl7-39 for pgsql-hackers@arkaria.postgresql.org; Thu, 10 Sep 2026 17:21:56 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1x4iTE-007ykv-1n for pgsql-hackers@lists.postgresql.org; Thu, 10 Sep 2026 17:21:56 +0000 Received: from mail-ot1-x329.google.com ([2607:f8b0:4864:20::329]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1x4iTC-00000004zkR-0tyV for pgsql-hackers@lists.postgresql.org; Thu, 10 Sep 2026 17:21:55 +0000 Received: by mail-ot1-x329.google.com with SMTP id 46e09a7af769-7f4cc6797b7so3733a34.0 for ; Thu, 10 Sep 2026 10:21:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789060913; x=1789665713; darn=lists.postgresql.org; h=content-transfer-encoding:content-type:in-reply-to:from :content-language:references:cc:to:subject:user-agent:mime-version :date:message-id:from:to:cc:subject:date:message-id:reply-to :content-type; bh=rAW58wHk6zqCh1LJMTK+pZ6o7a3Vydmw1FM3vn7u9vs=; b=l06iEnn7bQ7OM3FY39VWtnwTP6YfcYTltBXl7CAgjHcG/njqXIMQ2rXtuLi2JQl04w ktAB4+Y8YU3tcqPv62uEBe/rV8KzPCD7+j7F65MFWfXCqnfOKDGtCqXVJVhRNrOWMl8Q G7zXIH30ncOUOnpsHD0o8YFJIiRnvgsqUs8SYULMN7fM3Uhx3UI99B2Ne3cYOl0WeIQO sphMJ85E2U7E7R988MMI49uSrJTrcvnAyNb6jDLvfAWsa9tpCNX7HFYwm7JLbH6HjJqW sRCRy7Xv6HeDOE8yrALZiape3k+wV5kKtettFUeKnrD6zkP/wwiFxfQkEvHY0rTzLXsl wzGg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789060913; x=1789665713; h=content-transfer-encoding:content-type:in-reply-to:from :content-language:references:cc:to:subject:user-agent:mime-version :date:message-id:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=rAW58wHk6zqCh1LJMTK+pZ6o7a3Vydmw1FM3vn7u9vs=; b=mCfYLOSXdyF/WaA7SwrEg0JL5LDPE7IwlKWx/7CgWDB+JOGM1s1NecQnYhYbf+RH8n q6X1wL/AsU1LMt9ScQY8qeR5tRZvGKW1bvqmlhW9TAIRjxCtvlV/7gj7sKOERtMsmLF5 KOX8BNHkduHHy8MsCmzGwPxROE8e0eUFzGQ5YolT7BSWo9YKDAEMrFtGiW6puKr+Zq3s /vkwzwdfYKXesbeDIrzheoTDVPgqlcVEScNfHdbECdCFsXCPjH66yLlqMB0EQzmohrv+ s+44X9KrYhFR8XJfESFxbwZY6VKY7bCUdS6nzsF6PR/Dfk4H9+feUBr5vy7/0PXo+o5M LLTg== X-Gm-Message-State: AFuF++nii18Tf4naZigFll1UTgaaAT1huPhV7jJaJKP3EjNP0ar+h+jC RovQdJ8h62/8ZVa348WOkSuraeLMenkI/mi/cBC+FIzFkeGrCt4f3WrJ X-Gm-Gg: AYBFou2SBG0J0Z1c2es9NAyGWwDrsGBfIW2lzOCaKzu6svkeqgPrcTjHI8jGXL6TA9F +k6Mh8toeGQUGaBDD1tZd85yGVu8RLVTkAA3j+J5qzxhy/12JWdcT/iRLF9m0rTd5y4+L2omZAY czdF2ny3JIa2SrOS77q7sp33buU947kwfVy2ScBCrj/wxNBWMNTUTXlkLiTOiUmvHw05CH37TpC s0MKJX7D4/iN0O4LtkosT4osr3f+6zzUyokKiDAdOiuX2cDjC8mlyZAZw81m/3lrcB5uYAZEPr3 ns0OAJPJxYw9sF4MsSfZyFcWXRBNs2roCXMURM6eGfuAfGoONoHHI5G6JE/O+ii7hEqBZQesGbH BqFmKV23kroafUHDYw48Qcu4ZgZie0omY5ie5SJkogrF2CqDATgSAPNoWqsK0yQA1w/xoj1whu1 PAvqJByRLQTDGxZYkiGB8aPD31cOtlSj9jXJ57/zo4c6+Kr0vOs7FYqTHxX9ahM3ZA8Sy99h0bf Ij0sLrWCwLLeV/fVVmYot1JGSgo7MDYraVrqXpocGBNQ/0V32JttDIP2xN/QJhOtflV4TXMLdm2 /flvGQ== X-Received: by 2002:a05:6830:6ab2:b0:7d7:ea9f:c0f9 with SMTP id 46e09a7af769-7fa1bc0815dmr26425458a34.0.1789060913356; Thu, 10 Sep 2026 10:21:53 -0700 (PDT) Received: from ?IPV6:2603:8082:2b40:1f:174:7443:df1d:3d02? ([2603:8082:2b40:1f:174:7443:df1d:3d02]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f7adfaefsm21382842a34.23.2026.09.10.10.21.52 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 10 Sep 2026 10:21:52 -0700 (PDT) Message-ID: Date: Thu, 10 Sep 2026 12:21:12 -0500 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: COPY FROM with RLS To: jian he Cc: pgsql-hackers@lists.postgresql.org References: <70d4ee07.f25.19f161ce6b2.Coremail.jiezhilove@126.com> <1266b6dc.6689.19fa801caa0.Coremail.jiezhilove@126.com> <2e3ed175-8618-46a9-847b-b7e54597aa83@gmail.com> Content-Language: en-US From: Bryan Green In-Reply-To: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On 9/1/2026 2:13 AM, jian he wrote: > On Thu, Aug 27, 2026 at 9:04 AM wrote: >> >> Separately, getting the WITH CHECK expressions from a full rewrite of a dummy >> INSERT runs the target's ON INSERT rules on the RLS path, so the same COPY >> behaves differently depending on whether the table has RLS enabled: >> >> COPY into non-RLS table COPY into RLS table >> DO ALSO loads target, rule ignored ERROR (unsupported) >> DO INSTEAD (uncond.) loads the named table loads the redirect target >> DO INSTEAD (cond.) loads target, rule ignored ERROR (unsupported) >> >> The length check rejects the rewrites that fan out past one query, but a >> single unconditional DO INSTEAD passes it, so COPY into an RLS table lands the >> rows in the rule's redirect target rather than the named table. >> > > Portion of RLS node processing is inside the query rewriter (pg_rewrite_query), > pg_rewrite_query cannot be skipped. > So the only option is to disallow RLS on tables that have rules, IMHO. > > I also made some other miscellaneous changes and addressed the other > issue you mentioned. > > In the copy.sgml, I changed it as > > COPY FROM will invoke any triggers and check > constraints on the destination table. However, it will not invoke rules. > + If row-level security is enabled for the table, rules on the table are > + not supported. > > > Below is the commit message: > > Subject: [PATCH v15 1/1] COPY FROM with RLS > > Previously, COPY FROM on a table with row-level security enabled failed with > "COPY FROM not supported with row-level security". Now it is fully supported: > the table's INSERT policies are enforced against each copied row, the same as > for INSERT. > > To achieve this, CopyFrom() builds a dummy "INSERT INTO rel DEFAULT VALUES" > statement, runs it through parse analysis, the rewriter, and the planner, and > calls ExecutorStart() on the result. The plan is never executed; it only serves > to initialize the executor state, in particular the WITH CHECK OPTIONs derived > from the table's policies, which COPY then verifies against every row with > ExecWithCheckOptions(). > > Portion part of RLS node processing is inside the query rewriter, so this path > cannot skip query rewriting. Hence, unlike plain COPY FROM, which ignores rules > on the target table, COPY FROM with row-level security raises an error if the > table has any rule that would apply to an INSERT. > > discussion: https://postgr.es/m/CACJufxFbmnoa5O-vL43DPTCGt6oagY4dXgKxy=rcD9-e9g0zEg@mail.gmail.com > commitfest: https://commitfest.postgresql.org/patch/6178 > --------------------------- > > > > -- > jian > https://www.enterprisedb.com/ v15 checks out on the v3 items. Crash is gone on both an assert and a non-assert build, context is right, the rule cases error instead of redirecting, and quoting is fixed. The new one is the result of a forgotten guard. The WCO quals aren't in the volatile test that forces CIM_SINGLE, so under multi-insert a self-referential policy doesn't see the still-buffered rows and accepts ones a plain INSERT would reject. CREATE FUNCTION under_quota(o text) RETURNS bool LANGUAGE plpgsql VOLATILE AS $$ BEGIN RETURN (SELECT count(*) FROM q WHERE owner = o) < 3; END $$; CREATE POLICY ins ON q FOR INSERT WITH CHECK (under_quota(owner)); INSERT of ten rows stops at the fourth. COPY takes all ten. A no-op BEFORE ROW trigger makes COPY stop at four as well, so it's the batching. Probably wants ri_WithCheckOptions in that CIM_SINGLE test, or just forced under RLS. The wording "any rule that would apply to an INSERT" isn't quite right either. A conditional DO INSTEAD NOTHING stays one QSRC_ORIGINAL query and slips the three screens, so COPY inserts the row that would have dropped under INSERT with the rule. Also, relname_only wraps the whole ExecWithCheckOptions call, so a non-violation error from a policy expression loses the "line x". This is because relname_only is true for anything the call raises, including errors that are not due to policy rejection. Maybe set the flag only around the violation report instead of the whole evaluation? -- Bryan Green EDB: https://www.enterprisedb.com