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 1vxKPK-008fjk-18 for pgsql-hackers@arkaria.postgresql.org; Tue, 03 Mar 2026 07:43:06 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vxKPI-005LYc-2I for pgsql-hackers@arkaria.postgresql.org; Tue, 03 Mar 2026 07:43:05 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vxKPI-005LYU-1G for pgsql-hackers@lists.postgresql.org; Tue, 03 Mar 2026 07:43:04 +0000 Received: from udcm-wwu2.uni-muenster.de ([128.176.118.28]) by magus.postgresql.org with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1vxKPG-00000000966-2Dtx for pgsql-hackers@lists.postgresql.org; Tue, 03 Mar 2026 07:43:04 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=uni-muenster.de; i=@uni-muenster.de; q=dns/txt; s=uniout; t=1772523783; x=1804059783; h=message-id:date:mime-version:subject:to:references:from: in-reply-to:content-transfer-encoding; bh=5vKm2oKhWgb773Hy/B+Lo1VOXHOqzjCdwGm7yhyqB+U=; b=sHeNSu2iXQuTIFcNgc20oUqcHlz22j81uWbYlfdXGFwwExLfO1GYtbOE qw0JHvAKKtR9YHIW6iqznc5VEvL5XllSyNic7dGCKSWE80LlGtFnHQT4b euskUi8OUv+jChuWSCPP4wRYbSnZqopS/VIrrTgZI1notE1w/h8V6lDFh voBQIh9PY38opXEmkMKYgqvqIMHrxitfrjz8IggfsU+KGBGsEuCmRN+Za g+tpyKduXzFKIcMU/G56eYthuSbg7gl9c7bfJn6dAtCz6eLRFiGj4EC9J TSiodmfr2S3MdOj8dn7fdX93fXjf/VEMTNYMHokr1T1YQFXH8cKoOxxI3 g==; X-CSE-ConnectionGUID: gGRyf6GSRlCeQW8zfakELQ== X-CSE-MsgGUID: E5+QH+/QTjSEAO1jHrPfnw== X-IronPort-AV: E=Sophos;i="6.21,321,1763420400"; d="scan'208";a="386489804" Received: from secmail.uni-muenster.de ([128.176.118.4]) by UDCM-RELAY2.UNI-MUENSTER.DE with ESMTP; 03 Mar 2026 08:43:01 +0100 Received: from [192.168.178.27] (dynamic-093-133-070-122.93.133.pool.telefonica.de [93.133.70.122]) by SECMAIL.UNI-MUENSTER.DE (Postfix) with ESMTPSA id 37FE920ADF00; Tue, 3 Mar 2026 08:43:01 +0100 (CET) Message-ID: Date: Tue, 3 Mar 2026 08:42:59 +0100 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: POC: PLpgSQL FOREACH IN JSON ARRAY To: Pavel Stehule , PostgreSQL Hackers References: Content-Language: de-DE, en-GB From: Jim Jones In-Reply-To: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi Pavel, I quickly tested the patch, and I also could observe a ~3x performance improvement! A few first impressions: ## in exec_stmt_foreach_json_a the boolean variable found is declared as false, bit its value is never set to true until exec_set_found() is called: /* * Set the FOUND variable to indicate the result of executing the loop * (namely, whether we looped one or more times). This must be set here so * that it does not interfere with the value of the FOUND variable inside * the loop processing itself. */ exec_set_found(estate, found); Test: DO $$ DECLARE x int; BEGIN FOREACH x IN JSON ARRAY '[1,2,3]' LOOP RAISE NOTICE 'x: %', x; END LOOP; IF FOUND THEN RAISE NOTICE 'FOUND is true'; ELSE RAISE NOTICE 'FOUND is false'; END IF; END; $$; NOTICE: x: 1 NOTICE: x: 2 NOTICE: x: 3 NOTICE: FOUND is false ## Suggestion in the plpgsql.sgml The FOREACH loop is much like a FOREACH loop, to "much like a regular FOREACH loop over arrays" ## Typo in comment /* * We cannot to use fieldnames for tupdescentry, because * these names can be suffixed by name of row variable. ... We cannot to use > We cannot use ## Nit pick These error messages are not wrong, but IMO a errhint/errdetail could add some value here: ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot extract elements from a scalar"))); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot extract elements from an object"))); Something like this perhaps? ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot extract elements from a scalar"), errhint("FOREACH IN JSON ARRAY requires an array value."))); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("FOREACH expression must evaluate to a JSON array"), errdetail("Cannot iterate over a scalar value."))); Thanks for the patch! Best, Jim