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.94.2) (envelope-from ) id 1tp8IB-000sld-Ga for pgsql-general@arkaria.postgresql.org; Mon, 03 Mar 2025 16:05:21 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1tp8I9-00AmIs-Bf for pgsql-general@arkaria.postgresql.org; Mon, 03 Mar 2025 16:05:17 +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.94.2) (envelope-from ) id 1tp8I8-00AmIk-NN for pgsql-general@lists.postgresql.org; Mon, 03 Mar 2025 16:05:17 +0000 Received: from postout1.mail.lrz.de ([129.187.255.137]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1tp8I4-000jUo-1j for pgsql-general@lists.postgresql.org; Mon, 03 Mar 2025 16:05:16 +0000 Received: from lxmhs51.srv.lrz.de (localhost [127.0.0.1]) by postout1.mail.lrz.de (Postfix) with ESMTP id 4Z63Yh3qFtzyRY for ; Mon, 3 Mar 2025 17:05:12 +0100 (CET) Authentication-Results: postout.lrz.de (amavisd-new); dkim=pass (2048-bit key) reason="pass (just generated, assumed good)" header.d=tum.de DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tum.de; h= content-transfer-encoding:content-type:content-type:subject :subject:from:from:content-language:user-agent:mime-version:date :date:message-id:received:received; s=tu-postout21; t= 1741017912; bh=6pZLp5Bv2kjAfeD5tvg2cvQTZOCLDb80j/EEaBKRstg=; b=P eehGMNV1TExEzUvZ5uUiJ/eyI6pk/3AEvTOdiJKYjmVKEouc1NfAZ2jNmvoex3Y2 ELHyXRqLxUHH/iWsVdTudXRpYaBExOmQGj0dnNqcHauW+RlIDMk8wUUjfgf7TevD mOEj3xgBnNezKvUpm024cIcamVm1iRem5WoglOmkepOVfcq+NpIH5cQiSAlxz8C+ jotDBlLyq7NeHbGbFlxotQsh2g+w9GpZ15edj/O6Ndf5RgYSg/8Vd+ovJi1ZRxEp cuIV5bQtgwr8n46ZohnK9swbX/3XB/Hh4jFTLAzAbPB+2Ny/wBAw1JiuJV3RJYiM W/J2kNpzvMg16V4w0tB6A== X-Virus-Scanned: by amavisd-new at lrz.de in lxmhs51.srv.lrz.de X-Spam-Flag: NO X-Spam-Score: -2.872 X-Spam-Level: X-Spam-Status: No, score=-2.872 tagged_above=-999 required=5 tests=[ALL_TRUSTED=-1, BAYES_00=-1.9, DMARC_ADKIM_RELAXED=0.001, DMARC_ASPF_RELAXED=0.001, DMARC_POLICY_NONE=0.001, LRZ_DMARC_FAIL=0.001, LRZ_DMARC_FAIL_NONE=0.001, LRZ_DMARC_POLICY=0.001, LRZ_DMARC_TUM_FAIL=0.001, LRZ_DMARC_TUM_REJECT=3.5, LRZ_DMARC_TUM_REJECT_PO=-3.5, LRZ_ENVFROM_FROM_MATCH=0.001, LRZ_ENVFROM_TUM_S=0.001, LRZ_FROM_ENVFROM_ALIGNED_STRICT=0.001, LRZ_FROM_HAS_A=0.001, LRZ_FROM_HAS_AAAA=0.001, LRZ_FROM_HAS_MDOM=0.001, LRZ_FROM_HAS_MX=0.001, LRZ_FROM_HOSTED_DOMAIN=0.001, LRZ_FROM_NAME_IN_ADDR=0.001, LRZ_FROM_PHRASE=0.001, LRZ_FROM_PRE_SUR=0.001, LRZ_FROM_PRE_SUR_PHRASE=0.001, LRZ_FROM_TUM_S=0.001, LRZ_HAS_CLANG=0.001, LRZ_HAS_CT=0.001, LRZ_HAS_MIME_VERSION=0.001, LRZ_HAS_SPF=0.001, LRZ_MSGID_HL8_3HL4_HL12=0.001, LRZ_MSGID_MOZ=0.001, LRZ_TO_SHORT=0.001, LRZ_UA_MOZ=0.001] autolearn=no autolearn_force=no Received: from postout1.mail.lrz.de ([127.0.0.1]) by lxmhs51.srv.lrz.de (lxmhs51.srv.lrz.de [127.0.0.1]) (amavisd-new, port 20024) with LMTP id t664z-OVVw9S for ; Mon, 3 Mar 2025 17:05:12 +0100 (CET) Received: from [IPV6:2003:ed:af4a:e327:200e:9bf0:ae9:7691] (p200300edaf4ae327200e9bf00ae97691.dip0.t-ipconnect.de [IPv6:2003:ed:af4a:e327:200e:9bf0:ae9:7691]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by postout1.mail.lrz.de (Postfix) with ESMTPSA id 4Z63Yg5kWzzyQV for ; Mon, 3 Mar 2025 17:05:11 +0100 (CET) Message-ID: <05676a44-9408-4c15-9309-c0f39ecd511a@tum.de> Date: Mon, 3 Mar 2025 17:05:10 +0100 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Content-Language: en-US To: pgsql-general@lists.postgresql.org From: Jakob Teuber Subject: Infinite loop for generate_series with timestamp arguments 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 Hi all, I noticed, that the following query will send Postgres into an infinite loop: (a)  select generate_series(timestamp '2025-01-30', timestamp '2025-02-01', interval '1 month -29 days'); Adding one month to 2023-01-30 will result in 2023-02-28 due to February having no 31st day, and then subtracting 29 days will result in 2025-01-30 again, resulting in a repetition from there. Postgres does not detect this, and gets caught in an infinite loop. This query is not the only way this can happen, one can imagine many different setups, that, at some point, will lead to this problem. One could certainly argue that “go and do an infinite loop” is plainly the intended semantics of this query, but in other cases, Postgres tries to guard against these types of looping generate_series statements: (b)  select generate_series(timestamp '2025-03-31', timestamp '2025-04-01', interval '1 month -30 days');         → ERROR:  step size cannot equal zero (c)  select generate_series(timestamp '2024-12-31', timestamp '2025-01-01', interval '1 month -31 days');         → (0 rows) It is a bit surprising to me, that all these three cases are handled differently, as they seem exactly analogous. The issue with all the expression is that the specified interval, when added to the start date, results in the start date again.     select (timestamp '2025-01-30' + interval '1 month -29 days') as A,       (timestamp '2025-03-31' + interval '1 month -30 days') as B,       (timestamp '2024-12-31' + interval '1 month -31 days') as C;         →                       a          |          b |          c ---------------------+---------------------+---------------------              2025-01-31 00:00:00 | 2025-03-31 00:00:00 | 2024-12-31 00:00:00 Query (b) seems to be the most straight forward: Postgres seems to flag the interval '1 month -30 days' as “empty” no matter the context.     select generate_series(timestamp '2024-12-31', timestamp '2025-01-01', interval '1 month -30 days');         → psql:commands.sql:1: ERROR:  step size cannot equal zero     select timestamp '2024-12-31' + interval '1 month -30 days';         → 2025-01-01 00:00:00 This should theoretically be an unproblematic query resulting in [2024-12-31 00:00:00, 2025-01-01 00:00:00], but it seems like Postgres treats '1 month -30 days' as an “zero-size interval”, despite there being contexts, where it is not of size zero, and contexts, where other intervals that aren’t flagged, are of size zero. For query (c), the empty result makes it seem like Postgres interprets it as having a negative step size for a date in the future; the same result would happen with e. g. the interval '1 month -40 days' or generate_series(10, 5, +1). This would fit with the above, where Postgres assumes that a month always has 30 days for the purpose of determining how generate_series should run. Is this behavior intentional or should it be considered to be erroneous? Naturally, working with human time representation can introduce many subtleties and maybe it is beyond the scope of Postgres to correctly handle them (whatever that means), but at least failing in a consistent way shouldn’t be to much to ask for? If this is intentional, is there a system to this that I have failed to see? Best, Jakob (PS: as this is not really a bug report, but rather an inquiry to what extent this behavior is intended, I hope `postgres-general` is the correct choice of lists. If not, feel free to redirect me to a more appropriate one.) --- Jakob Teuber jakob.teuber@tum.de