Subject: Re: [PATCH] Add native windows on arm64 support

From: Tom Lane <[email-masked]>
Date: Thu, 07 Apr 2022 13:42:49 -0400

Andres Freund <[email-masked]> writes:
> On 2022-04-07 09:40:43 -0400, Tom Lane wrote:
>> If it causes problems down the road, how will we debug it?

> If what causes problems down the road? Afaics the patch doesn't change
> anything outside of windows-on-arm, so it shouldn't cause any breakage we care
> about until we get a buildfarm animal.

Do we have a volunteer to run a buildfarm animal?  I don't see much
point in thinking about this till there is one ready to go.

			regards, tom lane

---

From: Andres Freund <[email-masked]>
Date: Thu, 7 Apr 2022 10:53:59 -0700

Hi,

> point in thinking about this till there is one ready to go.

OP said that they might be able to:
https://www.postgresql.org/message-id/CAFPTBD_NZb%3Dq_6uE-QV%2BS%2Bpm%3DRc9sBKrg8CQ_Y3dc27Awrm7cQ%40mail.gmail.com

Niyas, any updates on that?

Greetings,

Andres Freund

---

From: Niyas Sait <[email-masked]>
Date: Tue, 19 Apr 2022 15:22:30 +0100

> Niyas, any updates on that?

Sorry for the delay! Configuring the scripts took some time. I have
successfully run the builfarm animal script using my git repository [1]
which contains the proposed patch on a Windows Arm64 machine.

I made a request to add a new machine to build farm through [2].

I believe we should be able to enable the build farm machine once the
change has been merged.

Thanks,
Niyas

[1] https://github.com/nsait-linaro/postgres.git
[2] https://buildfarm.postgresql.org/cgi-bin/register-form.pl

---

From: Michael Paquier <[email-masked]>
Date: Wed, 20 Apr 2022 09:28:09 +0900

> I made a request to add a new machine to build farm through [2].

Have you tested with the amount of coverage provided by vcregress.pl?

Another thing I was wondering about is if it would be possible to have
this option in Travis, but that does not seem to be the case:
https://docs.travis-ci.com/user/reference/windows/#windows-version

> I believe we should be able to enable the build farm machine once the

Better to wait for the beginning of the development cycle at this
stage, based on all the replies received.  That would bring us to the
beginning of July.

+               if ($solution->{platform} eq 'ARM64') {
+                       push(@pgportfiles, 'pg_crc32c_armv8_choose.c');
+                       push(@pgportfiles, 'pg_crc32c_armv8.c');
+               } else {
+                       push(@pgportfiles, 'pg_crc32c_sse42_choose.c');
+                       push(@pgportfiles, 'pg_crc32c_sse42.c');
+               }

+++ b/src/port/pg_crc32c_armv8.c
+#ifndef _MSC_VER
 #include <arm_acle.h>
+#endif
[ ... ]
+#ifdef _M_ARM64
+   /*
+    * arm64 way of hinting processor for spin loops optimisations
+    * ref: https://community.arm.com/support-forums/f/infrastructure-solutions-forum/48654/ssetoneon-faq
+    */
+   __isb(_ARM64_BARRIER_SY);
+#else
I think that such extra optimizations had better be in a separate
patch, and we should focus on getting the build done first.

+   # arm64 linker only supports dynamic base address
+   my $cfgrandbaseaddress = $self->{platform} eq 'ARM64' ? 'true' : 'false';
This issue is still lying around, and you may have been lucky.  Would
there be any issues to remove this change to get a basic support in?
As mentioned upthread, there is a long history of Postgres with ASLR.

This would mean that a basic patch could be done with just the changes
for gendef.pl, and the first part of the changes inMSBuildProject.pm.
Would that not be enough?
--
Michael

---

From: Niyas Sait <[email-masked]>
Date: Wed, 20 Apr 2022 10:43:06 +0100

> Have you tested with the amount of coverage provided by vcregress.pl?

I built and ran the relevant tests with the help of run_build.pl.

I think following tests are executed - check, contribcheck, ecpgcheck,
installcheck, isolationcheck, modulescheck, and upgradecheck.

> https://docs.travis-ci.com/user/reference/windows/#windows-version

Yes, I think Travis doesn't yet support Windows/Arm64 platform.

> +    * ref:
https://community.arm.com/support-forums/f/infrastructure-solutions-forum/48654/ssetoneon-faq

> Would that not be enough?

I cannot build without any of the above changes. Nothing specific for
optimization
is added to this patch.

> As mentioned upthread, there is a long history of Postgres with ASLR.

MSVC linker doesn't allow non-random base addresses for Arm64 platforms.
It is needed for basic support.

Niyas

---

From: Michael Paquier <[email-masked]>
Date: Thu, 21 Apr 2022 13:07:18 +0900

> It is needed for basic support.

Why does it not allow that?  Is that just a limitation of the
compiler?  If yes, what is the error happening?  This question is not
exactly answered yet as of this thread.  I may be missing a reference
about that in the upstream docs, but I see nowhere an explanation
about the reason and the why.  That's also one of the first questions
from Thomas upthread.
--
Michael

---

From: Niyas Sait <[email-masked]>
Date: Thu, 21 Apr 2022 10:21:04 +0100

> about the reason and the why.  That's also one of the first questions

The following error occurs:

LINK : fatal error LNK1246: '/DYNAMICBASE:NO' not compatible with 'ARM64'
target machine; link without '/DYNAMICBASE:NO

This seems to be a deliberate restriction for Arm64 targets. However, no
references were provided. To clarify, I have posted a question [1] on the
community channel of Visual Studio.

Niyas

[1]
https://developercommunity.visualstudio.com/t/LINK-:-fatal-error-LNK1246:-DYNAMICBAS/10020163

---

From: Michael Paquier <[email-masked]>
Date: Mon, 25 Apr 2022 10:17:16 +0900

> target machine; link without '/DYNAMICBASE:NO

Okay, that's interesting.  In light of things like 7f3e17b, that may
be annoying.  Perhaps newer Windows versions are able to handle that
better.  I am wondering if it would be worth re-evaluating this
change, and attempt to re-enable that in environments other than
arm64.  This could become interesting if we consider that there have
been talks to cut the support for a bunch of Windows versions to focus
on the newer ones only.

> community channel of Visual Studio.

Thanks for doing that!  Your post is just a couple of days old, let's
see if we get a reply on that.
--
Michael

---

From: Niyas Sait <[email-masked]>
Date: Mon, 9 May 2022 12:44:22 +0100

> see if we get a reply on that.

Microsoft updated documentation [1] and clarified that ASLR cannot be
disabled for Arm64 targets.

Because ASLR can't be disabled on ARM, ARM64, or ARM64EC architectures,
> /DYNAMICBASE:NO isn't supported for these targets.

Thanks
Niyas

[1]
https://docs.microsoft.com/en-us/cpp/build/reference/dynamicbase?view=msvc-170

---

From: Michael Paquier <[email-masked]>
Date: Tue, 10 May 2022 10:01:55 +0900

> > /DYNAMICBASE:NO isn't supported for these targets.

Better than nothing, I guess, though this does not stand as a reason
explaining why this choice has been made.  And it looks like we will
never know.  We are going to need more advanced testing to check that
DYNAMICBASE is stable enough if we begin to enable it.  Perhaps we
should really look at that for all the build targets we currently
support rather than just ARM, for the most modern Win32 environments
if we are going to cut support for most of the oldest releases..
--
Michael

---

From: Niyas Sait <[email-masked]>
Date: Thu, 14 Jul 2022 09:36:13 +0100

Hello,

Please find a new patch (no further changes) rebased on top of the master.

-- 
Niyas Sait
Linaro Limited

---

From: Andres Freund <[email-masked]>
Date: Thu, 25 Aug 2022 18:29:07 -0700

Hi,

> if we are going to cut support for most of the oldest releases..

I accidentally did a lot of testing with DYNAMICBASE - I accidentally
mistranslated MSBuildProject.pm's <RandomizedBaseAddress>false</> to adding
/DYNAMICBASE in the meson port. Survived several hundred CI cycles and dozens
of local runs without observing any problems related to that.

Which doesn't surprise me, given the way we reserve space in the new process.

Greetings,

Andres Freund

---

From: Michael Paquier <[email-masked]>
Date: Fri, 26 Aug 2022 11:09:41 +0900

> Which doesn't surprise me, given the way we reserve space in the new process.

Still, we had problems with that in the past and I don't recall huge
changes in the way we allocate shmem on WIN32.  Could there be some
stuff in Windows itself that would explain more stability?  I would
not mind switching back to DYNAMICBASE on HEAD seeing your results.
That would simplify this thread's patch a bit as well.
--
Michael

---

From: Andres Freund <[email-masked]>
Date: Thu, 25 Aug 2022 20:29:43 -0700

Hi,

On 2022-08-26 11:09:41 +0900, Michael Paquier wrote:
> changes in the way we allocate shmem on WIN32.

It's really not great that 7f3e17b4827 disabled randomization without even a
comment as to why...

There actually seems to have been a lot of work in that area. See 617dc6d299c
/ bcbf2346d69 and as well as the retrying in 45e004fb4e39. Those weren't
prevented by us disabling ASLR.

Greetings,

Andres Freund

---

From: Michael Paquier <[email-masked]>
Date: Sat, 27 Aug 2022 11:33:51 +0900

> It's really not great that 7f3e17b4827 disabled randomization without even a

This story is on this thread, with some processes not able to start:
https://www.postgresql.org/message-id/BD0D89EC2438455C9DE0DC94D36912F4@maumau

> prevented by us disabling ASLR.

Indeed.  45e004f looks like the most interesting bit here.  FWIW, I
would not mind re-enabling that on HEAD, as of something like the
attached.  I have done a dozen of runs without seeing a test failure,
and knowing that we don't support anything older than Win10 makes me
feel safer about this change.  Any objections?
--
Michael
From 0fde47c2d4e65bd9f1f5a7b4607f8b8b95162242 Mon Sep 17 00:00:00 2001
From: Michael Paquier <[email-masked]>
Date: Sat, 27 Aug 2022 10:20:53 +0900
Subject: [PATCH] Re-enable ASLR on Windows

---
 src/tools/msvc/MSBuildProject.pm | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/tools/msvc/MSBuildProject.pm b/src/tools/msvc/MSBuildProject.pm
index 62acdda3a1..594729ceb7 100644
--- a/src/tools/msvc/MSBuildProject.pm
+++ b/src/tools/msvc/MSBuildProject.pm
@@ -347,7 +347,6 @@ sub WriteItemDefinitionGroup
       <ProgramDatabaseFile>.\\$cfgname\\$self->{name}\\$self->{name}.pdb</ProgramDatabaseFile>
       <GenerateMapFile>false</GenerateMapFile>
       <MapFileName>.\\$cfgname\\$self->{name}\\$self->{name}.map</MapFileName>
-      <RandomizedBaseAddress>false</RandomizedBaseAddress>
       <!-- Permit links to MinGW-built, 32-bit DLLs (default before VS2012). -->
       <ImageHasSafeExceptionHandlers/>
       <SubSystem>Console</SubSystem>
-- 
2.37.2

---

From: Tom Lane <[email-masked]>
Date: Fri, 26 Aug 2022 23:02:58 -0400

> feel safer about this change.  Any objections?

We're early enough in the v16 cycle to have plenty of time to detect
any problems, so I see little reason not to try it.

			regards, tom lane

---

From: Andres Freund <[email-masked]>
Date: Sat, 27 Aug 2022 12:27:57 -0700

Hi,

On 2022-08-27 11:33:51 +0900, Michael Paquier wrote:
> FWIW, I would not mind re-enabling that on HEAD, as of something like the
> attached.  I have done a dozen of runs without seeing a test failure, and
> knowing that we don't support anything older than Win10 makes me feel safer
> about this change.  Any objections?

+1. We don't have a choice about it on other architectures and it seems like a
bad idea to disable on its own.

I checked, and it looks like we didn't add --disable-dynamicbase, so ASLR
wasn't disabled for mingw builds.

Greetings,

Andres Freund

---

From: Michael Paquier <[email-masked]>
Date: Sun, 28 Aug 2022 16:23:08 +0900

> wasn't disabled for mingw builds.

True enough.  I have applied the patch to re-enable that on HEAD.
Let's now see what happens in the next couple of days.  Popcorn is
ready here.
--
Michael

---

From: Tom Lane <[email-masked]>
Date: Sun, 28 Aug 2022 10:09:53 -0400

> Let's now see what happens in the next couple of days.  Popcorn is

Hmm:

https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=bowerbird&dt=2022-08-28%2010%3A30%3A29

Maybe that's just unrelated noise, but it sure looks weird: after
passing the core regression tests in "make check", it failed in
pg_upgrade's run with

diff -w -U3 H:/prog/bf/root/HEAD/pgsql.build/src/test/regress/expected/xml.out H:/prog/bf/root/HEAD/pgsql.build/src/bin/pg_upgrade/tmp_check/results/xml.out
--- H:/prog/bf/root/HEAD/pgsql.build/src/test/regress/expected/xml.out	Wed May 18 18:30:12 2022
+++ H:/prog/bf/root/HEAD/pgsql.build/src/bin/pg_upgrade/tmp_check/results/xml.out	Sun Aug 28 06:55:14 2022
@@ -1557,7 +1557,7 @@
 \\x
 SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS x xml PATH 'node()', y xml PATH '/');
 -[ RECORD 1 ]-----------------------------------------------------------
-x | pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post
+x | pre<?pi arg?><![CDATA[&ent1]]><!--c1--><n2>&amp;deep</n2>post
 y | <e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>+
   | 

I don't recall ever seeing a failure quite like this.

			regards, tom lane

---

From: Andres Freund <[email-masked]>
Date: Sun, 28 Aug 2022 08:41:54 -0700

Hi,

> +++ H:/prog/bf/root/HEAD/pgsql.build/src/bin/pg_upgrade/tmp_check/results/xml.out	Sun Aug 28 06:55:14 2022

Pretty weird, agreed. But hard to see how it could be caused by the
randomization change, except that perhaps it could highlight a preexisting
bug?

Greetings,

Andres Freund

---

From: Tom Lane <[email-masked]>
Date: Sun, 28 Aug 2022 11:51:19 -0400

> randomization change, except that perhaps it could highlight a preexisting

I have no idea either.  I agree there *shouldn't* be any connection,
so if ASLR is somehow triggering this then whatever is failing is
almost certainly buggy on its own terms.  But there's a lot of
moving parts here (mumble libxml mumble).  I'm going to wait to see
if it reproduces before spending much effort.

			regards, tom lane

---

From: Michael Paquier <[email-masked]>
Date: Mon, 29 Aug 2022 10:12:20 +0900

> if it reproduces before spending much effort.

I have noticed that yesterday, but cannot think much about it.  This
basically changes the position of "<!--c1-->" for the first record,
leaving the second one untouched:
<!--c1--><?pi arg?><![CDATA[&ent1]]>
<?pi arg?><![CDATA[&ent1]]><!--c1-->

I am not used to xmltable(), but I wonder if there is something in one
of these support functions in xml.c that gets influenced by the
randomization.  That sounds a bit hairy as make check passed in
bowerbird, and I have noticed at least two other Windows hosts running
TAP that passed.  Or that's just something with libxml itself.
--
Michael

---

From: Michael Paquier <[email-masked]>
Date: Tue, 30 Aug 2022 08:48:38 +0900

> TAP that passed.  Or that's just something with libxml itself.

This is amazing.  The issue has showed up a second time in a row in
bowerbird, as of:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=bowerbird&dt=2022-08-29%2013%3A30%3A32

I don't know what to think about ASLR that manipulates the comment in
this XML object under VS 2017 (perhaps a compiler issue?), but it
should be possible to go back to green simply by removing "<!--c1-->"
from the input string.  Creating an extra output pattern here would be
very costly, as xml_2.out and xml.out have outputs for --with-libxml.

Would people object if I do that for now?
--
Michael

---

From: Tom Lane <[email-masked]>
Date: Mon, 29 Aug 2022 19:58:31 -0400

> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=bowerbird&dt=2022-08-29%2013%3A30%3A32

That is fascinating.

> Would people object if I do that for now?

Let's let it go for a few more runs.  I want to know whether it
reproduces 100% or only sometimes.

			regards, tom lane

---

From: Andres Freund <[email-masked]>
Date: Mon, 29 Aug 2022 17:09:54 -0700

Hi,

> Would people object if I do that for now?

I do. Clearly something is wrong, what do we gain by averting our eyes? An
alternative output file strikes me as an equally bad idea - it's not like this
is a case where there are legitimately different outputs?

Bowerbird is the only animal testing libxml with visual studio afaics. And CI
doesn't have libxml either.  So we have no idea if it's the msvc version that
matters.

Andrew, what version of libxml is installed? Afaict we don't see that anywhere
with msvc.

Greetings,

Andres Freund

---

From: Andres Freund <[email-masked]>
Date: Mon, 29 Aug 2022 17:33:56 -0700

Hi,

> reproduces 100% or only sometimes.

The weirdest part is that it only happens as part of the the pg_upgrade test.

I just tested it in my test windows vm (win 10, vs 2019), with a build of
libxml I had around (2.9.7), and the regression tests passed both "normally"
and within pg_upgrade.

Greetings,

Andres Freund

---

From: Michael Paquier <[email-masked]>
Date: Tue, 30 Aug 2022 10:35:35 +0900

> The weirdest part is that it only happens as part of the the pg_upgrade test.

make check has just failed:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=bowerbird&dt=2022-08-30%2001%3A15%3A13

> libxml I had around (2.9.7), and the regression tests passed both "normally"

bowerbird is feeling from c:\\prog\\3p64\\include\\libxml2 and
c:\\prog\\3p64\\lib\\libxml2.lib.  I am not sure which version of
libxml this is, and the other animals of MSVC don't use libxml so it
is not possible to correlate that only to VS 2017, either.
--
Michael

---

From: Tom Lane <[email-masked]>
Date: Mon, 29 Aug 2022 22:00:10 -0400

> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=bowerbird&dt=2022-08-30%2001%3A15%3A13

So it *is* probabilistic, which is pretty much what you'd expect
if ASLR triggers it.  That brings us no closer to understanding
what the mechanism is, though.

			regards, tom lane

---

From: Michael Paquier <[email-masked]>
Date: Wed, 31 Aug 2022 08:36:01 +0900

> what the mechanism is, though.

There have been more failures, always switching the input from
"pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post"
to "pre<?pi arg?><![CDATA[&ent1]]><!--c1--><n2>&amp;deep</n2>post".

Using a PATH of node() influences the output.  I am not verse unto
XMLTABLE, but could it be an issue where each node is parsed and we
have something like a qsort() applied on the pointer addresses for
each part or something like that, causing the output to become
unstable?
--
Michael

---

From: Tom Lane <[email-masked]>
Date: Tue, 30 Aug 2022 20:07:27 -0400

> each part or something like that, causing the output to become

I'm not sure.  I dug into this enough to find that the output from
this query is generated in xml.c lines 4635ff:

                    /* Concatenate serialized values */
                    initStringInfo(&str);
                    for (int i = 0; i < count; i++)
                    {
                        textstr =
                            xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
                                                 xtCxt->xmlerrcxt);

                        appendStringInfoText(&str, textstr);
                    }
                    cstr = str.data;

So evidently, the problem occurs because the elements of the
nodesetval->nodeTab[] array are in a different order than we expect.

I looked into <xpath.h> and found the definition of the "nodesetval"
struct, and the comments are eye-opening to say the least:

/*
 * A node-set (an unordered collection of nodes without duplicates).
 */
typedef struct _xmlNodeSet xmlNodeSet;
typedef xmlNodeSet *xmlNodeSetPtr;
struct _xmlNodeSet {
    int nodeNr;			/* number of nodes in the set */
    int nodeMax;		/* size of the array as allocated */
    xmlNodePtr *nodeTab;	/* array of nodes in no particular order */
    /* @@ with_ns to check wether namespace nodes should be looked at @@ */
};

It seems like maybe we're relying on an ordering we should not.
Yet surely the ordering of the pieces of this output is meaningful?
Are we using the wrong libxml API to create this result?

Anyway, I'm now suspicious that we've accidentally exposed a logic
bug in the XMLTABLE code, rather than anything wrong with the
ASLR stuff.

			regards, tom lane

---

From: Michael Paquier <[email-masked]>
Date: Wed, 31 Aug 2022 09:15:00 +0900

> each part or something like that, causing the output to become

Hmm.  I think that I may have an idea here after looking at our xml.c
and xpath.c in libxml2/.  From what I understand, we process the PATH
through XmlTableGetValue() that builds a XML node path in
xmlXPathCompiledEval().  The interesting part comes from libxml2's
xmlXPathCompiledEvalInternal(), where I think we don't apply a sort on
the contents generated.  Hence, I am wondering if the solution here
would be to do one xmlXPathNodeSetSort(xpathobj->nodesetval) after
compiling the path with xmlXPathCompiledEval() in XmlTableGetValue().
This should ensure that the items are ordered even if ASLR mixes if
the pointer positions.

A complete solution would involve more code paths, but we'd need only
one change in XmlTableGetValue() for the current regression tests to
work.  I don't have an environment where I can reproduce that, so that
would be up to the buildfarm to stress this solution..

Thoughts?
--
Michael

---

From: Michael Paquier <[email-masked]>
Date: Wed, 31 Aug 2022 09:16:50 +0900

> bug in the XMLTABLE code, rather than anything wrong with the

Funny.  I have reached basically the same conclusion as you a couple
of minutes ago, but I also think that I have found what we need to do
here to ensure the ordering of the nodes generated by the libxml
code.
--
Michael

---

From: Tom Lane <[email-masked]>
Date: Tue, 30 Aug 2022 20:28:58 -0400

Michael Paquier <[email-masked]> writes:
> Hence, I am wondering if the solution here

Hmm, I see that function declared in <xpathInternals.h>, which
sure doesn't give me a warm feeling that we're supposed to call it
directly.  But maybe there's an approved way to get the result.

Or perhaps this test case is wrong, and instead of "node()" we
need to write something that specifies a sorted result?

			regards, tom lane

---

From: Michael Paquier <[email-masked]>
Date: Wed, 31 Aug 2022 15:09:16 +0900

> need to write something that specifies a sorted result?

I am no specialist on this matter..  Still, looking around, this looks
to be the kind of job that xsl:sort would do?  We cannot use it here
for an XML-only context, though, and it does not seem like there is a
direct way to enforce the ordering per the nature of the XPath
language, either.  I may be missing something of course.

Please note that I am also getting a bit the cold feet with the idea
of enforcing a sorting of the elements in the generated output,
actually, as this could have a performance penalty for all users
particularly on large blobs of data.

At the end, I'd like to think that it would be wiser to just remove
entirely the test for node() or reduce the contents of the string to
be able to strictly control the output order (say a simple
'<e>pre<![CDATA[&ent1]]>post</e>' would do the trick).  I cannot think
about a better idea now.  Note that removing the test case where we
have node() has no impact on the coverage of xml.c.
--
Michael

---

From: Tom Lane <[email-masked]>
Date: Wed, 31 Aug 2022 13:33:53 -0400

> have node() has no impact on the coverage of xml.c.

Yeah, I confirm that: local code-coverage testing shows no change
in the number of lines reached in xml.c when I remove that column:

-SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS x xml PATH 'node()', y xml PATH '/');
+SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS y xml PATH '/');

Dropping the query altogether does result in a reduction in the
number of lines hit.

I did not check the other XMLTABLE infrastructure, so what probably
is best to do is keep the two output columns but change 'node()'
to something with a more stable result; any preferences?

			regards, tom lane