+`
+end
+
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id HAA05591
+ for ; Mon, 19 Oct 1998 07:31:09 -0400 (EDT)
+Received: from hub.org (
[email protected] [209.47.148.200]) by renoir.op.net (o1/$Revision: 1.1 $) with ESMTP id HAA13574 for
; Mon, 19 Oct 1998 07:12:57 -0400 (EDT)
+Received: from localhost (majordom@localhost)
+ by hub.org (8.8.8/8.8.8) with SMTP id GAA13957;
+ Mon, 19 Oct 1998 06:25:09 -0400 (EDT)
+Received: by hub.org (TLB v0.10a (1.23 tibbs 1997/01/09 00:29:32)); Mon, 19 Oct 1998 06:22:35 +0000 (EDT)
+Received: (from majordom@localhost)
+ by hub.org (8.8.8/8.8.8) id GAA13581
+ for pgsql-hackers-outgoing; Mon, 19 Oct 1998 06:22:33 -0400 (EDT)
+X-Authentication-Warning: hub.org: majordom set sender to
[email protected] using -f
+ by hub.org (8.8.8/8.8.8) with ESMTP id GAA13566
+ for
; Mon, 19 Oct 1998 06:22:27 -0400 (EDT)
+Received: by dsh.de; id MAA13918; Mon, 19 Oct 1998 12:21:16 +0200 (MET DST)
+Received: from dshmail.dsh.de(53.47.15.3) by neptun.dsh.de via smap (3.2)
+ id xma013635; Mon, 19 Oct 98 12:20:55 +0200
+Received: from mail1.hh1.dsh.de (mail1.hh1.dsh.de [53.47.9.5])
+ by dshmail.dsh.de (8.8.7/8.8.7) with ESMTP id MAA11037;
+ Mon, 19 Oct 1998 12:18:27 +0200 (MET DST)
+ by mail1.hh1.dsh.de (8.8.7/8.8.7) with SMTP id MAA29382;
+ Mon, 19 Oct 1998 12:20:49 +0200
+Received: from orion.SAPserv.Hamburg.dsh.de
+ by mars.SAPserv.Hamburg.dsh.de with smtp
+ for <>
+ id m0zVA2V-000B5AC; Mon, 19 Oct 98 09:47 MET DST
+Received: by orion.SAPserv.Hamburg.dsh.de
+ id m0zVCaT-000EBPC; Mon, 19 Oct 98 12:30 MET DST
+Message-Id:
+Subject: Re: [HACKERS] What about LIMIT in SELECT ?
+Date: Mon, 19 Oct 1998 12:30:52 +0200 (MET DST)
+X-Mailer: ELM [version 2.4 PL25]
+Content-Type: text
+Precedence: bulk
+Status: RO
+
+Hiroshi Inoue wrote:
+
+> When using cursors,in most cases the response to get first(next) rows
+> is necessary for me,not the throughput.
+> How can we tell PostgreSQL optimzer that the response is necessary ?
+
+ With my LIMIT patch, the offset and the row count are part of
+ the querytree. And if a LIMIT is given, the limitCount elemet
+ of the querytree (a Node *) isn't NULL what it is by default.
+
+ When a LIMIT is given, the optimizer could assume that first
+ rows is wanted (even if the limit is ALL maybe - but I have
+ to think about this some more). And this assumption might let
+ it decide to use an index to resolve an ORDER BY even if no
+ qualification was given.
+
+ Telling the optimizer that first rows wanted in a cursor
+ operation would read
+
+ DECLARE CURSOR c FOR SELECT * FROM mytab ORDER BY a LIMIT ALL;
+
+
+Jan
+
+--
+
+#======================================================================#
+# It's easier to get forgiveness for being wrong than for being right. #
+# Let's break this rule - forgive me. #
+
+
+
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id GAA02483
+ for ; Tue, 20 Oct 1998 06:01:48 -0400 (EDT)
+Received: from hub.org (
[email protected] [209.47.148.200]) by renoir.op.net (o1/$Revision: 1.1 $) with ESMTP id FAA07799 for
; Tue, 20 Oct 1998 05:51:19 -0400 (EDT)
+Received: from localhost (majordom@localhost)
+ by hub.org (8.8.8/8.8.8) with SMTP id FAA00108;
+ Tue, 20 Oct 1998 05:17:58 -0400 (EDT)
+Received: by hub.org (TLB v0.10a (1.23 tibbs 1997/01/09 00:29:32)); Tue, 20 Oct 1998 05:16:37 +0000 (EDT)
+Received: (from majordom@localhost)
+ by hub.org (8.8.8/8.8.8) id FAA29953
+ for pgsql-hackers-outgoing; Tue, 20 Oct 1998 05:16:35 -0400 (EDT)
+X-Authentication-Warning: hub.org: majordom set sender to
[email protected] using -f
+ by hub.org (8.8.8/8.8.8) with ESMTP id FAA29939
+ for ; Tue, 20 Oct 1998 05:16:27 -0400 (EDT)
+Received: by dsh.de; id LAA04585; Tue, 20 Oct 1998 11:15:05 +0200 (MET DST)
+Received: from dshmail.dsh.de(53.47.15.3) by neptun.dsh.de via smap (3.2)
+ id xma004337; Tue, 20 Oct 98 11:14:46 +0200
+Received: from mail1.hh1.dsh.de (mail1.hh1.dsh.de [53.47.9.5])
+ by dshmail.dsh.de (8.8.7/8.8.7) with ESMTP id LAA14628;
+ Tue, 20 Oct 1998 11:12:27 +0200 (MET DST)
+ by mail1.hh1.dsh.de (8.8.7/8.8.7) with SMTP id LAA03564;
+ Tue, 20 Oct 1998 11:14:52 +0200
+Received: from orion.SAPserv.Hamburg.dsh.de
+ by mars.SAPserv.Hamburg.dsh.de with smtp
+ for <>
+ id m0zVVUa-000B5AC; Tue, 20 Oct 98 08:42 MET DST
+Received: by orion.SAPserv.Hamburg.dsh.de
+ id m0zVY2c-000EBPC; Tue, 20 Oct 98 11:25 MET DST
+Message-Id:
+Subject: Re: [HACKERS] What about LIMIT in SELECT ?
+Date: Tue, 20 Oct 1998 11:25:22 +0200 (MET DST)
+X-Mailer: ELM [version 2.4 PL25]
+Content-Type: text
+Precedence: bulk
+Status: RO
+
+Hiroshi Inoue wrote:
+
+> > * Prevent psort() usage when query already using index matching ORDER BY
+> >
+> >
+>
+> I can't find the reference to descending order cases except my posting.
+> If we use an index scan to remove sorts in those cases,backward positioning
+> and scanning are necessary.
+
+ I think it's only thought as a reminder that the optimizer
+ needs some optimization.
+
+ That topic, and the LIMIT stuff too I think, is past 6.4 work
+ and may go into a 6.4.1 performance release. So when we are
+ after 6.4, we have enough time to work out a real solution,
+ instead of just throwing in a patch as a quick shot.
+
+ What we two did where steps in the same direction. Your one
+ covers more situations, but after all if multiple people have
+ the same idea there is a good chance that it is the right
+ thing to do.
+
+>
+> Let t be a table with 2 indices, index1(key1,key2), index2(key1,key3).
+> i.e. key1 is common to index1 and index2.
+>
+> And for the query
+> select * from t where key1>....;
+>
+> If PosgreSQL optimizer choose [ index scan on index1 ] we can't remove
+> sorts from the following query.
+> select * from t where key1>... order by key1,key3;
+>
+> Similarly if [ index scan on index2 ] are chosen we can't remove sorts
+> from the following query.
+> select * from t where key1>... order by key1,key2;
+>
+> But in both cases (clever) optimizer can choose another index for scan.
+
+ Right. As I remember, your solution does basically the same
+ as my one. It does not change the optimizers decision about
+ the index or if an index at all is used. So I assume they
+ hook into the same position where depending on the order by
+ clause the sort node is added. And that is at the very end of
+ the optimizer.
+
+ What you describe above requires changes in upper levels of
+ optimization. Doing that is far away from my knowledge about
+ the optimizer. And some of your earlier statements let me
+ think you aren't familiar enough with it too. We need at
+ least help from others to do it well.
+
+ I don't want to dive that deep into the optimizer. There was
+ a far too long time where the rule system was broken and got
+ out of sync with the parser/optimizer capabilities. I fixed
+ many things in it for 6.4. My first priority now is, not to
+ let such a situation come up again.
+
+
+Jan
+
+--
+
+#======================================================================#
+# It's easier to get forgiveness for being wrong than for being right. #
+# Let's break this rule - forgive me. #
+
+
+
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id NAA08269
+ for ; Tue, 20 Oct 1998 13:00:01 -0400 (EDT)
+Received: by dsh.de; id TAA14203; Tue, 20 Oct 1998 19:02:15 +0200 (MET DST)
+Received: from dshmail.dsh.de(53.47.15.3) by neptun.dsh.de via smap (3.2)
+ id xma014037; Tue, 20 Oct 98 19:01:39 +0200
+Received: from mail1.hh1.dsh.de (mail1.hh1.dsh.de [53.47.9.5])
+ by dshmail.dsh.de (8.8.7/8.8.7) with ESMTP id SAA24445;
+ Tue, 20 Oct 1998 18:59:16 +0200 (MET DST)
+ by mail1.hh1.dsh.de (8.8.7/8.8.7) with SMTP id TAA06159;
+ Tue, 20 Oct 1998 19:01:40 +0200
+Received: from orion.SAPserv.Hamburg.dsh.de
+ by mars.SAPserv.Hamburg.dsh.de with smtp
+ for <>
+ id m0zVcmS-000B5AC; Tue, 20 Oct 98 16:29 MET DST
+Received: by orion.SAPserv.Hamburg.dsh.de
+ id m0zVfKV-000EBPC; Tue, 20 Oct 98 19:12 MET DST
+Message-Id:
+Subject: Re: [HACKERS] What about LIMIT in SELECT ?
+Date: Tue, 20 Oct 1998 19:12:19 +0200 (MET DST)
+X-Mailer: ELM [version 2.4 PL25]
+Content-Type: text
+Status: ROr
+
+>
+> I agree. Another good thing is that the LIMIT thing will not require a
+> dump/reload, so it is a good candidate for a minor release.
+
+ That's wrong, sorry.
+
+ The limit thing as I implemented it adds 2 new variables to
+ the Query structure. Rewrite rules are stored as querytrees
+ and in the existing pg_rewrite entries that would be missing.
+
+
+Jan
+
+--
+
+#======================================================================#
+# It's easier to get forgiveness for being wrong than for being right. #
+# Let's break this rule - forgive me. #
+
+
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id NAA08484
+ for ; Tue, 20 Oct 1998 13:24:45 -0400 (EDT)
+Received: from localhost (majordom@localhost)
+ by hub.org (8.8.8/8.8.8) with SMTP id NAA01878;
+ Tue, 20 Oct 1998 13:00:06 -0400 (EDT)
+Received: by hub.org (TLB v0.10a (1.23 tibbs 1997/01/09 00:29:32)); Tue, 20 Oct 1998 12:59:59 +0000 (EDT)
+Received: (from majordom@localhost)
+ by hub.org (8.8.8/8.8.8) id MAA01579
+ for pgsql-hackers-outgoing; Tue, 20 Oct 1998 12:59:58 -0400 (EDT)
+X-Authentication-Warning: hub.org: majordom set sender to
[email protected] using -f
+ by hub.org (8.8.8/8.8.8) with ESMTP id MAA01557
+ for ; Tue, 20 Oct 1998 12:59:52 -0400 (EDT)
+Received: by dsh.de; id TAA14203; Tue, 20 Oct 1998 19:02:15 +0200 (MET DST)
+Received: from dshmail.dsh.de(53.47.15.3) by neptun.dsh.de via smap (3.2)
+ id xma014037; Tue, 20 Oct 98 19:01:39 +0200
+Received: from mail1.hh1.dsh.de (mail1.hh1.dsh.de [53.47.9.5])
+ by dshmail.dsh.de (8.8.7/8.8.7) with ESMTP id SAA24445;
+ Tue, 20 Oct 1998 18:59:16 +0200 (MET DST)
+ by mail1.hh1.dsh.de (8.8.7/8.8.7) with SMTP id TAA06159;
+ Tue, 20 Oct 1998 19:01:40 +0200
+Received: from orion.SAPserv.Hamburg.dsh.de
+ by mars.SAPserv.Hamburg.dsh.de with smtp
+ for <>
+ id m0zVcmS-000B5AC; Tue, 20 Oct 98 16:29 MET DST
+Received: by orion.SAPserv.Hamburg.dsh.de
+ id m0zVfKV-000EBPC; Tue, 20 Oct 98 19:12 MET DST
+Message-Id:
+Subject: Re: [HACKERS] What about LIMIT in SELECT ?
+Date: Tue, 20 Oct 1998 19:12:19 +0200 (MET DST)
+X-Mailer: ELM [version 2.4 PL25]
+Content-Type: text
+Precedence: bulk
+Status: ROr
+
+>
+> I agree. Another good thing is that the LIMIT thing will not require a
+> dump/reload, so it is a good candidate for a minor release.
+
+ That's wrong, sorry.
+
+ The limit thing as I implemented it adds 2 new variables to
+ the Query structure. Rewrite rules are stored as querytrees
+ and in the existing pg_rewrite entries that would be missing.
+
+
+Jan
+
+--
+
+#======================================================================#
+# It's easier to get forgiveness for being wrong than for being right. #
+# Let's break this rule - forgive me. #
+
+
+
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id NAA08339
+ for ; Tue, 20 Oct 1998 13:10:18 -0400 (EDT)
+Received: by dsh.de; id TAA17171; Tue, 20 Oct 1998 19:12:30 +0200 (MET DST)
+Received: from dshmail.dsh.de(53.47.15.3) by neptun.dsh.de via smap (3.2)
+ id xma017064; Tue, 20 Oct 98 19:12:00 +0200
+Received: from mail1.hh1.dsh.de (mail1.hh1.dsh.de [53.47.9.5])
+ by dshmail.dsh.de (8.8.7/8.8.7) with ESMTP id TAA24806;
+ Tue, 20 Oct 1998 19:09:37 +0200 (MET DST)
+ by mail1.hh1.dsh.de (8.8.7/8.8.7) with SMTP id TAA06212;
+ Tue, 20 Oct 1998 19:12:01 +0200
+Received: from orion.SAPserv.Hamburg.dsh.de
+ by mars.SAPserv.Hamburg.dsh.de with smtp
+ for <>
+ id m0zVcwS-000B5AC; Tue, 20 Oct 98 16:39 MET DST
+Received: by orion.SAPserv.Hamburg.dsh.de
+ id m0zVfUW-000EBPC; Tue, 20 Oct 98 19:22 MET DST
+Message-Id:
+Subject: Re: [HACKERS] What about LIMIT in SELECT ?
+Date: Tue, 20 Oct 1998 19:22:40 +0200 (MET DST)
+X-Mailer: ELM [version 2.4 PL25]
+Content-Type: text
+Status: RO
+
+>
+> > >
+> > > I agree. Another good thing is that the LIMIT thing will not require a
+> > > dump/reload, so it is a good candidate for a minor release.
+> >
+> > That's wrong, sorry.
+> >
+> > The limit thing as I implemented it adds 2 new variables to
+> > the Query structure. Rewrite rules are stored as querytrees
+> > and in the existing pg_rewrite entries that would be missing.
+>
+> Oh, sorry. I forgot. That could be tough.
+
+ But it wouldn't hurt to add them now to have them in
+ place. The required out-, read- and copyfuncs are in
+ my patch too. This would prevent dump/load when we
+ later add the real LIMIT functionality. And it does
+ not change anything now.
+
+
+Jan
+
+--
+
+#======================================================================#
+# It's easier to get forgiveness for being wrong than for being right. #
+# Let's break this rule - forgive me. #
+
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id OAA11449
+ for ; Tue, 20 Oct 1998 14:57:34 -0400 (EDT)
+Received: from localhost (majordom@localhost)
+ by hub.org (8.8.8/8.8.8) with SMTP id NAA03547;
+ Tue, 20 Oct 1998 13:10:38 -0400 (EDT)
+Received: by hub.org (TLB v0.10a (1.23 tibbs 1997/01/09 00:29:32)); Tue, 20 Oct 1998 13:10:23 +0000 (EDT)
+Received: (from majordom@localhost)
+ by hub.org (8.8.8/8.8.8) id NAA03488
+ for pgsql-hackers-outgoing; Tue, 20 Oct 1998 13:10:21 -0400 (EDT)
+X-Authentication-Warning: hub.org: majordom set sender to
[email protected] using -f
+ by hub.org (8.8.8/8.8.8) with ESMTP id NAA03455
+ for ; Tue, 20 Oct 1998 13:10:10 -0400 (EDT)
+Received: by dsh.de; id TAA17171; Tue, 20 Oct 1998 19:12:30 +0200 (MET DST)
+Received: from dshmail.dsh.de(53.47.15.3) by neptun.dsh.de via smap (3.2)
+ id xma017064; Tue, 20 Oct 98 19:12:00 +0200
+Received: from mail1.hh1.dsh.de (mail1.hh1.dsh.de [53.47.9.5])
+ by dshmail.dsh.de (8.8.7/8.8.7) with ESMTP id TAA24806;
+ Tue, 20 Oct 1998 19:09:37 +0200 (MET DST)
+ by mail1.hh1.dsh.de (8.8.7/8.8.7) with SMTP id TAA06212;
+ Tue, 20 Oct 1998 19:12:01 +0200
+Received: from orion.SAPserv.Hamburg.dsh.de
+ by mars.SAPserv.Hamburg.dsh.de with smtp
+ for <>
+ id m0zVcwS-000B5AC; Tue, 20 Oct 98 16:39 MET DST
+Received: by orion.SAPserv.Hamburg.dsh.de
+ id m0zVfUW-000EBPC; Tue, 20 Oct 98 19:22 MET DST
+Message-Id:
+Subject: Re: [HACKERS] What about LIMIT in SELECT ?
+Date: Tue, 20 Oct 1998 19:22:40 +0200 (MET DST)
+X-Mailer: ELM [version 2.4 PL25]
+Content-Type: text
+Precedence: bulk
+Status: ROr
+
+>
+> > >
+> > > I agree. Another good thing is that the LIMIT thing will not require a
+> > > dump/reload, so it is a good candidate for a minor release.
+> >
+> > That's wrong, sorry.
+> >
+> > The limit thing as I implemented it adds 2 new variables to
+> > the Query structure. Rewrite rules are stored as querytrees
+> > and in the existing pg_rewrite entries that would be missing.
+>
+> Oh, sorry. I forgot. That could be tough.
+
+ But it wouldn't hurt to add them now to have them in
+ place. The required out-, read- and copyfuncs are in
+ my patch too. This would prevent dump/load when we
+ later add the real LIMIT functionality. And it does
+ not change anything now.
+
+
+Jan
+
+--
+
+#======================================================================#
+# It's easier to get forgiveness for being wrong than for being right. #
+# Let's break this rule - forgive me. #
+
+
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id CAA29494
+ for ; Wed, 21 Oct 1998 02:35:53 -0400 (EDT)
+Received: from localhost (majordom@localhost)
+ by hub.org (8.8.8/8.8.8) with SMTP id CAA13326;
+ Wed, 21 Oct 1998 02:10:42 -0400 (EDT)
+Received: by hub.org (TLB v0.10a (1.23 tibbs 1997/01/09 00:29:32)); Wed, 21 Oct 1998 02:09:35 +0000 (EDT)
+Received: (from majordom@localhost)
+ by hub.org (8.8.8/8.8.8) id CAA12900
+ for pgsql-hackers-outgoing; Wed, 21 Oct 1998 02:09:33 -0400 (EDT)
+X-Authentication-Warning: hub.org: majordom set sender to
[email protected] using -f
+ by hub.org (8.8.8/8.8.8) with ESMTP id CAA12871
+ for ; Wed, 21 Oct 1998 02:09:26 -0400 (EDT)
+Received: (from maillist@localhost)
+ by candle.pha.pa.us (8.9.0/8.9.0) id CAA27774;
+ Wed, 21 Oct 1998 02:09:27 -0400 (EDT)
+From: Bruce Momjian
+Subject: Re: [HACKERS] What about LIMIT in SELECT ?
+In-Reply-To: from Jan Wieck at "Oct 20, 1998 7:22:40 pm"
+Date: Wed, 21 Oct 1998 02:09:26 -0400 (EDT)
+X-Mailer: ELM [version 2.4ME+ PL47 (25)]
+MIME-Version: 1.0
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: 7bit
+Precedence: bulk
+Status: RO
+
+> >
+> > > >
+> > > > I agree. Another good thing is that the LIMIT thing will not require a
+> > > > dump/reload, so it is a good candidate for a minor release.
+> > >
+> > > That's wrong, sorry.
+> > >
+> > > The limit thing as I implemented it adds 2 new variables to
+> > > the Query structure. Rewrite rules are stored as querytrees
+> > > and in the existing pg_rewrite entries that would be missing.
+> >
+> > Oh, sorry. I forgot. That could be tough.
+>
+> But it wouldn't hurt to add them now to have them in
+> place. The required out-, read- and copyfuncs are in
+> my patch too. This would prevent dump/load when we
+> later add the real LIMIT functionality. And it does
+> not change anything now.
+>
+
+Jan, we found that I am having to require an initdb for the INET/CIDR
+type, so if you want stuff to change the views/rules for the limit
+addition post 6.4, please send them in and I will apply them.
+
+You clearly have the syntax down, so I think you should go ahead.
+
+
+--
+ Bruce Momjian | http://www.op.net/~candle
+ + If your life is a hard drive, | 830 Blythe Avenue
+ + Christ can be your backup. | Drexel Hill, Pennsylvania 19026
+
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id KAA20566
+ for ; Thu, 22 Oct 1998 10:20:54 -0400 (EDT)
+Received: by dsh.de; id QAA09067; Thu, 22 Oct 1998 16:23:14 +0200 (MET DST)
+Received: from dshmail.dsh.de(53.47.15.3) by neptun.dsh.de via smap (3.2)
+ id xma008719; Thu, 22 Oct 98 16:22:40 +0200
+Received: from mail1.hh1.dsh.de (mail1.hh1.dsh.de [53.47.9.5])
+ by dshmail.dsh.de (8.8.7/8.8.7) with ESMTP id QAA01558;
+ Thu, 22 Oct 1998 16:19:55 +0200 (MET DST)
+ by mail1.hh1.dsh.de (8.8.7/8.8.7) with SMTP id QAA18978;
+ Thu, 22 Oct 1998 16:22:20 +0200
+Received: from orion.SAPserv.Hamburg.dsh.de
+ by mars.SAPserv.Hamburg.dsh.de with smtp
+ for <>
+ id m0zWJG2-000B5AC; Thu, 22 Oct 98 13:50 MET DST
+Received: by orion.SAPserv.Hamburg.dsh.de
+ id m0zWLoE-000EBPC; Thu, 22 Oct 98 16:33 MET DST
+Message-Id:
+Subject: Re: [HACKERS] psql's help (the LIMIT stuff)
+Date: Thu, 22 Oct 1998 16:33:50 +0200 (MET DST)
+X-Mailer: ELM [version 2.4 PL25]
+Content-Type: text
+Status: ROr
+
+> >
+> > I hope the QUERY_LIMIT too.
+>
+> I still have that cnfify() possible fix to review for KQSO. Are you
+> still thinking limit for 6.4 final, or a minor release after that?
+
+ I posted the part that is the minimum applied to 6.4 to make
+ adding LIMIT later non-initdb earlier. Anyway, here it's
+ again.
+
+ My LIMIT implementation that does it like the SET in the
+ toplevel executor (but via parsetree values) is ready for
+ production. I only held it back because it's feature, not
+ bugfix.
+
+ Do you want it in 6.4 final?
+
+
+Jan
+
+--
+
+#======================================================================#
+# It's easier to get forgiveness for being wrong than for being right. #
+# Let's break this rule - forgive me. #
+
+
+diff -cr src.orig/backend/nodes/copyfuncs.c src/backend/nodes/copyfuncs.c
+*** src.orig/backend/nodes/copyfuncs.c Fri Oct 16 11:53:40 1998
+--- src/backend/nodes/copyfuncs.c Fri Oct 16 13:32:35 1998
+***************
+*** 1578,1583 ****
+--- 1578,1586 ----
+ newnode->unionClause = temp_list;
+ }
+
++ Node_Copy(from, newnode, limitOffset);
++ Node_Copy(from, newnode, limitCount);
++
+ return newnode;
+ }
+
+diff -cr src.orig/backend/nodes/outfuncs.c src/backend/nodes/outfuncs.c
+*** src.orig/backend/nodes/outfuncs.c Fri Oct 16 11:53:40 1998
+--- src/backend/nodes/outfuncs.c Fri Oct 16 13:30:50 1998
+***************
+*** 259,264 ****
+--- 259,268 ----
+ appendStringInfo(str, (node->hasSubLinks ? "true" : "false"));
+ appendStringInfo(str, " :unionClause ");
+ _outNode(str, node->unionClause);
++ appendStringInfo(str, " :limitOffset ");
++ _outNode(str, node->limitOffset);
++ appendStringInfo(str, " :limitCount ");
++ _outNode(str, node->limitCount);
+ }
+
+ static void
+diff -cr src.orig/backend/nodes/readfuncs.c src/backend/nodes/readfuncs.c
+*** src.orig/backend/nodes/readfuncs.c Fri Oct 16 11:53:40 1998
+--- src/backend/nodes/readfuncs.c Fri Oct 16 13:31:43 1998
+***************
+*** 163,168 ****
+--- 163,174 ----
+ token = lsptok(NULL, &length); /* skip :unionClause */
+ local_node->unionClause = nodeRead(true);
+
++ token = lsptok(NULL, &length); /* skip :limitOffset */
++ local_node->limitOffset = nodeRead(true);
++
++ token = lsptok(NULL, &length); /* skip :limitCount */
++ local_node->limitCount = nodeRead(true);
++
+ return local_node;
+ }
+
+diff -cr src.orig/include/nodes/parsenodes.h src/include/nodes/parsenodes.h
+*** src.orig/include/nodes/parsenodes.h Fri Oct 16 11:53:58 1998
+--- src/include/nodes/parsenodes.h Fri Oct 16 13:35:32 1998
+***************
+*** 60,65 ****
+--- 60,67 ----
+
+ List *unionClause; /* unions are linked under the previous
+ * query */
++ Node *limitOffset; /* # of result tuples to skip */
++ Node *limitCount; /* # of result tuples to return */
+
+ /* internal to planner */
+ List *base_rel_list; /* base relation list */
+***************
+*** 639,644 ****
+--- 641,648 ----
+ char *portalname; /* the portal (cursor) to create */
+ bool binary; /* a binary (internal) portal? */
+ bool unionall; /* union without unique sort */
++ Node *limitOffset; /* # of result tuples to skip */
++ Node *limitCount; /* # of result tuples to return */
+ } SelectStmt;
+
+
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id LAA01724
+ for ; Thu, 22 Oct 1998 11:33:31 -0400 (EDT)
+Received: from hub.org (
[email protected] [209.47.148.200]) by renoir.op.net (o1/$Revision: 1.1 $) with ESMTP id LAA12702 for
; Thu, 22 Oct 1998 11:25:02 -0400 (EDT)
+Received: from localhost (majordom@localhost)
+ by hub.org (8.9.1/8.8.8) with SMTP id KAA11023;
+ Thu, 22 Oct 1998 10:22:13 -0400 (EDT)
+Received: by hub.org (TLB v0.10a (1.23 tibbs 1997/01/09 00:29:32)); Thu, 22 Oct 1998 10:21:07 +0000 (EDT)
+Received: (from majordom@localhost)
+ by hub.org (8.9.1/8.8.8) id KAA10873
+ for pgsql-hackers-outgoing; Thu, 22 Oct 1998 10:21:05 -0400 (EDT)
+X-Authentication-Warning: hub.org: majordom set sender to
[email protected] using -f
+ by hub.org (8.9.1/8.8.8) with ESMTP id KAA10847
+ for
; Thu, 22 Oct 1998 10:21:00 -0400 (EDT)
+Received: by dsh.de; id QAA09067; Thu, 22 Oct 1998 16:23:14 +0200 (MET DST)
+Received: from dshmail.dsh.de(53.47.15.3) by neptun.dsh.de via smap (3.2)
+ id xma008719; Thu, 22 Oct 98 16:22:40 +0200
+Received: from mail1.hh1.dsh.de (mail1.hh1.dsh.de [53.47.9.5])
+ by dshmail.dsh.de (8.8.7/8.8.7) with ESMTP id QAA01558;
+ Thu, 22 Oct 1998 16:19:55 +0200 (MET DST)
+ by mail1.hh1.dsh.de (8.8.7/8.8.7) with SMTP id QAA18978;
+ Thu, 22 Oct 1998 16:22:20 +0200
+Received: from orion.SAPserv.Hamburg.dsh.de
+ by mars.SAPserv.Hamburg.dsh.de with smtp
+ for <>
+ id m0zWJG2-000B5AC; Thu, 22 Oct 98 13:50 MET DST
+Received: by orion.SAPserv.Hamburg.dsh.de
+ id m0zWLoE-000EBPC; Thu, 22 Oct 98 16:33 MET DST
+Message-Id:
+Subject: Re: [HACKERS] psql's help (the LIMIT stuff)
+Date: Thu, 22 Oct 1998 16:33:50 +0200 (MET DST)
+X-Mailer: ELM [version 2.4 PL25]
+Content-Type: text
+Precedence: bulk
+Status: RO
+
+> >
+> > I hope the QUERY_LIMIT too.
+>
+> I still have that cnfify() possible fix to review for KQSO. Are you
+> still thinking limit for 6.4 final, or a minor release after that?
+
+ I posted the part that is the minimum applied to 6.4 to make
+ adding LIMIT later non-initdb earlier. Anyway, here it's
+ again.
+
+ My LIMIT implementation that does it like the SET in the
+ toplevel executor (but via parsetree values) is ready for
+ production. I only held it back because it's feature, not
+ bugfix.
+
+ Do you want it in 6.4 final?
+
+
+Jan
+
+--
+
+#======================================================================#
+# It's easier to get forgiveness for being wrong than for being right. #
+# Let's break this rule - forgive me. #
+
+
+diff -cr src.orig/backend/nodes/copyfuncs.c src/backend/nodes/copyfuncs.c
+*** src.orig/backend/nodes/copyfuncs.c Fri Oct 16 11:53:40 1998
+--- src/backend/nodes/copyfuncs.c Fri Oct 16 13:32:35 1998
+***************
+*** 1578,1583 ****
+--- 1578,1586 ----
+ newnode->unionClause = temp_list;
+ }
+
++ Node_Copy(from, newnode, limitOffset);
++ Node_Copy(from, newnode, limitCount);
++
+ return newnode;
+ }
+
+diff -cr src.orig/backend/nodes/outfuncs.c src/backend/nodes/outfuncs.c
+*** src.orig/backend/nodes/outfuncs.c Fri Oct 16 11:53:40 1998
+--- src/backend/nodes/outfuncs.c Fri Oct 16 13:30:50 1998
+***************
+*** 259,264 ****
+--- 259,268 ----
+ appendStringInfo(str, (node->hasSubLinks ? "true" : "false"));
+ appendStringInfo(str, " :unionClause ");
+ _outNode(str, node->unionClause);
++ appendStringInfo(str, " :limitOffset ");
++ _outNode(str, node->limitOffset);
++ appendStringInfo(str, " :limitCount ");
++ _outNode(str, node->limitCount);
+ }
+
+ static void
+diff -cr src.orig/backend/nodes/readfuncs.c src/backend/nodes/readfuncs.c
+*** src.orig/backend/nodes/readfuncs.c Fri Oct 16 11:53:40 1998
+--- src/backend/nodes/readfuncs.c Fri Oct 16 13:31:43 1998
+***************
+*** 163,168 ****
+--- 163,174 ----
+ token = lsptok(NULL, &length); /* skip :unionClause */
+ local_node->unionClause = nodeRead(true);
+
++ token = lsptok(NULL, &length); /* skip :limitOffset */
++ local_node->limitOffset = nodeRead(true);
++
++ token = lsptok(NULL, &length); /* skip :limitCount */
++ local_node->limitCount = nodeRead(true);
++
+ return local_node;
+ }
+
+diff -cr src.orig/include/nodes/parsenodes.h src/include/nodes/parsenodes.h
+*** src.orig/include/nodes/parsenodes.h Fri Oct 16 11:53:58 1998
+--- src/include/nodes/parsenodes.h Fri Oct 16 13:35:32 1998
+***************
+*** 60,65 ****
+--- 60,67 ----
+
+ List *unionClause; /* unions are linked under the previous
+ * query */
++ Node *limitOffset; /* # of result tuples to skip */
++ Node *limitCount; /* # of result tuples to return */
+
+ /* internal to planner */
+ List *base_rel_list; /* base relation list */
+***************
+*** 639,644 ****
+--- 641,648 ----
+ char *portalname; /* the portal (cursor) to create */
+ bool binary; /* a binary (internal) portal? */
+ bool unionall; /* union without unique sort */
++ Node *limitOffset; /* # of result tuples to skip */
++ Node *limitCount; /* # of result tuples to return */
+ } SelectStmt;
+
+
+
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id LAA21185
+ for ; Thu, 22 Oct 1998 11:01:00 -0400 (EDT)
+Received: from dsh.de (
[email protected] [53.122.101.2]) by renoir.op.net (o1/$Revision: 1.1 $) with ESMTP id KAA09646 for
; Thu, 22 Oct 1998 10:44:36 -0400 (EDT)
+Received: by dsh.de; id QAA19394; Thu, 22 Oct 1998 16:43:42 +0200 (MET DST)
+Received: from dshmail.dsh.de(53.47.15.3) by neptun.dsh.de via smap (3.2)
+ id xma017268; Thu, 22 Oct 98 16:39:44 +0200
+Received: from mail1.hh1.dsh.de (mail1.hh1.dsh.de [53.47.9.5])
+ by dshmail.dsh.de (8.8.7/8.8.7) with ESMTP id QAA02988;
+ Thu, 22 Oct 1998 16:36:46 +0200 (MET DST)
+ by mail1.hh1.dsh.de (8.8.7/8.8.7) with SMTP id QAA19155;
+ Thu, 22 Oct 1998 16:39:10 +0200
+Received: from orion.SAPserv.Hamburg.dsh.de
+ by mars.SAPserv.Hamburg.dsh.de with smtp
+ for <>
+ id m0zWJWL-000B5DC; Thu, 22 Oct 98 14:07 MET DST
+Received: by orion.SAPserv.Hamburg.dsh.de
+ id m0zWM4W-000EBPC; Thu, 22 Oct 98 16:50 MET DST
+Message-Id:
+Subject: Re: [HACKERS] psql's help (the LIMIT stuff)
+Date: Thu, 22 Oct 1998 16:50:40 +0200 (MET DST)
+X-Mailer: ELM [version 2.4 PL25]
+Content-Type: text
+Status: RO
+
+>
+> > > >
+> > > > I hope the QUERY_LIMIT too.
+> > >
+> > > I still have that cnfify() possible fix to review for KQSO. Are you
+> > > still thinking limit for 6.4 final, or a minor release after that?
+> >
+> > I posted the part that is the minimum applied to 6.4 to make
+> > adding LIMIT later non-initdb earlier. Anyway, here it's
+> > again.
+>
+> Already applied. I assume it is the same as the one I applied.
+
+ Seen, thanks. Your 'Applied' just arrived after I packed it
+ again. It's the same.
+
+> We are close to final, and can easily put it in 6.4.1, which I am sure
+> we will need, and if we split CVS trees, you'll have lots of minor
+> versions to pick from. :-)
+>
+> Seems like it would be a nice minor release item, but the problem is
+> that minor releases aren't tested as much as major ones. How confident
+> are you in the code? What do others thing?
+
+ I regression tested it, and did additional tests in the
+ SPI/PL area. It works. It only touches the parser and the
+ executor. Rules, planner/optimizer just bypass the values in
+ the parsetree. The parser and the executor are parts of
+ Postgres I feel very familiar with (not so in the optimizer).
+ I trust in the code and would use it in a production
+ environment.
+
+ It's below.
+
+
+Jan
+
+--
+
+#======================================================================#
+# It's easier to get forgiveness for being wrong than for being right. #
+# Let's break this rule - forgive me. #
+
+
+diff -cr src.orig/backend/commands/command.c src/backend/commands/command.c
+*** src.orig/backend/commands/command.c Fri Oct 16 11:53:38 1998
+--- src/backend/commands/command.c Fri Oct 16 12:56:44 1998
+***************
+*** 39,44 ****
+--- 39,45 ----
+ #include "utils/mcxt.h"
+ #include "utils/portal.h"
+ #include "utils/syscache.h"
++ #include "string.h"
+
+ /* ----------------
+ * PortalExecutorHeapMemory stuff
+***************
+*** 101,106 ****
+--- 102,108 ----
+ int feature;
+ QueryDesc *queryDesc;
+ MemoryContext context;
++ Const limcount;
+
+ /* ----------------
+ * sanity checks
+***************
+*** 113,118 ****
+--- 115,134 ----
+ }
+
+ /* ----------------
++ * Create a const node from the given count value
++ * ----------------
++ */
++ memset(&limcount, 0, sizeof(limcount));
++ limcount.type = T_Const;
++ limcount.consttype = INT4OID;
++ limcount.constlen = sizeof(int4);
++ limcount.constvalue = (Datum)count;
++ limcount.constisnull = FALSE;
++ limcount.constbyval = TRUE;
++ limcount.constisset = FALSE;
++ limcount.constiscast = FALSE;
++
++ /* ----------------
+ * get the portal from the portal name
+ * ----------------
+ */
+***************
+*** 176,182 ****
+ PortalExecutorHeapMemory = (MemoryContext)
+ PortalGetHeapMemory(portal);
+
+! ExecutorRun(queryDesc, PortalGetState(portal), feature, count);
+
+ if (dest == None) /* MOVE */
+ pfree(queryDesc);
+--- 192,198 ----
+ PortalExecutorHeapMemory = (MemoryContext)
+ PortalGetHeapMemory(portal);
+
+! ExecutorRun(queryDesc, PortalGetState(portal), feature, (Node *)NULL, (Node *)&limcount);
+
+ if (dest == None) /* MOVE */
+ pfree(queryDesc);
+diff -cr src.orig/backend/executor/execMain.c src/backend/executor/execMain.c
+*** src.orig/backend/executor/execMain.c Fri Oct 16 11:53:38 1998
+--- src/backend/executor/execMain.c Fri Oct 16 20:05:19 1998
+***************
+*** 64,69 ****
+--- 64,70 ----
+ static void EndPlan(Plan *plan, EState *estate);
+ static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
+ Query *parseTree, CmdType operation,
++ int offsetTuples,
+ int numberTuples, ScanDirection direction,
+ void (*printfunc) ());
+ static void ExecRetrieve(TupleTableSlot *slot, void (*printfunc) (),
+***************
+*** 163,169 ****
+ * ----------------------------------------------------------------
+ */
+ TupleTableSlot *
+! ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
+ {
+ CmdType operation;
+ Query *parseTree;
+--- 164,170 ----
+ * ----------------------------------------------------------------
+ */
+ TupleTableSlot *
+! ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, Node *limoffset, Node *limcount)
+ {
+ CmdType operation;
+ Query *parseTree;
+***************
+*** 171,176 ****
+--- 172,179 ----
+ TupleTableSlot *result;
+ CommandDest dest;
+ void (*destination) ();
++ int offset = 0;
++ int count = 0;
+
+ /******************
+ * sanity checks
+***************
+*** 191,196 ****
+--- 194,289 ----
+ estate->es_processed = 0;
+ estate->es_lastoid = InvalidOid;
+
++ /******************
++ * if given get the offset of the LIMIT clause
++ ******************
++ */
++ if (limoffset != NULL)
++ {
++ Const *coffset;
++ Param *poffset;
++ ParamListInfo paramLI;
++ int i;
++
++ switch (nodeTag(limoffset))
++ {
++ case T_Const:
++ coffset = (Const *)limoffset;
++ offset = (int)(coffset->constvalue);
++ break;
++
++ case T_Param:
++ poffset = (Param *)limoffset;
++ paramLI = estate->es_param_list_info;
++
++ if (paramLI == NULL)
++ elog(ERROR, "parameter for limit offset not in executor state");
++ for (i = 0; paramLI[i].kind != PARAM_INVALID; i++)
++ {
++ if (paramLI[i].kind == PARAM_NUM && paramLI[i].id == poffset->paramid)
++ break;
++ }
++ if (paramLI[i].kind == PARAM_INVALID)
++ elog(ERROR, "parameter for limit offset not in executor state");
++ if (paramLI[i].isnull)
++ elog(ERROR, "limit offset cannot be NULL value");
++ offset = (int)(paramLI[i].value);
++
++ break;
++
++ default:
++ elog(ERROR, "unexpected node type %d as limit offset", nodeTag(limoffset));
++ }
++
++ if (offset < 0)
++ elog(ERROR, "limit offset cannot be negative");
++ }
++
++ /******************
++ * if given get the count of the LIMIT clause
++ ******************
++ */
++ if (limcount != NULL)
++ {
++ Const *ccount;
++ Param *pcount;
++ ParamListInfo paramLI;
++ int i;
++
++ switch (nodeTag(limcount))
++ {
++ case T_Const:
++ ccount = (Const *)limcount;
++ count = (int)(ccount->constvalue);
++ break;
++
++ case T_Param:
++ pcount = (Param *)limcount;
++ paramLI = estate->es_param_list_info;
++
++ if (paramLI == NULL)
++ elog(ERROR, "parameter for limit count not in executor state");
++ for (i = 0; paramLI[i].kind != PARAM_INVALID; i++)
++ {
++ if (paramLI[i].kind == PARAM_NUM && paramLI[i].id == pcount->paramid)
++ break;
++ }
++ if (paramLI[i].kind == PARAM_INVALID)
++ elog(ERROR, "parameter for limit count not in executor state");
++ if (paramLI[i].isnull)
++ elog(ERROR, "limit count cannot be NULL value");
++ count = (int)(paramLI[i].value);
++
++ break;
++
++ default:
++ elog(ERROR, "unexpected node type %d as limit count", nodeTag(limcount));
++ }
++
++ if (count < 0)
++ elog(ERROR, "limit count cannot be negative");
++ }
++
+ switch (feature)
+ {
+
+***************
+*** 199,205 ****
+ plan,
+ parseTree,
+ operation,
+! ALL_TUPLES,
+ ForwardScanDirection,
+ destination);
+ break;
+--- 292,299 ----
+ plan,
+ parseTree,
+ operation,
+! offset,
+! count,
+ ForwardScanDirection,
+ destination);
+ break;
+***************
+*** 208,213 ****
+--- 302,308 ----
+ plan,
+ parseTree,
+ operation,
++ offset,
+ count,
+ ForwardScanDirection,
+ destination);
+***************
+*** 222,227 ****
+--- 317,323 ----
+ plan,
+ parseTree,
+ operation,
++ offset,
+ count,
+ BackwardScanDirection,
+ destination);
+***************
+*** 237,242 ****
+--- 333,339 ----
+ plan,
+ parseTree,
+ operation,
++ 0,
+ ONE_TUPLE,
+ ForwardScanDirection,
+ destination);
+***************
+*** 691,696 ****
+--- 788,794 ----
+ Plan *plan,
+ Query *parseTree,
+ CmdType operation,
++ int offsetTuples,
+ int numberTuples,
+ ScanDirection direction,
+ void (*printfunc) ())
+***************
+*** 742,747 ****
+--- 840,859 ----
+ {
+ result = NULL;
+ break;
++ }
++
++ /******************
++ * For now we completely execute the plan and skip
++ * result tuples if requested by LIMIT offset.
++ * Finally we should try to do it in deeper levels
++ * if possible (during index scan)
++ * - Jan
++ ******************
++ */
++ if (offsetTuples > 0)
++ {
++ --offsetTuples;
++ continue;
+ }
+
+ /******************
+diff -cr src.orig/backend/executor/functions.c src/backend/executor/functions.c
+*** src.orig/backend/executor/functions.c Fri Oct 16 11:53:38 1998
+--- src/backend/executor/functions.c Fri Oct 16 19:01:02 1998
+***************
+*** 130,135 ****
+--- 130,138 ----
+ None);
+ estate = CreateExecutorState();
+
++ if (queryTree->limitOffset != NULL || queryTree->limitCount != NULL)
++ elog(ERROR, "LIMIT clause from SQL functions not yet implemented");
++
+ if (nargs > 0)
+ {
+ int i;
+***************
+*** 200,206 ****
+
+ feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
+
+! return ExecutorRun(es->qd, es->estate, feature, 0);
+ }
+
+ static void
+--- 203,209 ----
+
+ feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
+
+! return ExecutorRun(es->qd, es->estate, feature, (Node *)NULL, (Node *)NULL);
+ }
+
+ static void
+diff -cr src.orig/backend/executor/spi.c src/backend/executor/spi.c
+*** src.orig/backend/executor/spi.c Fri Oct 16 11:53:39 1998
+--- src/backend/executor/spi.c Fri Oct 16 19:25:33 1998
+***************
+*** 791,796 ****
+--- 791,798 ----
+ bool isRetrieveIntoRelation = false;
+ char *intoName = NULL;
+ int res;
++ Const tcount_const;
++ Node *count = NULL;
+
+ switch (operation)
+ {
+***************
+*** 825,830 ****
+--- 827,865 ----
+ return SPI_ERROR_OPUNKNOWN;
+ }
+
++ /* ----------------
++ * Get the query LIMIT tuple count
++ * ----------------
++ */
++ if (parseTree->limitCount != NULL)
++ {
++ /* ----------------
++ * A limit clause in the parsetree overrides the
++ * tcount parameter
++ * ----------------
++ */
++ count = parseTree->limitCount;
++ }
++ else
++ {
++ /* ----------------
++ * No LIMIT clause in parsetree. Use a local Const node
++ * to put tcount into it
++ * ----------------
++ */
++ memset(&tcount_const, 0, sizeof(tcount_const));
++ tcount_const.type = T_Const;
++ tcount_const.consttype = INT4OID;
++ tcount_const.constlen = sizeof(int4);
++ tcount_const.constvalue = (Datum)tcount;
++ tcount_const.constisnull = FALSE;
++ tcount_const.constbyval = TRUE;
++ tcount_const.constisset = FALSE;
++ tcount_const.constiscast = FALSE;
++
++ count = (Node *)&tcount_const;
++ }
++
+ if (state == NULL) /* plan preparation */
+ return res;
+ #ifdef SPI_EXECUTOR_STATS
+***************
+*** 845,851 ****
+ return SPI_OK_CURSOR;
+ }
+
+! ExecutorRun(queryDesc, state, EXEC_FOR, tcount);
+
+ _SPI_current->processed = state->es_processed;
+ if (operation == CMD_SELECT && queryDesc->dest == SPI)
+--- 880,886 ----
+ return SPI_OK_CURSOR;
+ }
+
+! ExecutorRun(queryDesc, state, EXEC_FOR, parseTree->limitOffset, count);
+
+ _SPI_current->processed = state->es_processed;
+ if (operation == CMD_SELECT && queryDesc->dest == SPI)
+diff -cr src.orig/backend/parser/analyze.c src/backend/parser/analyze.c
+*** src.orig/backend/parser/analyze.c Fri Oct 16 11:53:41 1998
+--- src/backend/parser/analyze.c Fri Oct 16 13:29:27 1998
+***************
+*** 180,186 ****
+--- 180,190 ----
+
+ case T_SelectStmt:
+ if (!((SelectStmt *) parseTree)->portalname)
++ {
+ result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
++ result->limitOffset = ((SelectStmt *)parseTree)->limitOffset;
++ result->limitCount = ((SelectStmt *)parseTree)->limitCount;
++ }
+ else
+ result = transformCursorStmt(pstate, (SelectStmt *) parseTree);
+ break;
+diff -cr src.orig/backend/parser/gram.y src/backend/parser/gram.y
+*** src.orig/backend/parser/gram.y Fri Oct 16 11:53:42 1998
+--- src/backend/parser/gram.y Sun Oct 18 22:20:36 1998
+***************
+*** 45,50 ****
+--- 45,51 ----
+ #include "catalog/catname.h"
+ #include "utils/elog.h"
+ #include "access/xact.h"
++ #include "catalog/pg_type.h"
+
+ #ifdef MULTIBYTE
+ #include "mb/pg_wchar.h"
+***************
+*** 163,169 ****
+ sort_clause, sortby_list, index_params, index_list, name_list,
+ from_clause, from_list, opt_array_bounds, nest_array_bounds,
+ expr_list, attrs, res_target_list, res_target_list2,
+! def_list, opt_indirection, group_clause, TriggerFuncArgs
+
+ %type func_return
+ %type set_opt
+--- 164,171 ----
+ sort_clause, sortby_list, index_params, index_list, name_list,
+ from_clause, from_list, opt_array_bounds, nest_array_bounds,
+ expr_list, attrs, res_target_list, res_target_list2,
+! def_list, opt_indirection, group_clause, TriggerFuncArgs,
+! opt_select_limit
+
+ %type func_return
+ %type set_opt
+***************
+*** 192,197 ****
+--- 194,201 ----
+
+ %type fetch_how_many
+
++ %type select_limit_value select_offset_value
++
+ %type OptSeqList
+ %type OptSeqElem
+
+***************
+*** 267,273 ****
+ FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
+ GRANT, GROUP, HAVING, HOUR_P,
+ IN, INNER_P, INSENSITIVE, INSERT, INTERVAL, INTO, IS,
+! JOIN, KEY, LANGUAGE, LEADING, LEFT, LIKE, LOCAL,
+ MATCH, MINUTE_P, MONTH_P, NAMES,
+ NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULL_P, NUMERIC,
+ OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P,
+--- 271,277 ----
+ FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
+ GRANT, GROUP, HAVING, HOUR_P,
+ IN, INNER_P, INSENSITIVE, INSERT, INTERVAL, INTO, IS,
+! JOIN, KEY, LANGUAGE, LEADING, LEFT, LIKE, LIMIT, LOCAL,
+ MATCH, MINUTE_P, MONTH_P, NAMES,
+ NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULL_P, NUMERIC,
+ OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P,
+***************
+*** 299,305 ****
+ INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
+ LANCOMPILER, LISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE, MINVALUE, MOVE,
+ NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
+! OIDS, OPERATOR, PASSWORD, PROCEDURAL,
+ RECIPE, RENAME, RESET, RETURNS, ROW, RULE,
+ SEQUENCE, SERIAL, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED,
+ UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
+--- 303,309 ----
+ INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
+ LANCOMPILER, LISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE, MINVALUE, MOVE,
+ NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
+! OFFSET, OIDS, OPERATOR, PASSWORD, PROCEDURAL,
+ RECIPE, RENAME, RESET, RETURNS, ROW, RULE,
+ SEQUENCE, SERIAL, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED,
+ UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
+***************
+*** 2591,2596 ****
+--- 2595,2601 ----
+ result from_clause where_clause
+ group_clause having_clause
+ union_clause sort_clause
++ opt_select_limit
+ {
+ SelectStmt *n = makeNode(SelectStmt);
+ n->unique = $2;
+***************
+*** 2602,2607 ****
+--- 2607,2622 ----
+ n->havingClause = $8;
+ n->unionClause = $9;
+ n->sortClause = $10;
++ if ($11 != NIL)
++ {
++ n->limitOffset = nth(0, $11);
++ n->limitCount = nth(1, $11);
++ }
++ else
++ {
++ n->limitOffset = NULL;
++ n->limitCount = NULL;
++ }
+ $$ = (Node *)n;
+ }
+ ;
+***************
+*** 2699,2704 ****
+--- 2714,2794 ----
+ | ASC { $$ = "<"; }
+ | DESC { $$ = ">"; }
+ | /*EMPTY*/ { $$ = "<"; /*default*/ }
++ ;
++
++ opt_select_limit: LIMIT select_offset_value ',' select_limit_value
++ { $$ = lappend(lappend(NIL, $2), $4); }
++ | LIMIT select_limit_value OFFSET select_offset_value
++ { $$ = lappend(lappend(NIL, $4), $2); }
++ | LIMIT select_limit_value
++ { $$ = lappend(lappend(NIL, NULL), $2); }
++ | OFFSET select_offset_value LIMIT select_limit_value
++ { $$ = lappend(lappend(NIL, $2), $4); }
++ | OFFSET select_offset_value
++ { $$ = lappend(lappend(NIL, $2), NULL); }
++ | /* EMPTY */
++ { $$ = NIL; }
++ ;
++
++ select_limit_value: Iconst
++ {
++ Const *n = makeNode(Const);
++
++ if ($1 < 1)
++ elog(ERROR, "selection limit must be ALL or a positive integer value > 0");
++
++ n->consttype = INT4OID;
++ n->constlen = sizeof(int4);
++ n->constvalue = (Datum)$1;
++ n->constisnull = FALSE;
++ n->constbyval = TRUE;
++ n->constisset = FALSE;
++ n->constiscast = FALSE;
++ $$ = (Node *)n;
++ }
++ | ALL
++ {
++ Const *n = makeNode(Const);
++ n->consttype = INT4OID;
++ n->constlen = sizeof(int4);
++ n->constvalue = (Datum)0;
++ n->constisnull = FALSE;
++ n->constbyval = TRUE;
++ n->constisset = FALSE;
++ n->constiscast = FALSE;
++ $$ = (Node *)n;
++ }
++ | PARAM
++ {
++ Param *n = makeNode(Param);
++ n->paramkind = PARAM_NUM;
++ n->paramid = $1;
++ n->paramtype = INT4OID;
++ $$ = (Node *)n;
++ }
++ ;
++
++ select_offset_value: Iconst
++ {
++ Const *n = makeNode(Const);
++
++ n->consttype = INT4OID;
++ n->constlen = sizeof(int4);
++ n->constvalue = (Datum)$1;
++ n->constisnull = FALSE;
++ n->constbyval = TRUE;
++ n->constisset = FALSE;
++ n->constiscast = FALSE;
++ $$ = (Node *)n;
++ }
++ | PARAM
++ {
++ Param *n = makeNode(Param);
++ n->paramkind = PARAM_NUM;
++ n->paramid = $1;
++ n->paramtype = INT4OID;
++ $$ = (Node *)n;
++ }
+ ;
+
+ /*
+diff -cr src.orig/backend/parser/keywords.c src/backend/parser/keywords.c
+*** src.orig/backend/parser/keywords.c Fri Oct 16 11:53:42 1998
+--- src/backend/parser/keywords.c Sun Oct 18 22:13:29 1998
+***************
+*** 128,133 ****
+--- 128,134 ----
+ {"leading", LEADING},
+ {"left", LEFT},
+ {"like", LIKE},
++ {"limit", LIMIT},
+ {"listen", LISTEN},
+ {"load", LOAD},
+ {"local", LOCAL},
+***************
+*** 156,161 ****
+--- 157,163 ----
+ {"null", NULL_P},
+ {"numeric", NUMERIC},
+ {"of", OF},
++ {"offset", OFFSET},
+ {"oids", OIDS},
+ {"old", CURRENT},
+ {"on", ON},
+diff -cr src.orig/backend/rewrite/rewriteDefine.c src/backend/rewrite/rewriteDefine.c
+*** src.orig/backend/rewrite/rewriteDefine.c Fri Oct 16 11:53:46 1998
+--- src/backend/rewrite/rewriteDefine.c Fri Oct 16 13:48:55 1998
+***************
+*** 312,317 ****
+--- 312,323 ----
+ heap_close(event_relation);
+
+ /*
++ * LIMIT in view is not supported
++ */
++ if (query->limitOffset != NULL || query->limitCount != NULL)
++ elog(ERROR, "LIMIT clause not supported in views");
++
++ /*
+ * ... and finally the rule must be named _RETviewname.
+ */
+ sprintf(expected_name, "_RET%s", event_obj->relname);
+diff -cr src.orig/backend/tcop/pquery.c src/backend/tcop/pquery.c
+*** src.orig/backend/tcop/pquery.c Fri Oct 16 11:53:47 1998
+--- src/backend/tcop/pquery.c Fri Oct 16 14:02:36 1998
+***************
+*** 40,46 ****
+ #include "commands/command.h"
+
+ static char *CreateOperationTag(int operationType);
+! static void ProcessQueryDesc(QueryDesc *queryDesc);
+
+
+ /* ----------------------------------------------------------------
+--- 40,46 ----
+ #include "commands/command.h"
+
+ static char *CreateOperationTag(int operationType);
+! static void ProcessQueryDesc(QueryDesc *queryDesc, Node *limoffset, Node *limcount);
+
+
+ /* ----------------------------------------------------------------
+***************
+*** 205,211 ****
+ * ----------------------------------------------------------------
+ */
+ static void
+! ProcessQueryDesc(QueryDesc *queryDesc)
+ {
+ Query *parseTree;
+ Plan *plan;
+--- 205,211 ----
+ * ----------------------------------------------------------------
+ */
+ static void
+! ProcessQueryDesc(QueryDesc *queryDesc, Node *limoffset, Node *limcount)
+ {
+ Query *parseTree;
+ Plan *plan;
+***************
+*** 330,336 ****
+ * actually run the plan..
+ * ----------------
+ */
+! ExecutorRun(queryDesc, state, EXEC_RUN, 0);
+
+ /* save infos for EndCommand */
+ UpdateCommandInfo(operation, state->es_lastoid, state->es_processed);
+--- 330,336 ----
+ * actually run the plan..
+ * ----------------
+ */
+! ExecutorRun(queryDesc, state, EXEC_RUN, limoffset, limcount);
+
+ /* save infos for EndCommand */
+ UpdateCommandInfo(operation, state->es_lastoid, state->es_processed);
+***************
+*** 373,377 ****
+ print_plan(plan, parsetree);
+ }
+ else
+! ProcessQueryDesc(queryDesc);
+ }
+--- 373,377 ----
+ print_plan(plan, parsetree);
+ }
+ else
+! ProcessQueryDesc(queryDesc, parsetree->limitOffset, parsetree->limitCount);
+ }
+diff -cr src.orig/include/executor/executor.h src/include/executor/executor.h
+*** src.orig/include/executor/executor.h Fri Oct 16 11:53:56 1998
+--- src/include/executor/executor.h Fri Oct 16 12:04:17 1998
+***************
+*** 83,89 ****
+ * prototypes from functions in execMain.c
+ */
+ extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
+! extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count);
+ extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
+ extern HeapTuple ExecConstraints(char *caller, Relation rel, HeapTuple tuple);
+ #ifdef QUERY_LIMIT
+--- 83,89 ----
+ * prototypes from functions in execMain.c
+ */
+ extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
+! extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, Node *limoffset, Node *limcount);
+ extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
+ extern HeapTuple ExecConstraints(char *caller, Relation rel, HeapTuple tuple);
+ #ifdef QUERY_LIMIT
+
+ by candle.pha.pa.us (8.9.0/8.9.0) with ESMTP id NAA01350
+ for ; Thu, 22 Oct 1998 13:12:29 -0400 (EDT)
+Received: from hub.org (
[email protected] [209.47.148.200]) by renoir.op.net (o1/$Revision: 1.1 $) with ESMTP id MAA17808 for
; Thu, 22 Oct 1998 12:35:22 -0400 (EDT)
+Received: from localhost (majordom@localhost)
+ by hub.org (8.9.1/8.8.8) with SMTP id KAA14887;
+ Thu, 22 Oct 1998 10:49:09 -0400 (EDT)
+Received: by hub.org (TLB v0.10a (1.23 tibbs 1997/01/09 00:29:32)); Thu, 22 Oct 1998 10:44:59 +0000 (EDT)
+Received: (from majordom@localhost)
+ by hub.org (8.9.1/8.8.8) id KAA14445
+ for pgsql-hackers-outgoing; Thu, 22 Oct 1998 10:44:57 -0400 (EDT)
+X-Authentication-Warning: hub.org: majordom set sender to
[email protected] using -f
+ by hub.org (8.9.1/8.8.8) with ESMTP id KAA14431
+ for
; Thu, 22 Oct 1998 10:44:47 -0400 (EDT)
+Received: by dsh.de; id QAA19394; Thu, 22 Oct 1998 16:43:42 +0200 (MET DST)
+Received: from dshmail.dsh.de(53.47.15.3) by neptun.dsh.de via smap (3.2)
+ id xma017268; Thu, 22 Oct 98 16:39:44 +0200
+Received: from mail1.hh1.dsh.de (mail1.hh1.dsh.de [53.47.9.5])
+ by dshmail.dsh.de (8.8.7/8.8.7) with ESMTP id QAA02988;
+ Thu, 22 Oct 1998 16:36:46 +0200 (MET DST)
+ by mail1.hh1.dsh.de (8.8.7/8.8.7) with SMTP id QAA19155;
+ Thu, 22 Oct 1998 16:39:10 +0200
+Received: from orion.SAPserv.Hamburg.dsh.de
+ by mars.SAPserv.Hamburg.dsh.de with smtp
+ for <>
+ id m0zWJWL-000B5DC; Thu, 22 Oct 98 14:07 MET DST
+Received: by orion.SAPserv.Hamburg.dsh.de
+ id m0zWM4W-000EBPC; Thu, 22 Oct 98 16:50 MET DST
+Message-Id:
+Subject: Re: [HACKERS] psql's help (the LIMIT stuff)
+Date: Thu, 22 Oct 1998 16:50:40 +0200 (MET DST)
+X-Mailer: ELM [version 2.4 PL25]
+Content-Type: text
+Precedence: bulk
+Status: ROr
+
+>
+> > > >
+> > > > I hope the QUERY_LIMIT too.
+> > >
+> > > I still have that cnfify() possible fix to review for KQSO. Are you
+> > > still thinking limit for 6.4 final, or a minor release after that?
+> >
+> > I posted the part that is the minimum applied to 6.4 to make
+> > adding LIMIT later non-initdb earlier. Anyway, here it's
+> > again.
+>
+> Already applied. I assume it is the same as the one I applied.
+
+ Seen, thanks. Your 'Applied' just arrived after I packed it
+ again. It's the same.
+
+> We are close to final, and can easily put it in 6.4.1, which I am sure
+> we will need, and if we split CVS trees, you'll have lots of minor
+> versions to pick from. :-)
+>
+> Seems like it would be a nice minor release item, but the problem is
+> that minor releases aren't tested as much as major ones. How confident
+> are you in the code? What do others thing?
+
+ I regression tested it, and did additional tests in the
+ SPI/PL area. It works. It only touches the parser and the
+ executor. Rules, planner/optimizer just bypass the values in
+ the parsetree. The parser and the executor are parts of
+ Postgres I feel very familiar with (not so in the optimizer).
+ I trust in the code and would use it in a production
+ environment.
+
+ It's below.
+
+
+Jan
+
+--
+
+#======================================================================#
+# It's easier to get forgiveness for being wrong than for being right. #
+# Let's break this rule - forgive me. #
+
+
+diff -cr src.orig/backend/commands/command.c src/backend/commands/command.c
+*** src.orig/backend/commands/command.c Fri Oct 16 11:53:38 1998
+--- src/backend/commands/command.c Fri Oct 16 12:56:44 1998
+***************
+*** 39,44 ****
+--- 39,45 ----
+ #include "utils/mcxt.h"
+ #include "utils/portal.h"
+ #include "utils/syscache.h"
++ #include "string.h"
+
+ /* ----------------
+ * PortalExecutorHeapMemory stuff
+***************
+*** 101,106 ****
+--- 102,108 ----
+ int feature;
+ QueryDesc *queryDesc;
+ MemoryContext context;
++ Const limcount;
+
+ /* ----------------
+ * sanity checks
+***************
+*** 113,118 ****
+--- 115,134 ----
+ }
+
+ /* ----------------
++ * Create a const node from the given count value
++ * ----------------
++ */
++ memset(&limcount, 0, sizeof(limcount));
++ limcount.type = T_Const;
++ limcount.consttype = INT4OID;
++ limcount.constlen = sizeof(int4);
++ limcount.constvalue = (Datum)count;
++ limcount.constisnull = FALSE;
++ limcount.constbyval = TRUE;
++ limcount.constisset = FALSE;
++ limcount.constiscast = FALSE;
++
++ /* ----------------
+ * get the portal from the portal name
+ * ----------------
+ */
+***************
+*** 176,182 ****
+ PortalExecutorHeapMemory = (MemoryContext)
+ PortalGetHeapMemory(portal);
+
+! ExecutorRun(queryDesc, PortalGetState(portal), feature, count);
+
+ if (dest == None) /* MOVE */
+ pfree(queryDesc);
+--- 192,198 ----
+ PortalExecutorHeapMemory = (MemoryContext)
+ PortalGetHeapMemory(portal);
+
+! ExecutorRun(queryDesc, PortalGetState(portal), feature, (Node *)NULL, (Node *)&limcount);
+
+ if (dest == None) /* MOVE */
+ pfree(queryDesc);
+diff -cr src.orig/backend/executor/execMain.c src/backend/executor/execMain.c
+*** src.orig/backend/executor/execMain.c Fri Oct 16 11:53:38 1998
+--- src/backend/executor/execMain.c Fri Oct 16 20:05:19 1998
+***************
+*** 64,69 ****
+--- 64,70 ----
+ static void EndPlan(Plan *plan, EState *estate);
+ static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
+ Query *parseTree, CmdType operation,
++ int offsetTuples,
+ int numberTuples, ScanDirection direction,
+ void (*printfunc) ());
+ static void ExecRetrieve(TupleTableSlot *slot, void (*printfunc) (),
+***************
+*** 163,169 ****
+ * ----------------------------------------------------------------
+ */
+ TupleTableSlot *
+! ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
+ {
+ CmdType operation;
+ Query *parseTree;
+--- 164,170 ----
+ * ----------------------------------------------------------------
+ */
+ TupleTableSlot *
+! ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, Node *limoffset, Node *limcount)
+ {
+ CmdType operation;
+ Query *parseTree;
+***************
+*** 171,176 ****
+--- 172,179 ----
+ TupleTableSlot *result;
+ CommandDest dest;
+ void (*destination) ();
++ int offset = 0;
++ int count = 0;
+
+ /******************
+ * sanity checks
+***************
+*** 191,196 ****
+--- 194,289 ----
+ estate->es_processed = 0;
+ estate->es_lastoid = InvalidOid;
+
++ /******************
++ * if given get the offset of the LIMIT clause
++ ******************
++ */
++ if (limoffset != NULL)
++ {
++ Const *coffset;
++ Param *poffset;
++ ParamListInfo paramLI;
++ int i;
++
++ switch (nodeTag(limoffset))
++ {
++ case T_Const:
++ coffset = (Const *)limoffset;
++ offset = (int)(coffset->constvalue);
++ break;
++
++ case T_Param:
++ poffset = (Param *)limoffset;
++ paramLI = estate->es_param_list_info;
++
++ if (paramLI == NULL)
++ elog(ERROR, "parameter for limit offset not in executor state");
++ for (i = 0; paramLI[i].kind != PARAM_INVALID; i++)
++ {
++ if (paramLI[i].kind == PARAM_NUM && paramLI[i].id == poffset->paramid)
++ break;
++ }
++ if (paramLI[i].kind == PARAM_INVALID)
++ elog(ERROR, "parameter for limit offset not in executor state");
++ if (paramLI[i].isnull)
++ elog(ERROR, "limit offset cannot be NULL value");
++ offset = (int)(paramLI[i].value);
++
++ break;
++
++ default:
++ elog(ERROR, "unexpected node type %d as limit offset", nodeTag(limoffset));
++ }
++
++ if (offset < 0)
++ elog(ERROR, "limit offset cannot be negative");
++ }
++
++ /******************
++ * if given get the count of the LIMIT clause
++ ******************
++ */
++ if (limcount != NULL)
++ {
++ Const *ccount;
++ Param *pcount;
++ ParamListInfo paramLI;
++ int i;
++
++ switch (nodeTag(limcount))
++ {
++ case T_Const:
++ ccount = (Const *)limcount;
++ count = (int)(ccount->constvalue);
++ break;
++
++ case T_Param:
++ pcount = (Param *)limcount;
++ paramLI = estate->es_param_list_info;
++
++ if (paramLI == NULL)
++ elog(ERROR, "parameter for limit count not in executor state");
++ for (i = 0; paramLI[i].kind != PARAM_INVALID; i++)
++ {
++ if (paramLI[i].kind == PARAM_NUM && paramLI[i].id == pcount->paramid)
++ break;
++ }
++ if (paramLI[i].kind == PARAM_INVALID)
++ elog(ERROR, "parameter for limit count not in executor state");
++ if (paramLI[i].isnull)
++ elog(ERROR, "limit count cannot be NULL value");
++ count = (int)(paramLI[i].value);
++
++ break;
++
++ default:
++ elog(ERROR, "unexpected node type %d as limit count", nodeTag(limcount));
++ }
++
++ if (count < 0)
++ elog(ERROR, "limit count cannot be negative");
++ }
++
+ switch (feature)
+ {
+
+***************
+*** 199,205 ****
+ plan,
+ parseTree,
+ operation,
+! ALL_TUPLES,
+ ForwardScanDirection,
+ destination);
+ break;
+--- 292,299 ----
+ plan,
+ parseTree,
+ operation,
+! offset,
+! count,
+ ForwardScanDirection,
+ destination);
+ break;
+***************
+*** 208,213 ****
+--- 302,308 ----
+ plan,
+ parseTree,
+ operation,
++ offset,
+ count,
+ ForwardScanDirection,
+ destination);
+***************
+*** 222,227 ****
+--- 317,323 ----
+ plan,
+ parseTree,
+ operation,
++ offset,
+ count,
+ BackwardScanDirection,
+ destination);
+***************
+*** 237,242 ****
+--- 333,339 ----
+ plan,
+ parseTree,
+ operation,
++ 0,
+ ONE_TUPLE,
+ ForwardScanDirection,
+ destination);
+***************
+*** 691,696 ****
+--- 788,794 ----
+ Plan *plan,
+ Query *parseTree,
+ CmdType operation,
++ int offsetTuples,
+ int numberTuples,
+ ScanDirection direction,
+ void (*printfunc) ())
+***************
+*** 742,747 ****
+--- 840,859 ----
+ {
+ result = NULL;
+ break;
++ }
++
++ /******************
++ * For now we completely execute the plan and skip
++ * result tuples if requested by LIMIT offset.
++ * Finally we should try to do it in deeper levels
++ * if possible (during index scan)
++ * - Jan
++ ******************
++ */
++ if (offsetTuples > 0)
++ {
++ --offsetTuples;
++ continue;
+ }
+
+ /******************
+diff -cr src.orig/backend/executor/functions.c src/backend/executor/functions.c
+*** src.orig/backend/executor/functions.c Fri Oct 16 11:53:38 1998
+--- src/backend/executor/functions.c Fri Oct 16 19:01:02 1998
+***************
+*** 130,135 ****
+--- 130,138 ----
+ None);
+ estate = CreateExecutorState();
+
++ if (queryTree->limitOffset != NULL || queryTree->limitCount != NULL)
++ elog(ERROR, "LIMIT clause from SQL functions not yet implemented");
++
+ if (nargs > 0)
+ {
+ int i;
+***************
+*** 200,206 ****
+
+ feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
+
+! return ExecutorRun(es->qd, es->estate, feature, 0);
+ }
+
+ static void
+--- 203,209 ----
+
+ feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
+
+! return ExecutorRun(es->qd, es->estate, feature, (Node *)NULL, (Node *)NULL);
+ }
+
+ static void
+diff -cr src.orig/backend/executor/spi.c src/backend/executor/spi.c
+*** src.orig/backend/executor/spi.c Fri Oct 16 11:53:39 1998
+--- src/backend/executor/spi.c Fri Oct 16 19:25:33 1998
+***************
+*** 791,796 ****
+--- 791,798 ----
+ bool isRetrieveIntoRelation = false;
+ char *intoName = NULL;
+ int res;
++ Const tcount_const;
++ Node *count = NULL;
+
+ switch (operation)
+ {
+***************
+*** 825,830 ****
+--- 827,865 ----
+ return SPI_ERROR_OPUNKNOWN;
+ }
+
++ /* ----------------
++ * Get the query LIMIT tuple count
++ * ----------------
++ */
++ if (parseTree->limitCount != NULL)
++ {
++ /* ----------------
++ * A limit clause in the parsetree overrides the
++ * tcount parameter
++ * ----------------
++ */
++ count = parseTree->limitCount;
++ }
++ else
++ {
++ /* ----------------
++ * No LIMIT clause in parsetree. Use a local Const node
++ * to put tcount into it
++ * ----------------
++ */
++ memset(&tcount_const, 0, sizeof(tcount_const));
++ tcount_const.type = T_Const;
++ tcount_const.consttype = INT4OID;
++ tcount_const.constlen = sizeof(int4);
++ tcount_const.constvalue = (Datum)tcount;
++ tcount_const.constisnull = FALSE;
++ tcount_const.constbyval = TRUE;
++ tcount_const.constisset = FALSE;
++ tcount_const.constiscast = FALSE;
++
++ count = (Node *)&tcount_const;
++ }
++
+ if (state == NULL) /* plan preparation */
+ return res;
+ #ifdef SPI_EXECUTOR_STATS
+***************
+*** 845,851 ****
+ return SPI_OK_CURSOR;
+ }
+
+! ExecutorRun(queryDesc, state, EXEC_FOR, tcount);
+
+ _SPI_current->processed = state->es_processed;
+ if (operation == CMD_SELECT && queryDesc->dest == SPI)
+--- 880,886 ----
+ return SPI_OK_CURSOR;
+ }
+
+! ExecutorRun(queryDesc, state, EXEC_FOR, parseTree->limitOffset, count);
+
+ _SPI_current->processed = state->es_processed;
+ if (operation == CMD_SELECT && queryDesc->dest == SPI)
+diff -cr src.orig/backend/parser/analyze.c src/backend/parser/analyze.c
+*** src.orig/backend/parser/analyze.c Fri Oct 16 11:53:41 1998
+--- src/backend/parser/analyze.c Fri Oct 16 13:29:27 1998
+***************
+*** 180,186 ****
+--- 180,190 ----
+
+ case T_SelectStmt:
+ if (!((SelectStmt *) parseTree)->portalname)
++ {
+ result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
++ result->limitOffset = ((SelectStmt *)parseTree)->limitOffset;
++ result->limitCount = ((SelectStmt *)parseTree)->limitCount;
++ }
+ else
+ result = transformCursorStmt(pstate, (SelectStmt *) parseTree);
+ break;
+diff -cr src.orig/backend/parser/gram.y src/backend/parser/gram.y
+*** src.orig/backend/parser/gram.y Fri Oct 16 11:53:42 1998
+--- src/backend/parser/gram.y Sun Oct 18 22:20:36 1998
+***************
+*** 45,50 ****
+--- 45,51 ----
+ #include "catalog/catname.h"
+ #include "utils/elog.h"
+ #include "access/xact.h"
++ #include "catalog/pg_type.h"
+
+ #ifdef MULTIBYTE
+ #include "mb/pg_wchar.h"
+***************
+*** 163,169 ****
+ sort_clause, sortby_list, index_params, index_list, name_list,
+ from_clause, from_list, opt_array_bounds, nest_array_bounds,
+ expr_list, attrs, res_target_list, res_target_list2,
+! def_list, opt_indirection, group_clause, TriggerFuncArgs
+
+ %type func_return
+ %type set_opt
+--- 164,171 ----
+ sort_clause, sortby_list, index_params, index_list, name_list,
+ from_clause, from_list, opt_array_bounds, nest_array_bounds,
+ expr_list, attrs, res_target_list, res_target_list2,
+! def_list, opt_indirection, group_clause, TriggerFuncArgs,
+! opt_select_limit
+
+ %type func_return
+ %type set_opt
+***************
+*** 192,197 ****
+--- 194,201 ----
+
+ %type fetch_how_many
+
++ %type select_limit_value select_offset_value
++
+ %type OptSeqList
+ %type OptSeqElem
+
+***************
+*** 267,273 ****
+ FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
+ GRANT, GROUP, HAVING, HOUR_P,
+ IN, INNER_P, INSENSITIVE, INSERT, INTERVAL, INTO, IS,
+! JOIN, KEY, LANGUAGE, LEADING, LEFT, LIKE, LOCAL,
+ MATCH, MINUTE_P, MONTH_P, NAMES,
+ NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULL_P, NUMERIC,
+ OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P,
+--- 271,277 ----
+ FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
+ GRANT, GROUP, HAVING, HOUR_P,
+ IN, INNER_P, INSENSITIVE, INSERT, INTERVAL, INTO, IS,
+! JOIN, KEY, LANGUAGE, LEADING, LEFT, LIKE, LIMIT, LOCAL,
+ MATCH, MINUTE_P, MONTH_P, NAMES,
+ NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULL_P, NUMERIC,
+ OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P,
+***************
+*** 299,305 ****
+ INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
+ LANCOMPILER, LISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE, MINVALUE, MOVE,
+ NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
+! OIDS, OPERATOR, PASSWORD, PROCEDURAL,
+ RECIPE, RENAME, RESET, RETURNS, ROW, RULE,
+ SEQUENCE, SERIAL, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED,
+ UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
+--- 303,309 ----
+ INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
+ LANCOMPILER, LISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE, MINVALUE, MOVE,
+ NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
+! OFFSET, OIDS, OPERATOR, PASSWORD, PROCEDURAL,
+ RECIPE, RENAME, RESET, RETURNS, ROW, RULE,
+ SEQUENCE, SERIAL, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED,
+ UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
+***************
+*** 2591,2596 ****
+--- 2595,2601 ----
+ result from_clause where_clause
+ group_clause having_clause
+ union_clause sort_clause
++ opt_select_limit
+ {
+ SelectStmt *n = makeNode(SelectStmt);
+ n->unique = $2;
+***************
+*** 2602,2607 ****
+--- 2607,2622 ----
+ n->havingClause = $8;
+ n->unionClause = $9;
+ n->sortClause = $10;
++ if ($11 != NIL)
++ {
++ n->limitOffset = nth(0, $11);
++ n->limitCount = nth(1, $11);
++ }
++ else
++ {
++ n->limitOffset = NULL;
++ n->limitCount = NULL;
++ }
+ $$ = (Node *)n;
+ }
+ ;
+***************
+*** 2699,2704 ****
+--- 2714,2794 ----
+ | ASC { $$ = "<"; }
+ | DESC { $$ = ">"; }
+ | /*EMPTY*/ { $$ = "<"; /*default*/ }
++ ;
++
++ opt_select_limit: LIMIT select_offset_value ',' select_limit_value
++ { $$ = lappend(lappend(NIL, $2), $4); }
++ | LIMIT select_limit_value OFFSET select_offset_value
++ { $$ = lappend(lappend(NIL, $4), $2); }
++ | LIMIT select_limit_value
++ { $$ = lappend(lappend(NIL, NULL), $2); }
++ | OFFSET select_offset_value LIMIT select_limit_value
++ { $$ = lappend(lappend(NIL, $2), $4); }
++ | OFFSET select_offset_value
++ { $$ = lappend(lappend(NIL, $2), NULL); }
++ | /* EMPTY */
++ { $$ = NIL; }
++ ;
++
++ select_limit_value: Iconst
++ {
++ Const *n = makeNode(Const);
++
++ if ($1 < 1)
++ elog(ERROR, "selection limit must be ALL or a positive integer value > 0");
++
++ n->consttype = INT4OID;
++ n->constlen = sizeof(int4);
++ n->constvalue = (Datum)$1;
++ n->constisnull = FALSE;
++ n->constbyval = TRUE;
++ n->constisset = FALSE;
++ n->constiscast = FALSE;
++ $$ = (Node *)n;
++ }
++ | ALL
++ {
++ Const *n = makeNode(Const);
++ n->consttype = INT4OID;
++ n->constlen = sizeof(int4);
++ n->constvalue = (Datum)0;
++ n->constisnull = FALSE;
++ n->constbyval = TRUE;
++ n->constisset = FALSE;
++ n->constiscast = FALSE;
++ $$ = (Node *)n;
++ }
++ | PARAM
++ {
++ Param *n = makeNode(Param);
++ n->paramkind = PARAM_NUM;
++ n->paramid = $1;
++ n->paramtype = INT4OID;
++ $$ = (Node *)n;
++ }
++ ;
++
++ select_offset_value: Iconst
++ {
++ Const *n = makeNode(Const);
++
++ n->consttype = INT4OID;
++ n->constlen = sizeof(int4);
++ n->constvalue = (Datum)$1;
++ n->constisnull = FALSE;
++ n->constbyval = TRUE;
++ n->constisset = FALSE;
++ n->constiscast = FALSE;
++ $$ = (Node *)n;
++ }
++ | PARAM
++ {
++ Param *n = makeNode(Param);
++ n->paramkind = PARAM_NUM;
++ n->paramid = $1;
++ n->paramtype = INT4OID;
++ $$ = (Node *)n;
++ }
+ ;
+
+ /*
+diff -cr src.orig/backend/parser/keywords.c src/backend/parser/keywords.c
+*** src.orig/backend/parser/keywords.c Fri Oct 16 11:53:42 1998
+--- src/backend/parser/keywords.c Sun Oct 18 22:13:29 1998
+***************
+*** 128,133 ****
+--- 128,134 ----
+ {"leading", LEADING},
+ {"left", LEFT},
+ {"like", LIKE},
++ {"limit", LIMIT},
+ {"listen", LISTEN},
+ {"load", LOAD},
+ {"local", LOCAL},
+***************
+*** 156,161 ****
+--- 157,163 ----
+ {"null", NULL_P},
+ {"numeric", NUMERIC},
+ {"of", OF},
++ {"offset", OFFSET},
+ {"oids", OIDS},
+ {"old", CURRENT},
+ {"on", ON},
+diff -cr src.orig/backend/rewrite/rewriteDefine.c src/backend/rewrite/rewriteDefine.c
+*** src.orig/backend/rewrite/rewriteDefine.c Fri Oct 16 11:53:46 1998
+--- src/backend/rewrite/rewriteDefine.c Fri Oct 16 13:48:55 1998
+***************
+*** 312,317 ****
+--- 312,323 ----
+ heap_close(event_relation);
+
+ /*
++ * LIMIT in view is not supported
++ */
++ if (query->limitOffset != NULL || query->limitCount != NULL)
++ elog(ERROR, "LIMIT clause not supported in views");
++
++ /*
+ * ... and finally the rule must be named _RETviewname.
+ */
+ sprintf(expected_name, "_RET%s", event_obj->relname);
+diff -cr src.orig/backend/tcop/pquery.c src/backend/tcop/pquery.c
+*** src.orig/backend/tcop/pquery.c Fri Oct 16 11:53:47 1998
+--- src/backend/tcop/pquery.c Fri Oct 16 14:02:36 1998
+***************
+*** 40,46 ****
+ #include "commands/command.h"
+
+ static char *CreateOperationTag(int operationType);
+! static void ProcessQueryDesc(QueryDesc *queryDesc);
+
+
+ /* ----------------------------------------------------------------
+--- 40,46 ----
+ #include "commands/command.h"
+
+ static char *CreateOperationTag(int operationType);
+! static void ProcessQueryDesc(QueryDesc *queryDesc, Node *limoffset, Node *limcount);
+
+
+ /* ----------------------------------------------------------------
+***************
+*** 205,211 ****
+ * ----------------------------------------------------------------
+ */
+ static void
+! ProcessQueryDesc(QueryDesc *queryDesc)
+ {
+ Query *parseTree;
+ Plan *plan;
+--- 205,211 ----
+ * ----------------------------------------------------------------
+ */
+ static void
+! ProcessQueryDesc(QueryDesc *queryDesc, Node *limoffset, Node *limcount)
+ {
+ Query *parseTree;
+ Plan *plan;
+***************
+*** 330,336 ****
+ * actually run the plan..
+ * ----------------
+ */
+! ExecutorRun(queryDesc, state, EXEC_RUN, 0);
+
+ /* save infos for EndCommand */
+ UpdateCommandInfo(operation, state->es_lastoid, state->es_processed);
+--- 330,336 ----
+ * actually run the plan..
+ * ----------------
+ */
+! ExecutorRun(queryDesc, state, EXEC_RUN, limoffset, limcount);
+
+ /* save infos for EndCommand */
+ UpdateCommandInfo(operation, state->es_lastoid, state->es_processed);
+***************
+*** 373,377 ****
+ print_plan(plan, parsetree);
+ }
+ else
+! ProcessQueryDesc(queryDesc);
+ }
+--- 373,377 ----
+ print_plan(plan, parsetree);
+ }
+ else
+! ProcessQueryDesc(queryDesc, parsetree->limitOffset, parsetree->limitCount);
+ }
+diff -cr src.orig/include/executor/executor.h src/include/executor/executor.h
+*** src.orig/include/executor/executor.h Fri Oct 16 11:53:56 1998
+--- src/include/executor/executor.h Fri Oct 16 12:04:17 1998
+***************
+*** 83,89 ****
+ * prototypes from functions in execMain.c
+ */
+ extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
+! extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count);
+ extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
+ extern HeapTuple ExecConstraints(char *caller, Relation rel, HeapTuple tuple);
+ #ifdef QUERY_LIMIT
+--- 83,89 ----
+ * prototypes from functions in execMain.c
+ */
+ extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
+! extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, Node *limoffset, Node *limcount);
+ extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
+ extern HeapTuple ExecConstraints(char *caller, Relation rel, HeapTuple tuple);
+ #ifdef QUERY_LIMIT
+
+