Thread: BUG #14322: Possible inconsistent behavior with timestamp_to_str()
VGhlIGZvbGxvd2luZyBidWcgaGFzIGJlZW4gbG9nZ2VkIG9uIHRoZSB3ZWJz aXRlOgoKQnVnIHJlZmVyZW5jZTogICAgICAxNDMyMgpMb2dnZWQgYnk6ICAg ICAgICAgIEtlaXRoIEZpc2tlCkVtYWlsIGFkZHJlc3M6ICAgICAga2VpdGhA a2VpdGhmNC5jb20KUG9zdGdyZVNRTCB2ZXJzaW9uOiA5LjUuNApPcGVyYXRp bmcgc3lzdGVtOiAgIFVidW50dSAxNi4wNApEZXNjcmlwdGlvbjogICAgICAg IAoKSXQgc2VlbXMgd2hlbiBJIGNhbGwgdGltZXN0YW1wX3RvX3N0cigpIG9u IGEgbm9uLW51bGwgdmFsdWUgdGhlbiBjYWxsIGl0IG9uCmEgbnVsbCB2YWx1 ZSBpbiB0aGUgc2FtZSBzdGF0ZW1lbnQsIGl0IHJldHVybnMgdGhlIHByZXZp b3VzIG5vbi1udWxsIHZhbHVlLgpJJ3ZlIGluY2x1ZGVkIHRoZSBjb2RlIGFu ZCBkZWJ1ZyBsaW5lcyBmcm9tIHdoZXJlIEkgZW5jb3VudGVyZWQgdGhpcyB3 aGVuCnRlc3RpbmcgbXkgYXBwLg0KDQpJIHVzZSBTUEkgdG8gcmV0cmlldmUg dGhlIHZhbHVlczoNCg0KYXBwZW5kU3RyaW5nSW5mbygmYnVmLCAiU0VMRUNU IHN1Yl9taW46OnRpbWVzdGFtcHR6LCBzdWJfbWF4Ojp0aW1lc3RhbXB0egpG Uk9NICVzLmNoZWNrX3N1YnBhcnRpdGlvbl9saW1pdHMoJyVzLiVzJywgJ3Rp bWUnKSIsIHBhcnRtYW5fc2NoZW1hLApwYXJlbnRfc2NoZW1hbmFtZSwgcGFy ZW50X3RhYmxlbmFtZSk7DQpyZXQgPSBTUElfZXhlY3V0ZShidWYuZGF0YSwg dHJ1ZSwgMSk7DQpzdWJfdGltZXN0YW1wX21pbiA9IERhdHVtR2V0VGltZXN0 YW1wVHooU1BJX2dldGJpbnZhbChTUElfdHVwdGFibGUtPnZhbHNbMF0sClNQ SV90dXB0YWJsZS0+dHVwZGVzYywgMSwgJnN1Yl90aW1lc3RhbXBfbWluX2lz bnVsbCkpOyANCnN1Yl90aW1lc3RhbXBfbWF4ID0gRGF0dW1HZXRUaW1lc3Rh bXBUeihTUElfZ2V0YmludmFsKFNQSV90dXB0YWJsZS0+dmFsc1swXSwKU1BJ X3R1cHRhYmxlLT50dXBkZXNjLCAyLCAmc3ViX3RpbWVzdGFtcF9tYXhfaXNu dWxsKSk7IA0KDQpRdWVyeWluZyB0aGUgdGFibGUgbm9ybWFsbHksIGNhbiBz ZWUgaXQgcmV0dXJucyBudWxsczoNCg0Ka2VpdGhAa2VpdGg9IyBzZWxlY3Qg KiBmcm9tCnBhcnRtYW4uY2hlY2tfc3VicGFydGl0aW9uX2xpbWl0cygncGFy dG1hbl90ZXN0LnRpbWVfdGFwdGVzdF90YWJsZScsCid0aW1lJyk7DQogc3Vi X21pbiB8IHN1Yl9tYXggDQotLS0tLS0tLS0rLS0tLS0tLS0tDQogwqtOVUxM wrsgIHwgwqtOVUxMwrsNCigxIHJvdykNCg0KDQplbG9nKERFQlVHMSwgInN1 Yl90aW1lc3RhbXBfbWluOiAlcywgc3ViX3RpbWVzdGFtcF9taW5faXNudWxs OiAlZCwKc3ViX3RpbWVzdGFtcF9tYXg6ICVsZCwgc3ViX3RpbWVzdGFtcF9t YXhfaXNudWxsOiAlZCINCiAgICAgICAgLCB0aW1lc3RhbXB0el90b19zdHIo c3ViX3RpbWVzdGFtcF9taW4pLCBzdWJfdGltZXN0YW1wX21pbl9pc251bGws CnN1Yl90aW1lc3RhbXBfbWF4LCBzdWJfdGltZXN0YW1wX21heF9pc251bGwp Ow0KZWxvZyhERUJVRzEsICJydW5fbWFpbnQgYmVmb3JlIGxvb3A6IGN1cnJl bnRfcGFydGl0aW9uX3RpbWVzdGFtcDogJXMsCnByZW1hZGVfY291bnQ6ICVk LCBzdWJfdGltZXN0YW1wX21pbjogJXMsIHN1Yl90aW1lc3RhbXBfbWF4OiAl cyINCiAgICAgICAgLCB0aW1lc3RhbXB0el90b19zdHIoY3VycmVudF9wYXJ0 aXRpb25fdGltZXN0YW1wKSwgcHJlbWFkZV9jb3VudCwKdGltZXN0YW1wdHpf dG9fc3RyKHN1Yl90aW1lc3RhbXBfbWluKSwKdGltZXN0YW1wdHpfdG9fc3Ry KHN1Yl90aW1lc3RhbXBfbWF4KSk7DQplbG9nKERFQlVHMSwgInJ1bl9tYWlu dCBiZWZvcmUgbG9vcDI6IHByZW1hZGVfY291bnQ6ICVkLCBzdWJfdGltZXN0 YW1wX21pbjoKJXMsIHN1Yl90aW1lc3RhbXBfbWF4OiAlcyINCiAgICAgICAg LCBwcmVtYWRlX2NvdW50LCB0aW1lc3RhbXB0el90b19zdHIoc3ViX3RpbWVz dGFtcF9taW4pLAp0aW1lc3RhbXB0el90b19zdHIoc3ViX3RpbWVzdGFtcF9t YXgpKTsNCg0KICANCkNhbiBzZWUgdGhlIGluaXRpYWwgZGVidWcgbGluZSwg dGhpbmdzIGFyZSBub3JtYWwuIE5leHQgbGluZQpjdXJyZW50X3BhcnRpdGlv bl90aW1lc3RhbXAgaXMgcmV0dXJuZWQgYWxvbmcgd2l0aCB0aGUgc3ViIHZh bHVlcyB3aGljaCBhcmUKdGhlbiBlcXVhbCB0byBjdXJyZW50X3BhcnRpdGlv bl90aW1lc3RhbXAuIFJlbW92aW5nCmN1cnJlbnRfcGFydGl0aW9uX3RpbWVz dGFtcCBmcm9tIHRoZSBzYW1lIHN0YXRlbWVudCBpbiB0aGUgZm9sbG93aW5n IGxpbmUKdGhpbmdzIHJldHVybiBhcyBleHBlY3RlZC4NCg0KDQoyMDE2LTA5 LTA5IDE3OjM4OjE1IEVEVCBbXSBbOTgyOF06IFsyOS0xXSB1c2VyPSxkYj0s ZT0wMDAwMCBERUJVRzogCnN1Yl90aW1lc3RhbXBfbWluOiAxOTk5LTEyLTMx IDE5OjAwOjAwLTA1LCBzdWJfdGltZXN0YW1wX21pbl9pc251bGw6IDEsCnN1 Yl90aW1lc3RhbXBfbWF4OiAwLCBzdWJfdGltZXN0YW1wX21heF9pc251bGw6 IDENCjIwMTYtMDktMDkgMTc6Mzg6MTUgRURUIFtdIFs5ODI4XTogWzMwLTFd IHVzZXI9LGRiPSxlPTAwMDAwIERFQlVHOiAKcnVuX21haW50IGJlZm9yZSBs b29wOiBjdXJyZW50X3BhcnRpdGlvbl90aW1lc3RhbXA6IDIwMTYtMDktMTEg MDA6MDA6MDAtMDQsCnByZW1hZGVfY291bnQ6IDAsIHN1Yl90aW1lc3RhbXBf bWluOiAyMDE2LTA5LTExIDAwOjAwOjAwLTA0LApzdWJfdGltZXN0YW1wX21h eDogMjAxNi0wOS0xMSAwMDowMDowMC0wNA0KMjAxNi0wOS0wOSAxNzozODox NSBFRFQgW10gWzk4MjhdOiBbMzEtMV0gdXNlcj0sZGI9LGU9MDAwMDAgREVC VUc6IApydW5fbWFpbnQgYmVmb3JlIGxvb3AyOiBwcmVtYWRlX2NvdW50OiAw LCBzdWJfdGltZXN0YW1wX21pbjogMTk5OS0xMi0zMQoxOTowMDowMC0wNSwg c3ViX3RpbWVzdGFtcF9tYXg6IDE5OTktMTItMzEgMTk6MDA6MDAtMDUNCg0K SSBrbm93IEkgc2hvdWxkIGFsd2F5cyBjaGVjayBmb3IgYSBudWxsIHJldHVy biBmcm9tIFNQSSBiZWZvcmUgb3BlcmF0aW5nIG9uCmEgdmFsdWUsIGFuZCBJ IGRvIGJlZm9yZSBJIGFjdHVhbGx5IHVzZSB0aG9zZSB2YWx1ZXMuIEJ1dCBJ IGhhZCB0aGVtIGluIG15CmRlYnVnIGxpbmVzIHdoZXJlIGNoZWNraW5nIGZv ciB3aGV0aGVyIHRoZXkncmUgbnVsbCBiZWZvcmUgb3V0cHV0dGluZyB0bwpk ZWJ1ZyBkaWRuJ3Qgc2VlbSB0byBtYXR0ZXIgYW5kIGl0IHdhcyByZWFsbHkg Y29uZnVzaW5nIG1lIHdoeSB0aGUgdmFsdWVzCndlcmUgcmV0dXJuaW5nIHJl Y2VudCB0aW1lc3RhbXAgdmFsdWVzIHdoZW4gSSB3YXMgcHJldHR5IHN1cmUg dGhleSB3ZXJlCm51bGwuIE5vdCBzdXJlIGlmIHRoaXMgY2FuIGp1c3QgYmUg Y2hhbGtlZCB1cCB0byB1bmRlZmluZWQgYmVoYXZpb3Igd2hlbgpkZWFsaW5n IHdpdGggbnVsbHMgb3IgaXQncyBhbiBhY3R1YWwgcHJvYmxlbSwgc28gZmln dXJlZCBJJ2QgcmVwb3J0IGl0LgoK
On 2016-09-09 23:54:48 +0000, keith@keithf4.com wrote: > The following bug has been logged on the website: > > Bug reference: 14322 > Logged by: Keith Fiske > Email address: keith@keithf4.com > PostgreSQL version: 9.5.4 > Operating system: Ubuntu 16.04 > Description: > > It seems when I call timestamp_to_str() on a non-null value then call it on > a null value in the same statement, it returns the previous non-null value. > I've included the code and debug lines from where I encountered this when > testing my app. Uh. You can't just call timestamptz_to_str() on a NULL value. The datum doesn't have to have any meaningful value if it's null. > I know I should always check for a null return from SPI before operating on > a value, and I do before I actually use those values. But I had them in my > debug lines where checking for whether they're null before outputting to > debug didn't seem to matter and it was really confusing me why the values > were returning recent timestamp values when I was pretty sure they were > null. Not sure if this can just be chalked up to undefined behavior when > dealing with nulls or it's an actual problem, so figured I'd report it. I don't think there's an issue here. The datum value isn't guaranteed to be initialized if the value is null, and I think that's what you're seeing here. Greetings, Andres Freund
On Fri, Sep 9, 2016 at 8:02 PM, Andres Freund <andres@anarazel.de> wrote: > On 2016-09-09 23:54:48 +0000, keith@keithf4.com wrote: > > The following bug has been logged on the website: > > > > Bug reference: 14322 > > Logged by: Keith Fiske > > Email address: keith@keithf4.com > > PostgreSQL version: 9.5.4 > > Operating system: Ubuntu 16.04 > > Description: > > > > It seems when I call timestamp_to_str() on a non-null value then call it > on > > a null value in the same statement, it returns the previous non-null > value. > > I've included the code and debug lines from where I encountered this when > > testing my app. > > Uh. You can't just call timestamptz_to_str() on a NULL value. The datum > doesn't have to have any meaningful value if it's null. > > > > I know I should always check for a null return from SPI before operating > on > > a value, and I do before I actually use those values. But I had them in > my > > debug lines where checking for whether they're null before outputting to > > debug didn't seem to matter and it was really confusing me why the values > > were returning recent timestamp values when I was pretty sure they were > > null. Not sure if this can just be chalked up to undefined behavior when > > dealing with nulls or it's an actual problem, so figured I'd report it. > > I don't think there's an issue here. The datum value isn't guaranteed > to be initialized if the value is null, and I think that's what you're > seeing here. > > Greetings, > > Andres Freund > Understood. Just confused me for a while and wanted to make sure it wasn't a real issue. Thanks! Keith
On Fri, Sep 9, 2016 at 8:02 PM, Keith <keith@keithf4.com> wrote: > > > On Fri, Sep 9, 2016 at 8:02 PM, Andres Freund <andres@anarazel.de> wrote: > >> On 2016-09-09 23:54:48 +0000, keith@keithf4.com wrote: >> > The following bug has been logged on the website: >> > >> > Bug reference: 14322 >> > Logged by: Keith Fiske >> > Email address: keith@keithf4.com >> > PostgreSQL version: 9.5.4 >> > Operating system: Ubuntu 16.04 >> > Description: >> > >> > It seems when I call timestamp_to_str() on a non-null value then call >> it on >> > a null value in the same statement, it returns the previous non-null >> value. >> > I've included the code and debug lines from where I encountered this >> when >> > testing my app. >> >> Uh. You can't just call timestamptz_to_str() on a NULL value. The datum >> doesn't have to have any meaningful value if it's null. >> >> >> > I know I should always check for a null return from SPI before >> operating on >> > a value, and I do before I actually use those values. But I had them in >> my >> > debug lines where checking for whether they're null before outputting to >> > debug didn't seem to matter and it was really confusing me why the >> values >> > were returning recent timestamp values when I was pretty sure they were >> > null. Not sure if this can just be chalked up to undefined behavior when >> > dealing with nulls or it's an actual problem, so figured I'd report it. >> >> I don't think there's an issue here. The datum value isn't guaranteed >> to be initialized if the value is null, and I think that's what you're >> seeing here. >> >> Greetings, >> >> Andres Freund >> > > Understood. Just confused me for a while and wanted to make sure it wasn't > a real issue. Thanks! > > Keith > Ok, this may be more of an actual issue than I thought. It seems that, even if all variables to the timestamptz_to_str() function have a valid value set, if it is called multiple times in the same statement, it keeps the first value for all following occurrences. Belows is an example. I kept getting zero for the difference between these two timestamps and it was confusing the hell out of me until I did a debug output of the actual query being run. You can see if I swap the two values around, it keeps the same timestamp value of the first occurrence each time. I also output the variable values to ensure they're valid. My code: resetStringInfo(&buf); appendStringInfo(&buf, "SELECT round(EXTRACT('epoch' FROM age('%s', '%s')) / EXTRACT('epoch' FROM '%s'::interval))::int" , timestamptz_to_str(last_partition_timestamp), timestamptz_to_str(current_partition_timestamp), partition_interval); elog(DEBUG1, "getting premade: %s, last_partition_timestamp: %ld, current_partition_timestamp: %ld", buf.data, last_partition_timestamp, current_partition_timestamp); resetStringInfo(&buf); appendStringInfo(&buf, "SELECT round(EXTRACT('epoch' FROM age('%s', '%s')) / EXTRACT('epoch' FROM '%s'::interval))::int" , timestamptz_to_str(current_partition_timestamp), timestamptz_to_str(last_partition_timestamp), partition_interval); elog(DEBUG1, "getting premade: %s, last_partition_timestamp: %ld, current_partition_timestamp: %ld", buf.data, last_partition_timestamp, current_partition_timestamp); Log output: 2016-09-13 16:01:02 EDT [] [16417]: [24-1] user=,db=,e=00000 DEBUG: getting premade: SELECT round(EXTRACT('epoch' FROM age('2016-09-23 00:00:00-04', '2016-09-23 00:00:00-04')) / EXTRACT('epoch' FROM '1 day'::interval))::int, last_partition_timestamp: 527918400000000, current_partition_timestamp: 527227200000000 2016-09-13 16:01:02 EDT [] [16417]: [25-1] user=,db=,e=00000 DEBUG: getting premade: SELECT round(EXTRACT('epoch' FROM age('2016-09-15 00:00:00-04', '2016-09-15 00:00:00-04')) / EXTRACT('epoch' FROM '1 day'::interval))::int, last_partition_timestamp: 527918400000000, current_partition_timestamp: 527227200000000 Keith
On Tue, Sep 13, 2016 at 4:16 PM, Keith Fiske <keith@omniti.com> wrote: > > > > On Fri, Sep 9, 2016 at 8:02 PM, Keith <keith@keithf4.com> wrote: > >> >> >> On Fri, Sep 9, 2016 at 8:02 PM, Andres Freund <andres@anarazel.de> wrote: >> >>> On 2016-09-09 23:54:48 +0000, keith@keithf4.com wrote: >>> > The following bug has been logged on the website: >>> > >>> > Bug reference: 14322 >>> > Logged by: Keith Fiske >>> > Email address: keith@keithf4.com >>> > PostgreSQL version: 9.5.4 >>> > Operating system: Ubuntu 16.04 >>> > Description: >>> > >>> > It seems when I call timestamp_to_str() on a non-null value then call >>> it on >>> > a null value in the same statement, it returns the previous non-null >>> value. >>> > I've included the code and debug lines from where I encountered this >>> when >>> > testing my app. >>> >>> Uh. You can't just call timestamptz_to_str() on a NULL value. The datum >>> doesn't have to have any meaningful value if it's null. >>> >>> >>> > I know I should always check for a null return from SPI before >>> operating on >>> > a value, and I do before I actually use those values. But I had them >>> in my >>> > debug lines where checking for whether they're null before outputting >>> to >>> > debug didn't seem to matter and it was really confusing me why the >>> values >>> > were returning recent timestamp values when I was pretty sure they were >>> > null. Not sure if this can just be chalked up to undefined behavior >>> when >>> > dealing with nulls or it's an actual problem, so figured I'd report it. >>> >>> I don't think there's an issue here. The datum value isn't guaranteed >>> to be initialized if the value is null, and I think that's what you're >>> seeing here. >>> >>> Greetings, >>> >>> Andres Freund >>> >> >> Understood. Just confused me for a while and wanted to make sure it >> wasn't a real issue. Thanks! >> >> Keith >> > > > Ok, this may be more of an actual issue than I thought. It seems that, > even if all variables to the timestamptz_to_str() function have a valid > value set, if it is called multiple times in the same statement, it keeps > the first value for all following occurrences. Belows is an example. I kept > getting zero for the difference between these two timestamps and it was > confusing the hell out of me until I did a debug output of the actual query > being run. You can see if I swap the two values around, it keeps the same > timestamp value of the first occurrence each time. I also output the > variable values to ensure they're valid. > > My code: > resetStringInfo(&buf); > appendStringInfo(&buf, "SELECT round(EXTRACT('epoch' FROM age('%s', '%s')) > / EXTRACT('epoch' FROM '%s'::interval))::int" > , timestamptz_to_str(last_partition_timestamp), > timestamptz_to_str(current_partition_timestamp), partition_interval); > elog(DEBUG1, "getting premade: %s, last_partition_timestamp: %ld, > current_partition_timestamp: %ld", buf.data, last_partition_timestamp, > current_partition_timestamp); > > resetStringInfo(&buf); > appendStringInfo(&buf, "SELECT round(EXTRACT('epoch' FROM age('%s', '%s')) > / EXTRACT('epoch' FROM '%s'::interval))::int" > , timestamptz_to_str(current_partition_timestamp), > timestamptz_to_str(last_partition_timestamp), partition_interval); > elog(DEBUG1, "getting premade: %s, last_partition_timestamp: %ld, > current_partition_timestamp: %ld", buf.data, last_partition_timestamp, > current_partition_timestamp); > > > Log output: > 2016-09-13 16:01:02 EDT [] [16417]: [24-1] user=,db=,e=00000 DEBUG: > getting premade: SELECT round(EXTRACT('epoch' FROM age('2016-09-23 > 00:00:00-04', '2016-09-23 00:00:00-04')) / EXTRACT('epoch' FROM '1 > day'::interval))::int, last_partition_timestamp: 527918400000000, > current_partition_timestamp: 527227200000000 > 2016-09-13 16:01:02 EDT [] [16417]: [25-1] user=,db=,e=00000 DEBUG: > getting premade: SELECT round(EXTRACT('epoch' FROM age('2016-09-15 > 00:00:00-04', '2016-09-15 00:00:00-04')) / EXTRACT('epoch' FROM '1 > day'::interval))::int, last_partition_timestamp: 527918400000000, > current_partition_timestamp: 527227200000000 > > Keith > So think I found a fix for it for now using timestamp_out() instead (saw it referenced in the timestamptz_to_str() source). Just makes the string building more complicated appendStringInfo(&buf, "SELECT round(EXTRACT('epoch' FROM age('%s', '%s')) / EXTRACT('epoch' FROM '%s'::interval))::int" , DatumGetCString(DirectFunctionCall1(timestamp_out, TimestampTzGetDatum(last_partition_timestamp))) , DatumGetCString(DirectFunctionCall1(timestamp_out, TimestampTzGetDatum(current_partition_timestamp))) , partition_interval); Looking through the code it seems all calls to timestamp_to_str() only reference it once per statement. I don't see any reason why you shouldn't be able to call this function more than once at a time, though. Makes cases where you need text output of timestamps a lot easier to build.
Keith Fiske <keith@omniti.com> writes: > Ok, this may be more of an actual issue than I thought. It seems that, even > if all variables to the timestamptz_to_str() function have a valid value > set, if it is called multiple times in the same statement, it keeps the > first value for all following occurrences. What is current_partition_timestamp? Is it coming from the transaction timestamp? That doesn't change intra-statement ... regards, tom lane
On Tue, Sep 13, 2016 at 4:52 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote: > Keith Fiske <keith@omniti.com> writes: > > Ok, this may be more of an actual issue than I thought. It seems that, > even > > if all variables to the timestamptz_to_str() function have a valid value > > set, if it is called multiple times in the same statement, it keeps the > > first value for all following occurrences. > > What is current_partition_timestamp? Is it coming from the transaction > timestamp? That doesn't change intra-statement ... > > regards, tom lane > No, it's obtained elsewhere by querying the current max timestamp value from the partition set. Shouldn't matter if it was, though. You can see that all it takes to change the string output of the timestamp is swapping the values around. The values of the variables are the same in either case.
On 2016-09-13 22:58, Keith Fiske wrote: > No, it's obtained elsewhere by querying the current max timestamp value > from the partition set. > Shouldn't matter if it was, though. You can see that all it takes to change > the string output of the timestamp is swapping the values around. The > values of the variables are the same in either case. Did you miss this part? /* [ ... ] Note * also that the result is in a static buffer, not pstrdup'd. */ .m
On Tue, Sep 13, 2016 at 5:03 PM, Marko Tiikkaja <marko@joh.to> wrote: > On 2016-09-13 22:58, Keith Fiske wrote: > >> No, it's obtained elsewhere by querying the current max timestamp value >> from the partition set. >> Shouldn't matter if it was, though. You can see that all it takes to >> change >> the string output of the timestamp is swapping the values around. The >> values of the variables are the same in either case. >> > > Did you miss this part? > > /* [ ... ] Note > * also that the result is in a static buffer, not pstrdup'd. > */ > > > .m > Yeah I did see that when i went to look at its source and wondered if that may be why, but I'm still fairly new to C and wasn't sure that was the reason. Still think it would be nice to use it in a more flexible manner.
Keith Fiske <keith@omniti.com> writes: > On Tue, Sep 13, 2016 at 5:03 PM, Marko Tiikkaja <marko@joh.to> wrote: >> Did you miss this part? >> /* [ ... ] Note >> * also that the result is in a static buffer, not pstrdup'd. Oh, duh. > Yeah I did see that when i went to look at its source and wondered if that > may be why, but I'm still fairly new to C and wasn't sure that was the > reason. > Still think it would be nice to use it in a more flexible manner. You can pstrdup its result if you want. If it did that internally it would result in some memory leaks we didn't want, IIRC. regards, tom lane