should we add a XLogRecPtr/LSN SQL type?

Started by Andres Freundabout 12 years ago44 messages
#1Andres Freund
andres@2ndquadrant.com

Hi,

There's already a couple of SQL function dealing with XLogRecPtrs and
the logical replication work will add a couple of more. Currently each
of those funtions taking/returning an LSN does sprintf/scanf to
print/parse the strings. Which both is awkward and potentially
noticeable performancewise.

It seems relatively simple to add a proper type, with implicit casts
from text, instead?

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#2Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#1)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Dec 11, 2013 at 7:41 AM, Andres Freund <andres@2ndquadrant.com> wrote:

There's already a couple of SQL function dealing with XLogRecPtrs and
the logical replication work will add a couple of more. Currently each
of those funtions taking/returning an LSN does sprintf/scanf to
print/parse the strings. Which both is awkward and potentially
noticeable performancewise.

It seems relatively simple to add a proper type, with implicit casts
from text, instead?

I'm pretty sure that this was discussed last year, and I voted for it
-- except for the implicit casts part, perhaps -- but more people
voted against it, so it died. I still think that was a mistake, but I
just work here.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Euler Taveira
euler@timbira.com.br
In reply to: Andres Freund (#1)
Re: should we add a XLogRecPtr/LSN SQL type?

On 11-12-2013 09:41, Andres Freund wrote:

There's already a couple of SQL function dealing with XLogRecPtrs and
the logical replication work will add a couple of more. Currently each
of those funtions taking/returning an LSN does sprintf/scanf to
print/parse the strings. Which both is awkward and potentially
noticeable performancewise.

While discussing pg_xlog_location_diff function, Robert posted a lsn
datatype [1]/messages/by-id/CA+TgmoZRMNN0eVEsD-kxB9e-MvdmwoTi6guuJUvQP_8q2C5Cyg@mail.gmail.com. At that time we wouldn't go that far (a new datatype) to
cover only one function. If your proposal is just validation, I think
generic validation functions is the way to follow. However, if you are
thinking in adding operators, the lsn datatype should be implemented.

It seems relatively simple to add a proper type, with implicit casts
from text, instead?

Do you want to change the function signatures too?

[1]: /messages/by-id/CA+TgmoZRMNN0eVEsD-kxB9e-MvdmwoTi6guuJUvQP_8q2C5Cyg@mail.gmail.com
/messages/by-id/CA+TgmoZRMNN0eVEsD-kxB9e-MvdmwoTi6guuJUvQP_8q2C5Cyg@mail.gmail.com

--
Euler Taveira Timbira - http://www.timbira.com.br/
PostgreSQL: Consultoria, Desenvolvimento, Suporte 24x7 e Treinamento

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Greg Stark
stark@mit.edu
In reply to: Andres Freund (#1)
Re: should we add a XLogRecPtr/LSN SQL type?

Bonus points if you implement a (explicit) cast to and from timestamptz :)

--
greg
On 11 Dec 2013 12:41, "Andres Freund" <andres@2ndquadrant.com> wrote:

Show quoted text

Hi,

There's already a couple of SQL function dealing with XLogRecPtrs and
the logical replication work will add a couple of more. Currently each
of those funtions taking/returning an LSN does sprintf/scanf to
print/parse the strings. Which both is awkward and potentially
noticeable performancewise.

It seems relatively simple to add a proper type, with implicit casts
from text, instead?

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Andres Freund
andres@2ndquadrant.com
In reply to: Robert Haas (#2)
Re: should we add a XLogRecPtr/LSN SQL type?

On 2013-12-11 08:13:18 -0500, Robert Haas wrote:

On Wed, Dec 11, 2013 at 7:41 AM, Andres Freund <andres@2ndquadrant.com> wrote:

There's already a couple of SQL function dealing with XLogRecPtrs and
the logical replication work will add a couple of more. Currently each
of those funtions taking/returning an LSN does sprintf/scanf to
print/parse the strings. Which both is awkward and potentially
noticeable performancewise.

It seems relatively simple to add a proper type, with implicit casts
from text, instead?

I'm pretty sure that this was discussed last year, and I voted for it
but more people
voted against it, so it died. I still think that was a mistake, but I
just work here.

Ah. I missed or forgot that discussion. The primary argument seemed to
be that we were are only talking about a single function using it. I
don't think that really was true back then, but there definitely are
some more uses coming up. E.g. the changeset extraction SRF will return
the LSN of every extracted change...
I don't really buy the argument that it can wholl be replaced by
representing LSNs as numeric - those look significantly different enough
that that doesn't seem to make much sense to me. Also, they are a
pass-by-reference type...

-- except for the implicit casts part, perhaps --

I'd like to convert some existing functions to use the new type, and
without added casts that seems too likely to break existing usages. If
we just consistently use the new type everywhere, I can't see the usual
troubles with the added casts.

On 2013-12-11 10:13:51 -0300, Euler Taveira wrote:

On 11-12-2013 09:41, Andres Freund wrote:
While discussing pg_xlog_location_diff function, Robert posted a lsn
datatype [1]. At that time we wouldn't go that far (a new datatype) to
cover only one function. If your proposal is just validation, I think
generic validation functions is the way to follow. However, if you are
thinking in adding operators, the lsn datatype should be implemented.

I am mostly thinking of returning many such datums - representing them
as text is just pointless overhead. But even if it just were validation
- why sprinkle validation over dozens of places if we actually have a
typesystem to handle that kind of thing?

It seems relatively simple to add a proper type, with implicit casts
from text, instead?

Do you want to change the function signatures too?

Yes.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Simon Riggs
simon@2ndQuadrant.com
In reply to: Andres Freund (#5)
Re: should we add a XLogRecPtr/LSN SQL type?

On 12 December 2013 12:27, Andres Freund <andres@2ndquadrant.com> wrote:

On 2013-12-11 08:13:18 -0500, Robert Haas wrote:

On Wed, Dec 11, 2013 at 7:41 AM, Andres Freund <andres@2ndquadrant.com> wrote:

There's already a couple of SQL function dealing with XLogRecPtrs and
the logical replication work will add a couple of more. Currently each
of those funtions taking/returning an LSN does sprintf/scanf to
print/parse the strings. Which both is awkward and potentially
noticeable performancewise.

It seems relatively simple to add a proper type, with implicit casts
from text, instead?

I'm pretty sure that this was discussed last year, and I voted for it
but more people
voted against it, so it died. I still think that was a mistake, but I
just work here.

Ah. I missed or forgot that discussion.

Hmm, don't recall that. Just in case I opposed it, its a good idea now.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Robert Haas
robertmhaas@gmail.com
In reply to: Simon Riggs (#6)
Re: should we add a XLogRecPtr/LSN SQL type?

On Thu, Dec 12, 2013 at 8:20 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 12 December 2013 12:27, Andres Freund <andres@2ndquadrant.com> wrote:

On 2013-12-11 08:13:18 -0500, Robert Haas wrote:

On Wed, Dec 11, 2013 at 7:41 AM, Andres Freund <andres@2ndquadrant.com> wrote:

There's already a couple of SQL function dealing with XLogRecPtrs and
the logical replication work will add a couple of more. Currently each
of those funtions taking/returning an LSN does sprintf/scanf to
print/parse the strings. Which both is awkward and potentially
noticeable performancewise.

It seems relatively simple to add a proper type, with implicit casts
from text, instead?

I'm pretty sure that this was discussed last year, and I voted for it
but more people
voted against it, so it died. I still think that was a mistake, but I
just work here.

Ah. I missed or forgot that discussion.

Hmm, don't recall that. Just in case I opposed it, its a good idea now.

I am happy to have my old patch resurrected - could become a trend.
But someone should probably go back and check who objected and for
what reasons.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Robert Haas (#7)
Re: should we add a XLogRecPtr/LSN SQL type?

Robert Haas escribi�:

I am happy to have my old patch resurrected - could become a trend.
But someone should probably go back and check who objected and for
what reasons.

Here it is FWIW:

/messages/by-id/CA+TgmoZRMNN0eVEsD-kxB9e-MvdmwoTi6guuJUvQP_8q2C5Cyg@mail.gmail.com

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#8)
Re: should we add a XLogRecPtr/LSN SQL type?

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Robert Haas escribi�:

I am happy to have my old patch resurrected - could become a trend.
But someone should probably go back and check who objected and for
what reasons.

Here it is FWIW:
/messages/by-id/CA+TgmoZRMNN0eVEsD-kxB9e-MvdmwoTi6guuJUvQP_8q2C5Cyg@mail.gmail.com

AFAICS the objections were "why bother with a datatype for just one
function". If we've now got multiple use-cases, that loses its force.

I'm not, however, terribly thrilled with the suggestions to add implicit
casts associated with this type. Implicit casts are generally dangerous.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Andres Freund
andres@2ndquadrant.com
In reply to: Tom Lane (#9)
Re: should we add a XLogRecPtr/LSN SQL type?

Hi,

On 2013-12-12 11:55:51 -0500, Tom Lane wrote:

I'm not, however, terribly thrilled with the suggestions to add implicit
casts associated with this type. Implicit casts are generally dangerous.

It's a tradeof. Currently we have the following functions returning LSNs
as text:
* pg_current_xlog_location
* pg_current_xlog_insert_location
* pg_last_xlog_receive_location
* pg_last_xlog_replay_location
one view containing LSNs
* pg_stat_replication
and the following functions accepting LSNs as textual paramters:
* pg_xlog_location_diff
* pg_xlogfile_name

The question is how do we deal with backward compatibility when
introducing a LSN type? There might be some broken code around
monitoring if we simply replace the type without implicit casts. But
just leaving all those as-is seems quite unattractive.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#10)
Re: should we add a XLogRecPtr/LSN SQL type?

Andres Freund <andres@2ndquadrant.com> writes:

On 2013-12-12 11:55:51 -0500, Tom Lane wrote:

I'm not, however, terribly thrilled with the suggestions to add implicit
casts associated with this type. Implicit casts are generally dangerous.

It's a tradeof. Currently we have the following functions returning LSNs
as text:
* pg_current_xlog_location
* pg_current_xlog_insert_location
* pg_last_xlog_receive_location
* pg_last_xlog_replay_location
one view containing LSNs
* pg_stat_replication
and the following functions accepting LSNs as textual paramters:
* pg_xlog_location_diff
* pg_xlogfile_name

The question is how do we deal with backward compatibility when
introducing a LSN type? There might be some broken code around
monitoring if we simply replace the type without implicit casts.

Given the limited usage, how bad would it really be if we simply
made all those take/return the LSN type? As long as the type's
I/O representation looks like the old text format, I suspect
most queries wouldn't notice.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Michael Paquier
michael.paquier@gmail.com
In reply to: Tom Lane (#11)
Re: should we add a XLogRecPtr/LSN SQL type?

On Fri, Dec 13, 2013 at 2:47 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andres Freund <andres@2ndquadrant.com> writes:

On 2013-12-12 11:55:51 -0500, Tom Lane wrote:

I'm not, however, terribly thrilled with the suggestions to add implicit
casts associated with this type. Implicit casts are generally dangerous.

It's a tradeof. Currently we have the following functions returning LSNs
as text:
* pg_current_xlog_location
* pg_current_xlog_insert_location
* pg_last_xlog_receive_location
* pg_last_xlog_replay_location
one view containing LSNs
* pg_stat_replication
and the following functions accepting LSNs as textual paramters:
* pg_xlog_location_diff
* pg_xlogfile_name

The question is how do we deal with backward compatibility when
introducing a LSN type? There might be some broken code around
monitoring if we simply replace the type without implicit casts.

Given the limited usage, how bad would it really be if we simply
made all those take/return the LSN type? As long as the type's
I/O representation looks like the old text format, I suspect
most queries wouldn't notice.

Are there some plans to awaken this patch (including changing the
output of the functions of xlogfuncs.c)? This would be useful for the
differential backup features I am looking at these days. I imagine
that it is too late for 9.4 though...
Regards,
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Andres Freund
andres@2ndquadrant.com
In reply to: Michael Paquier (#12)
Re: should we add a XLogRecPtr/LSN SQL type?

On 2014-02-02 00:04:41 +0900, Michael Paquier wrote:

On Fri, Dec 13, 2013 at 2:47 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andres Freund <andres@2ndquadrant.com> writes:

On 2013-12-12 11:55:51 -0500, Tom Lane wrote:

I'm not, however, terribly thrilled with the suggestions to add implicit
casts associated with this type. Implicit casts are generally dangerous.

It's a tradeof. Currently we have the following functions returning LSNs
as text:
* pg_current_xlog_location
* pg_current_xlog_insert_location
* pg_last_xlog_receive_location
* pg_last_xlog_replay_location
one view containing LSNs
* pg_stat_replication
and the following functions accepting LSNs as textual paramters:
* pg_xlog_location_diff
* pg_xlogfile_name

The question is how do we deal with backward compatibility when
introducing a LSN type? There might be some broken code around
monitoring if we simply replace the type without implicit casts.

Given the limited usage, how bad would it really be if we simply
made all those take/return the LSN type? As long as the type's
I/O representation looks like the old text format, I suspect
most queries wouldn't notice.

I've asked around inside 2ndq and we could find one single problematic
query, so it's really not too bad.

Are there some plans to awaken this patch (including changing the
output of the functions of xlogfuncs.c)? This would be useful for the
differential backup features I am looking at these days. I imagine
that it is too late for 9.4 though...

I think we should definitely go ahead with the patch per-se, we just
added another system view with lsns in it... I don't have a too strong
opinion whether to do it in 9.4 or 9.5. It seems fairly low impact to
me, and it's an old patch, but I personally don't have the tuits to
refresh the patch right now.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Michael Paquier
michael.paquier@gmail.com
In reply to: Andres Freund (#13)
1 attachment(s)
Re: should we add a XLogRecPtr/LSN SQL type?

On Sun, Feb 2, 2014 at 12:07 AM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-02 00:04:41 +0900, Michael Paquier wrote:

On Fri, Dec 13, 2013 at 2:47 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andres Freund <andres@2ndquadrant.com> writes:

On 2013-12-12 11:55:51 -0500, Tom Lane wrote:

I'm not, however, terribly thrilled with the suggestions to add implicit
casts associated with this type. Implicit casts are generally dangerous.

It's a tradeof. Currently we have the following functions returning LSNs
as text:
* pg_current_xlog_location
* pg_current_xlog_insert_location
* pg_last_xlog_receive_location
* pg_last_xlog_replay_location
one view containing LSNs
* pg_stat_replication
and the following functions accepting LSNs as textual paramters:
* pg_xlog_location_diff
* pg_xlogfile_name

The question is how do we deal with backward compatibility when
introducing a LSN type? There might be some broken code around
monitoring if we simply replace the type without implicit casts.

Given the limited usage, how bad would it really be if we simply
made all those take/return the LSN type? As long as the type's
I/O representation looks like the old text format, I suspect
most queries wouldn't notice.

I've asked around inside 2ndq and we could find one single problematic
query, so it's really not too bad.

Are there some plans to awaken this patch (including changing the
output of the functions of xlogfuncs.c)? This would be useful for the
differential backup features I am looking at these days. I imagine
that it is too late for 9.4 though...

I think we should definitely go ahead with the patch per-se, we just
added another system view with lsns in it... I don't have a too strong
opinion whether to do it in 9.4 or 9.5. It seems fairly low impact to
me, and it's an old patch, but I personally don't have the tuits to
refresh the patch right now.

Please find attached a patch implementing lsn as a datatype, based on
the one Robert wrote a couple of years ago. Since XLogRecPtr has been
changed to a simple uint64, this *refresh* has needed some work. In
order to have this data type plugged in more natively with other xlog
system functions, I have added as well PG_RETURN_LSN and PG_GETARG_LSN
to facilitate the interface, making easier code management if
XLogRecPtr or LSN format are changed in the future.

Patch contains regression tests as well as a bit of documentation.
Perhaps this is too late for 9.4, so if there are no objections I'll
simply add this patch to the next commit fest in June for 9.5. Having
the system functions use this data type (pg_start_backup, pg_xlog_*,
create_rep_slot, etc.) does not look to be that difficult as all the
functions in xlogfuncs.c actually use XLogRecPtr before changing it to
text, so it would actually simplify the code. I think I'll simply code
it as I just looking at it now...
Regards,
--
Michael

Attachments:

20140204_lsn_datatype.patchtext/x-patch; charset=US-ASCII; name=20140204_lsn_datatype.patchDownload
*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
***************
*** 155,160 ****
--- 155,166 ----
        </row>
  
        <row>
+        <entry><type>lsn</type></entry>
+        <entry></entry>
+        <entry>Log Sequence Number</entry>
+       </row>
+ 
+       <row>
         <entry><type>macaddr</type></entry>
         <entry></entry>
         <entry>MAC (Media Access Control) address</entry>
***************
*** 4502,4507 **** SELECT * FROM pg_attribute
--- 4508,4527 ----
     </para>
    </sect1>
  
+   <sect1 id="datatype-lsn">
+    <title><acronym>LSN</> Type</title>
+ 
+    <indexterm zone="datatype-lsn">
+     <primary>LSN</primary>
+    </indexterm>
+ 
+    <para>
+     The <type>lsn</type> data type can be used to store LSN (Log Sequence
+     Number) data which is a pointer to a location in the XLOG. This type is a
+     representation of XLogRecPtr.
+    </para>
+   </sect1>
+ 
    <sect1 id="datatype-pseudo">
     <title>Pseudo-Types</title>
  
*** a/src/backend/utils/adt/Makefile
--- b/src/backend/utils/adt/Makefile
***************
*** 20,26 **** OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
  	cash.o char.o date.o datetime.o datum.o domains.o \
  	enum.o float.o format_type.o \
  	geo_ops.o geo_selfuncs.o int.o int8.o json.o jsonfuncs.o like.o \
! 	lockfuncs.o misc.o nabstime.o name.o numeric.o numutils.o \
  	oid.o oracle_compat.o orderedsetaggs.o \
  	pseudotypes.o rangetypes.o rangetypes_gist.o \
  	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
--- 20,26 ----
  	cash.o char.o date.o datetime.o datum.o domains.o \
  	enum.o float.o format_type.o \
  	geo_ops.o geo_selfuncs.o int.o int8.o json.o jsonfuncs.o like.o \
! 	lockfuncs.o lsn.o misc.o nabstime.o name.o numeric.o numutils.o \
  	oid.o oracle_compat.o orderedsetaggs.o \
  	pseudotypes.o rangetypes.o rangetypes_gist.o \
  	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
*** /dev/null
--- b/src/backend/utils/adt/lsn.c
***************
*** 0 ****
--- 1,180 ----
+ /*-------------------------------------------------------------------------
+  *
+  * lsn.c
+  *	  Internal LSN operations
+  *
+  * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/adt/lsn.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "funcapi.h"
+ #include "libpq/pqformat.h"
+ #include "utils/builtins.h"
+ #include "utils/lsn.h"
+ 
+ #define MAXLSNLEN		17
+ #define MAXLSNCOMPONENT	8
+ 
+ /*----------------------------------------------------------
+  * Formatting and conversion routines.
+  *---------------------------------------------------------*/
+ 
+ Datum
+ lsn_in(PG_FUNCTION_ARGS)
+ {
+ 	char	   *str = PG_GETARG_CSTRING(0);
+ 	int			len1, len2;
+ 	uint32		id, off;
+ 	XLogRecPtr	result;
+ 
+ 	/* Sanity check input format. */
+ 	len1 = strspn(str, "0123456789abcdefABCDEF");
+ 	if (len1 < 1 || len1 > MAXLSNCOMPONENT || str[len1] != '/')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ 				 errmsg("invalid input syntax for lsn: \"%s\"", str)));
+ 	len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
+ 	if (len2 < 1 || len2 > MAXLSNCOMPONENT || str[len1 + 1 + len2] != '\0')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ 				 errmsg("invalid input syntax for lsn: \"%s\"", str)));
+ 
+ 	/* Decode result. */
+ 	id = (uint32) strtoul(str, NULL, 16);
+ 	off = (uint32) strtoul(str + len1 + 1, NULL, 16);
+ 	result = (XLogRecPtr) ((uint64) id << 32) | off;
+ 
+ 	PG_RETURN_LSN(result);
+ }
+ 
+ Datum
+ lsn_out(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr	lsn = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	char		buf[MAXLSNLEN + 1];
+ 	char	   *result;
+ 	uint32		id, off;
+ 
+ 	/* Decode ID and offset */
+ 	id = (uint32) (lsn >> 32);
+ 	off = (uint32) lsn;
+ 
+ 	sprintf(buf, "%X/%X", id, off);
+ 	result = pstrdup(buf);
+ 	PG_RETURN_CSTRING(result);
+ }
+ 
+ Datum
+ lsn_recv(PG_FUNCTION_ARGS)
+ {
+ 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+ 	XLogRecPtr	result;
+ 
+ 	result = pq_getmsgint64(buf);
+ 	PG_RETURN_LSN(result);
+ }
+ 
+ Datum
+ lsn_send(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	StringInfoData buf;
+ 
+ 	pq_begintypsend(&buf);
+ 	pq_sendint64(&buf, lsn);
+ 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+ }
+ 
+ 
+ /*----------------------------------------------------------
+  *	Relational operators for LSNs
+  *---------------------------------------------------------*/
+ 
+ Datum
+ lsn_eq(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 == lsn2);
+ }
+ 
+ Datum
+ lsn_ne(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 != lsn2);
+ }
+ 
+ Datum
+ lsn_lt(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 < lsn2);
+ }
+ 
+ Datum
+ lsn_gt(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 > lsn2);
+ }
+ 
+ Datum
+ lsn_le(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 <= lsn2);
+ }
+ 
+ Datum
+ lsn_ge(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 >= lsn2);
+ }
+ 
+ 
+ /*----------------------------------------------------------
+  *	Arithmetic operators on LSNs.
+  *---------------------------------------------------------*/
+ 
+ Datum
+ lsn_mi(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 	char		buf[256];
+ 	Datum		result;
+ 
+ 	/* Negative results are not allowed. */
+ 	if (lsn1 < lsn2)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ 				 errmsg("LSN out of range")));
+ 
+ 	/* Convert to numeric. */
+ 	sprintf(buf, UINT64_FORMAT, lsn1 - lsn2);
+ 	result = DirectFunctionCall3(numeric_in,
+ 								 CStringGetDatum(buf),
+ 								 ObjectIdGetDatum(0),
+ 								 Int32GetDatum(-1));
+ 
+ 	return result;
+ }
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
***************
*** 1592,1597 **** DESCR("less than or equal");
--- 1592,1613 ----
  DATA(insert OID = 2977 (  ">="	   PGNSP PGUID b f f 2950 2950 16 2976 2974 uuid_ge scalargtsel scalargtjoinsel ));
  DESCR("greater than or equal");
  
+ /* lsn operators */
+ DATA(insert OID = 3222 (  "="	   PGNSP PGUID b f f 3220 3220 16 3222 3223 lsn_eq eqsel eqjoinsel ));
+ DESCR("equal");
+ DATA(insert OID = 3223 (  "<>"	   PGNSP PGUID b f f 3220 3220 16 3223 3222 lsn_ne neqsel neqjoinsel ));
+ DESCR("not equal");
+ DATA(insert OID = 3224 (  "<"	   PGNSP PGUID b f f 3220 3220 16 3225 3227 lsn_lt scalarltsel scalarltjoinsel ));
+ DESCR("less than");
+ DATA(insert OID = 3225 (  ">"	   PGNSP PGUID b f f 3220 3220 16 3224 3226 lsn_gt scalargtsel scalargtjoinsel ));
+ DESCR("greater than");
+ DATA(insert OID = 3226 (  "<="	   PGNSP PGUID b f f 3220 3220 16 3227 3225 lsn_le scalarltsel scalarltjoinsel ));
+ DESCR("less than or equal");
+ DATA(insert OID = 3227 (  ">="	   PGNSP PGUID b f f 3220 3220 16 3226 3224 lsn_ge scalargtsel scalargtjoinsel ));
+ DESCR("greater than or equal");
+ DATA(insert OID = 3228 (  "-"	   PGNSP PGUID b f f 3220 3220 1700    0	0 lsn_mi - - ));
+ DESCR("minus");
+ 
  /* enum operators */
  DATA(insert OID = 3516 (  "="	   PGNSP PGUID b t t 3500 3500 16 3516 3517 enum_eq eqsel eqjoinsel ));
  DESCR("equal");
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4212,4217 **** DESCR("I/O");
--- 4212,4234 ----
  DATA(insert OID = 2963 (  uuid_hash		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "2950" _null_ _null_ _null_ _null_ uuid_hash _null_ _null_ _null_ ));
  DESCR("hash");
  
+ /* lsn */
+ DATA(insert OID = 3229 (  lsn_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3220 "2275" _null_ _null_ _null_ _null_ lsn_in _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3230 (  lsn_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3220" _null_ _null_ _null_ _null_ lsn_out _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3231 (  lsn_lt		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_lt _null_ _null_ _null_ ));
+ DATA(insert OID = 3232 (  lsn_le		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_le _null_ _null_ _null_ ));
+ DATA(insert OID = 3233 (  lsn_eq		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_eq _null_ _null_ _null_ ));
+ DATA(insert OID = 3234 (  lsn_ge		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_ge _null_ _null_ _null_ ));
+ DATA(insert OID = 3235 (  lsn_gt		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_gt _null_ _null_ _null_ ));
+ DATA(insert OID = 3236 (  lsn_ne		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_ne _null_ _null_ _null_ ));
+ DATA(insert OID = 3237 (  lsn_mi		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "3220 3220" _null_ _null_ _null_ _null_ lsn_mi _null_ _null_ _null_ ));
+ DATA(insert OID = 3238 (  lsn_recv		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3220 "2281" _null_ _null_ _null_ _null_ lsn_recv _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3239 (  lsn_send		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3220" _null_ _null_ _null_ _null_ lsn_send _null_ _null_ _null_ ));
+ DESCR("I/O");
+ 
  /* enum related procs */
  DATA(insert OID = 3504 (  anyenum_in	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ ));
  DESCR("I/O");
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 577,582 **** DESCR("UUID datatype");
--- 577,587 ----
  #define UUIDOID 2950
  DATA(insert OID = 2951 ( _uuid			PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
  
+ /* lsn */
+ DATA(insert OID = 3220 ( lsn			PGNSP PGUID 8 t b U t t \054 0 0 3221 lsn_in lsn_out lsn_recv lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+ DESCR("LSN datatype");
+ DATA(insert OID = 3221 ( _lsn			PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+ 
  /* text search */
  DATA(insert OID = 3614 ( tsvector		PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
  DESCR("text representation for text search");
*** a/src/include/fmgr.h
--- b/src/include/fmgr.h
***************
*** 230,235 **** extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
--- 230,236 ----
  #define PG_GETARG_CHAR(n)	 DatumGetChar(PG_GETARG_DATUM(n))
  #define PG_GETARG_BOOL(n)	 DatumGetBool(PG_GETARG_DATUM(n))
  #define PG_GETARG_OID(n)	 DatumGetObjectId(PG_GETARG_DATUM(n))
+ #define PG_GETARG_LSN(n)	 DatumGetLogSeqNum(PG_GETARG_DATUM(n))
  #define PG_GETARG_POINTER(n) DatumGetPointer(PG_GETARG_DATUM(n))
  #define PG_GETARG_CSTRING(n) DatumGetCString(PG_GETARG_DATUM(n))
  #define PG_GETARG_NAME(n)	 DatumGetName(PG_GETARG_DATUM(n))
***************
*** 302,307 **** extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
--- 303,309 ----
  #define PG_RETURN_CHAR(x)	 return CharGetDatum(x)
  #define PG_RETURN_BOOL(x)	 return BoolGetDatum(x)
  #define PG_RETURN_OID(x)	 return ObjectIdGetDatum(x)
+ #define PG_RETURN_LSN(x)	 return LogSeqNumGetDatum(x)
  #define PG_RETURN_POINTER(x) return PointerGetDatum(x)
  #define PG_RETURN_CSTRING(x) return CStringGetDatum(x)
  #define PG_RETURN_NAME(x)	 return NameGetDatum(x)
*** a/src/include/postgres.h
--- b/src/include/postgres.h
***************
*** 484,489 **** typedef Datum *DatumPtr;
--- 484,503 ----
  #define ObjectIdGetDatum(X) ((Datum) SET_4_BYTES(X))
  
  /*
+  * DatumGetLogSeqNum
+  *		Returns log sequence number of a datum.
+  */
+ 
+ #define DatumGetLogSeqNum(X) ((XLogRecPtr) GET_8_BYTES(X))
+ 
+ /*
+  * LogSeqNumGetDatum
+  *		Returns datum representation for a log sequence number.
+  */
+ 
+ #define LogSeqNumGetDatum(X) ((Datum) SET_8_BYTES(X))
+ 
+ /*
   * DatumGetTransactionId
   *		Returns transaction identifier value of a datum.
   */
*** /dev/null
--- b/src/include/utils/lsn.h
***************
*** 0 ****
--- 1,33 ----
+ /*-------------------------------------------------------------------------
+  *
+  * lsn.h
+  *		Declarations for operations on log sequence numbers (LSNs).
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/utils/lsn.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef LSN_H
+ #define LSN_H
+ 
+ #include "fmgr.h"
+ 
+ extern Datum lsn_in(PG_FUNCTION_ARGS);
+ extern Datum lsn_out(PG_FUNCTION_ARGS);
+ extern Datum lsn_recv(PG_FUNCTION_ARGS);
+ extern Datum lsn_send(PG_FUNCTION_ARGS);
+ 
+ extern Datum lsn_eq(PG_FUNCTION_ARGS);
+ extern Datum lsn_ne(PG_FUNCTION_ARGS);
+ extern Datum lsn_lt(PG_FUNCTION_ARGS);
+ extern Datum lsn_gt(PG_FUNCTION_ARGS);
+ extern Datum lsn_le(PG_FUNCTION_ARGS);
+ extern Datum lsn_ge(PG_FUNCTION_ARGS);
+ 
+ extern Datum lsn_mi(PG_FUNCTION_ARGS);
+ 
+ #endif   /* LSN_H */
*** /dev/null
--- b/src/test/regress/expected/lsn.out
***************
*** 0 ****
--- 1,62 ----
+ --
+ -- LSN
+ --
+ CREATE TABLE LSN_TBL (f1 lsn);
+ -- Largest and smallest input
+ INSERT INTO LSN_TBL VALUES ('0/0');
+ INSERT INTO LSN_TBL VALUES ('FFFFFFFF/FFFFFFFF');
+ -- Incorrect input
+ INSERT INTO LSN_TBL VALUES ('G/0');
+ ERROR:  invalid input syntax for lsn: "G/0"
+ LINE 1: INSERT INTO LSN_TBL VALUES ('G/0');
+                                     ^
+ INSERT INTO LSN_TBL VALUES ('-1/0');
+ ERROR:  invalid input syntax for lsn: "-1/0"
+ LINE 1: INSERT INTO LSN_TBL VALUES ('-1/0');
+                                     ^
+ INSERT INTO LSN_TBL VALUES (' 0/12345678');
+ ERROR:  invalid input syntax for lsn: " 0/12345678"
+ LINE 1: INSERT INTO LSN_TBL VALUES (' 0/12345678');
+                                     ^
+ INSERT INTO LSN_TBL VALUES ('ABCD/');
+ ERROR:  invalid input syntax for lsn: "ABCD/"
+ LINE 1: INSERT INTO LSN_TBL VALUES ('ABCD/');
+                                     ^
+ INSERT INTO LSN_TBL VALUES ('/ABCD');
+ ERROR:  invalid input syntax for lsn: "/ABCD"
+ LINE 1: INSERT INTO LSN_TBL VALUES ('/ABCD');
+                                     ^
+ DROP TABLE LSN_TBL;
+ -- Operators
+ SELECT lsn_eq('0/16AE7F8', '0/16AE7F8'); -- true
+  lsn_eq 
+ --------
+  t
+ (1 row)
+ 
+ SELECT lsn_ne('0/16AE7F8', '0/16AE7F7'); -- true
+  lsn_ne 
+ --------
+  t
+ (1 row)
+ 
+ SELECT lsn_lt('0/16AE7F7', '0/16AE7F8'); -- true
+  lsn_lt 
+ --------
+  t
+ (1 row)
+ 
+ SELECT lsn_gt('0/16AE7F8', '0/16AE7F7'); -- true
+  lsn_gt 
+ --------
+  t
+ (1 row)
+ 
+ SELECT lsn_mi('0/16AE7F7', '0/16AE7F8'); -- No negative results
+ ERROR:  LSN out of range
+ SELECT lsn_mi('0/16AE7F8', '0/16AE7F7'); -- correct
+  lsn_mi 
+ --------
+       1
+ (1 row)
+ 
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 13,19 **** test: tablespace
  # ----------
  # The first group of parallel tests
  # ----------
! test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes
  
  # Depends on things setup during char, varchar and text
  test: strings
--- 13,19 ----
  # ----------
  # The first group of parallel tests
  # ----------
! test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes lsn
  
  # Depends on things setup during char, varchar and text
  test: strings
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 19,24 **** test: uuid
--- 19,25 ----
  test: enum
  test: money
  test: rangetypes
+ test: lsn
  test: strings
  test: numerology
  test: point
*** /dev/null
--- b/src/test/regress/sql/lsn.sql
***************
*** 0 ****
--- 1,25 ----
+ --
+ -- LSN
+ --
+ 
+ CREATE TABLE LSN_TBL (f1 lsn);
+ 
+ -- Largest and smallest input
+ INSERT INTO LSN_TBL VALUES ('0/0');
+ INSERT INTO LSN_TBL VALUES ('FFFFFFFF/FFFFFFFF');
+ 
+ -- Incorrect input
+ INSERT INTO LSN_TBL VALUES ('G/0');
+ INSERT INTO LSN_TBL VALUES ('-1/0');
+ INSERT INTO LSN_TBL VALUES (' 0/12345678');
+ INSERT INTO LSN_TBL VALUES ('ABCD/');
+ INSERT INTO LSN_TBL VALUES ('/ABCD');
+ DROP TABLE LSN_TBL;
+ 
+ -- Operators
+ SELECT lsn_eq('0/16AE7F8', '0/16AE7F8'); -- true
+ SELECT lsn_ne('0/16AE7F8', '0/16AE7F7'); -- true
+ SELECT lsn_lt('0/16AE7F7', '0/16AE7F8'); -- true
+ SELECT lsn_gt('0/16AE7F8', '0/16AE7F7'); -- true
+ SELECT lsn_mi('0/16AE7F7', '0/16AE7F8'); -- No negative results
+ SELECT lsn_mi('0/16AE7F8', '0/16AE7F7'); -- correct
#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Michael Paquier (#14)
Re: should we add a XLogRecPtr/LSN SQL type?

Michael Paquier <michael.paquier@gmail.com> writes:

Please find attached a patch implementing lsn as a datatype, based on
the one Robert wrote a couple of years ago.

Patch contains regression tests as well as a bit of documentation.
Perhaps this is too late for 9.4, so if there are no objections I'll
simply add this patch to the next commit fest in June for 9.5.

I may have lost count, but aren't a bunch of the affected functions new
in 9.4? If so, there's a good argument to be made that we should get
this in now, rather than waiting and having an API change for those
functions in 9.5.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Michael Paquier
michael.paquier@gmail.com
In reply to: Tom Lane (#15)
2 attachment(s)
Re: should we add a XLogRecPtr/LSN SQL type?

On Tue, Feb 4, 2014 at 10:10 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Michael Paquier <michael.paquier@gmail.com> writes:

Please find attached a patch implementing lsn as a datatype, based on
the one Robert wrote a couple of years ago.

Patch contains regression tests as well as a bit of documentation.
Perhaps this is too late for 9.4, so if there are no objections I'll
simply add this patch to the next commit fest in June for 9.5.

I may have lost count, but aren't a bunch of the affected functions new
in 9.4? If so, there's a good argument to be made that we should get
this in now, rather than waiting and having an API change for those
functions in 9.5.

Cool... I have created a second patch that updates all the system
functions to use the new lsn datatype introduced in the 1st patch
(pg_start|stop_backup, replication_slot stuff, xlog diff things, etc.)
and it is attached. This cleans up quite a bit of code in xlogfuncs.c
because we do not need anymore the LSN <-> cstring transformations!
I am also attaching a v2 of the first patch, I noticed that lsn_in
introduced in the first patch was using some error messages not
consistent with the ones of validate_xlog_location:xlogfuncs.c. Note
as well that validate_xlog_location is removed in the 2nd patch where
all the system functions are swicthed to the new datatype.
Regards,
--
Michael

Attachments:

20140204_lsn_datatype_v2.patchtext/x-patch; charset=US-ASCII; name=20140204_lsn_datatype_v2.patchDownload
*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
***************
*** 155,160 ****
--- 155,166 ----
        </row>
  
        <row>
+        <entry><type>lsn</type></entry>
+        <entry></entry>
+        <entry>Log Sequence Number</entry>
+       </row>
+ 
+       <row>
         <entry><type>macaddr</type></entry>
         <entry></entry>
         <entry>MAC (Media Access Control) address</entry>
***************
*** 4502,4507 **** SELECT * FROM pg_attribute
--- 4508,4527 ----
     </para>
    </sect1>
  
+   <sect1 id="datatype-lsn">
+    <title><acronym>LSN</> Type</title>
+ 
+    <indexterm zone="datatype-lsn">
+     <primary>LSN</primary>
+    </indexterm>
+ 
+    <para>
+     The <type>lsn</type> data type can be used to store LSN (Log Sequence
+     Number) data which is a pointer to a location in the XLOG. This type is a
+     representation of XLogRecPtr.
+    </para>
+   </sect1>
+ 
    <sect1 id="datatype-pseudo">
     <title>Pseudo-Types</title>
  
*** a/src/backend/utils/adt/Makefile
--- b/src/backend/utils/adt/Makefile
***************
*** 20,26 **** OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
  	cash.o char.o date.o datetime.o datum.o domains.o \
  	enum.o float.o format_type.o \
  	geo_ops.o geo_selfuncs.o int.o int8.o json.o jsonfuncs.o like.o \
! 	lockfuncs.o misc.o nabstime.o name.o numeric.o numutils.o \
  	oid.o oracle_compat.o orderedsetaggs.o \
  	pseudotypes.o rangetypes.o rangetypes_gist.o \
  	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
--- 20,26 ----
  	cash.o char.o date.o datetime.o datum.o domains.o \
  	enum.o float.o format_type.o \
  	geo_ops.o geo_selfuncs.o int.o int8.o json.o jsonfuncs.o like.o \
! 	lockfuncs.o lsn.o misc.o nabstime.o name.o numeric.o numutils.o \
  	oid.o oracle_compat.o orderedsetaggs.o \
  	pseudotypes.o rangetypes.o rangetypes_gist.o \
  	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
*** /dev/null
--- b/src/backend/utils/adt/lsn.c
***************
*** 0 ****
--- 1,180 ----
+ /*-------------------------------------------------------------------------
+  *
+  * lsn.c
+  *	  Internal LSN operations
+  *
+  * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/adt/lsn.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "funcapi.h"
+ #include "libpq/pqformat.h"
+ #include "utils/builtins.h"
+ #include "utils/lsn.h"
+ 
+ #define MAXLSNLEN		17
+ #define MAXLSNCOMPONENT	8
+ 
+ /*----------------------------------------------------------
+  * Formatting and conversion routines.
+  *---------------------------------------------------------*/
+ 
+ Datum
+ lsn_in(PG_FUNCTION_ARGS)
+ {
+ 	char	   *str = PG_GETARG_CSTRING(0);
+ 	int			len1, len2;
+ 	uint32		id, off;
+ 	XLogRecPtr	result;
+ 
+ 	/* Sanity check input format. */
+ 	len1 = strspn(str, "0123456789abcdefABCDEF");
+ 	if (len1 < 1 || len1 > MAXLSNCOMPONENT || str[len1] != '/')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ 				 errmsg("invalid input syntax for transaction log location: \"%s\"", str)));
+ 	len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
+ 	if (len2 < 1 || len2 > MAXLSNCOMPONENT || str[len1 + 1 + len2] != '\0')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ 				 errmsg("invalid input syntax for transaction log location: \"%s\"", str)));
+ 
+ 	/* Decode result. */
+ 	id = (uint32) strtoul(str, NULL, 16);
+ 	off = (uint32) strtoul(str + len1 + 1, NULL, 16);
+ 	result = (XLogRecPtr) ((uint64) id << 32) | off;
+ 
+ 	PG_RETURN_LSN(result);
+ }
+ 
+ Datum
+ lsn_out(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr	lsn = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	char		buf[MAXLSNLEN + 1];
+ 	char	   *result;
+ 	uint32		id, off;
+ 
+ 	/* Decode ID and offset */
+ 	id = (uint32) (lsn >> 32);
+ 	off = (uint32) lsn;
+ 
+ 	sprintf(buf, "%X/%X", id, off);
+ 	result = pstrdup(buf);
+ 	PG_RETURN_CSTRING(result);
+ }
+ 
+ Datum
+ lsn_recv(PG_FUNCTION_ARGS)
+ {
+ 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+ 	XLogRecPtr	result;
+ 
+ 	result = pq_getmsgint64(buf);
+ 	PG_RETURN_LSN(result);
+ }
+ 
+ Datum
+ lsn_send(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	StringInfoData buf;
+ 
+ 	pq_begintypsend(&buf);
+ 	pq_sendint64(&buf, lsn);
+ 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+ }
+ 
+ 
+ /*----------------------------------------------------------
+  *	Relational operators for LSNs
+  *---------------------------------------------------------*/
+ 
+ Datum
+ lsn_eq(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 == lsn2);
+ }
+ 
+ Datum
+ lsn_ne(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 != lsn2);
+ }
+ 
+ Datum
+ lsn_lt(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 < lsn2);
+ }
+ 
+ Datum
+ lsn_gt(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 > lsn2);
+ }
+ 
+ Datum
+ lsn_le(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 <= lsn2);
+ }
+ 
+ Datum
+ lsn_ge(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 >= lsn2);
+ }
+ 
+ 
+ /*----------------------------------------------------------
+  *	Arithmetic operators on LSNs.
+  *---------------------------------------------------------*/
+ 
+ Datum
+ lsn_mi(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_LSN(1);
+ 	char		buf[256];
+ 	Datum		result;
+ 
+ 	/* Negative results are not allowed. */
+ 	if (lsn1 < lsn2)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ 				 errmsg("LSN out of range")));
+ 
+ 	/* Convert to numeric. */
+ 	sprintf(buf, UINT64_FORMAT, lsn1 - lsn2);
+ 	result = DirectFunctionCall3(numeric_in,
+ 								 CStringGetDatum(buf),
+ 								 ObjectIdGetDatum(0),
+ 								 Int32GetDatum(-1));
+ 
+ 	return result;
+ }
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
***************
*** 1592,1597 **** DESCR("less than or equal");
--- 1592,1613 ----
  DATA(insert OID = 2977 (  ">="	   PGNSP PGUID b f f 2950 2950 16 2976 2974 uuid_ge scalargtsel scalargtjoinsel ));
  DESCR("greater than or equal");
  
+ /* lsn operators */
+ DATA(insert OID = 3222 (  "="	   PGNSP PGUID b f f 3220 3220 16 3222 3223 lsn_eq eqsel eqjoinsel ));
+ DESCR("equal");
+ DATA(insert OID = 3223 (  "<>"	   PGNSP PGUID b f f 3220 3220 16 3223 3222 lsn_ne neqsel neqjoinsel ));
+ DESCR("not equal");
+ DATA(insert OID = 3224 (  "<"	   PGNSP PGUID b f f 3220 3220 16 3225 3227 lsn_lt scalarltsel scalarltjoinsel ));
+ DESCR("less than");
+ DATA(insert OID = 3225 (  ">"	   PGNSP PGUID b f f 3220 3220 16 3224 3226 lsn_gt scalargtsel scalargtjoinsel ));
+ DESCR("greater than");
+ DATA(insert OID = 3226 (  "<="	   PGNSP PGUID b f f 3220 3220 16 3227 3225 lsn_le scalarltsel scalarltjoinsel ));
+ DESCR("less than or equal");
+ DATA(insert OID = 3227 (  ">="	   PGNSP PGUID b f f 3220 3220 16 3226 3224 lsn_ge scalargtsel scalargtjoinsel ));
+ DESCR("greater than or equal");
+ DATA(insert OID = 3228 (  "-"	   PGNSP PGUID b f f 3220 3220 1700    0	0 lsn_mi - - ));
+ DESCR("minus");
+ 
  /* enum operators */
  DATA(insert OID = 3516 (  "="	   PGNSP PGUID b t t 3500 3500 16 3516 3517 enum_eq eqsel eqjoinsel ));
  DESCR("equal");
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4212,4217 **** DESCR("I/O");
--- 4212,4234 ----
  DATA(insert OID = 2963 (  uuid_hash		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "2950" _null_ _null_ _null_ _null_ uuid_hash _null_ _null_ _null_ ));
  DESCR("hash");
  
+ /* lsn */
+ DATA(insert OID = 3229 (  lsn_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3220 "2275" _null_ _null_ _null_ _null_ lsn_in _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3230 (  lsn_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3220" _null_ _null_ _null_ _null_ lsn_out _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3231 (  lsn_lt		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_lt _null_ _null_ _null_ ));
+ DATA(insert OID = 3232 (  lsn_le		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_le _null_ _null_ _null_ ));
+ DATA(insert OID = 3233 (  lsn_eq		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_eq _null_ _null_ _null_ ));
+ DATA(insert OID = 3234 (  lsn_ge		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_ge _null_ _null_ _null_ ));
+ DATA(insert OID = 3235 (  lsn_gt		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_gt _null_ _null_ _null_ ));
+ DATA(insert OID = 3236 (  lsn_ne		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ lsn_ne _null_ _null_ _null_ ));
+ DATA(insert OID = 3237 (  lsn_mi		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "3220 3220" _null_ _null_ _null_ _null_ lsn_mi _null_ _null_ _null_ ));
+ DATA(insert OID = 3238 (  lsn_recv		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3220 "2281" _null_ _null_ _null_ _null_ lsn_recv _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3239 (  lsn_send		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3220" _null_ _null_ _null_ _null_ lsn_send _null_ _null_ _null_ ));
+ DESCR("I/O");
+ 
  /* enum related procs */
  DATA(insert OID = 3504 (  anyenum_in	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ ));
  DESCR("I/O");
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 577,582 **** DESCR("UUID datatype");
--- 577,587 ----
  #define UUIDOID 2950
  DATA(insert OID = 2951 ( _uuid			PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
  
+ /* lsn */
+ DATA(insert OID = 3220 ( lsn			PGNSP PGUID 8 t b U t t \054 0 0 3221 lsn_in lsn_out lsn_recv lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+ DESCR("LSN datatype");
+ DATA(insert OID = 3221 ( _lsn			PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+ 
  /* text search */
  DATA(insert OID = 3614 ( tsvector		PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
  DESCR("text representation for text search");
*** a/src/include/fmgr.h
--- b/src/include/fmgr.h
***************
*** 230,235 **** extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
--- 230,236 ----
  #define PG_GETARG_CHAR(n)	 DatumGetChar(PG_GETARG_DATUM(n))
  #define PG_GETARG_BOOL(n)	 DatumGetBool(PG_GETARG_DATUM(n))
  #define PG_GETARG_OID(n)	 DatumGetObjectId(PG_GETARG_DATUM(n))
+ #define PG_GETARG_LSN(n)	 DatumGetLogSeqNum(PG_GETARG_DATUM(n))
  #define PG_GETARG_POINTER(n) DatumGetPointer(PG_GETARG_DATUM(n))
  #define PG_GETARG_CSTRING(n) DatumGetCString(PG_GETARG_DATUM(n))
  #define PG_GETARG_NAME(n)	 DatumGetName(PG_GETARG_DATUM(n))
***************
*** 302,307 **** extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
--- 303,309 ----
  #define PG_RETURN_CHAR(x)	 return CharGetDatum(x)
  #define PG_RETURN_BOOL(x)	 return BoolGetDatum(x)
  #define PG_RETURN_OID(x)	 return ObjectIdGetDatum(x)
+ #define PG_RETURN_LSN(x)	 return LogSeqNumGetDatum(x)
  #define PG_RETURN_POINTER(x) return PointerGetDatum(x)
  #define PG_RETURN_CSTRING(x) return CStringGetDatum(x)
  #define PG_RETURN_NAME(x)	 return NameGetDatum(x)
*** a/src/include/postgres.h
--- b/src/include/postgres.h
***************
*** 484,489 **** typedef Datum *DatumPtr;
--- 484,503 ----
  #define ObjectIdGetDatum(X) ((Datum) SET_4_BYTES(X))
  
  /*
+  * DatumGetLogSeqNum
+  *		Returns log sequence number of a datum.
+  */
+ 
+ #define DatumGetLogSeqNum(X) ((XLogRecPtr) GET_8_BYTES(X))
+ 
+ /*
+  * LogSeqNumGetDatum
+  *		Returns datum representation for a log sequence number.
+  */
+ 
+ #define LogSeqNumGetDatum(X) ((Datum) SET_8_BYTES(X))
+ 
+ /*
   * DatumGetTransactionId
   *		Returns transaction identifier value of a datum.
   */
*** /dev/null
--- b/src/include/utils/lsn.h
***************
*** 0 ****
--- 1,33 ----
+ /*-------------------------------------------------------------------------
+  *
+  * lsn.h
+  *		Declarations for operations on log sequence numbers (LSNs).
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/utils/lsn.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef LSN_H
+ #define LSN_H
+ 
+ #include "fmgr.h"
+ 
+ extern Datum lsn_in(PG_FUNCTION_ARGS);
+ extern Datum lsn_out(PG_FUNCTION_ARGS);
+ extern Datum lsn_recv(PG_FUNCTION_ARGS);
+ extern Datum lsn_send(PG_FUNCTION_ARGS);
+ 
+ extern Datum lsn_eq(PG_FUNCTION_ARGS);
+ extern Datum lsn_ne(PG_FUNCTION_ARGS);
+ extern Datum lsn_lt(PG_FUNCTION_ARGS);
+ extern Datum lsn_gt(PG_FUNCTION_ARGS);
+ extern Datum lsn_le(PG_FUNCTION_ARGS);
+ extern Datum lsn_ge(PG_FUNCTION_ARGS);
+ 
+ extern Datum lsn_mi(PG_FUNCTION_ARGS);
+ 
+ #endif   /* LSN_H */
*** /dev/null
--- b/src/test/regress/expected/lsn.out
***************
*** 0 ****
--- 1,62 ----
+ --
+ -- LSN
+ --
+ CREATE TABLE LSN_TBL (f1 lsn);
+ -- Largest and smallest input
+ INSERT INTO LSN_TBL VALUES ('0/0');
+ INSERT INTO LSN_TBL VALUES ('FFFFFFFF/FFFFFFFF');
+ -- Incorrect input
+ INSERT INTO LSN_TBL VALUES ('G/0');
+ ERROR:  invalid input syntax for lsn: "G/0"
+ LINE 1: INSERT INTO LSN_TBL VALUES ('G/0');
+                                     ^
+ INSERT INTO LSN_TBL VALUES ('-1/0');
+ ERROR:  invalid input syntax for lsn: "-1/0"
+ LINE 1: INSERT INTO LSN_TBL VALUES ('-1/0');
+                                     ^
+ INSERT INTO LSN_TBL VALUES (' 0/12345678');
+ ERROR:  invalid input syntax for lsn: " 0/12345678"
+ LINE 1: INSERT INTO LSN_TBL VALUES (' 0/12345678');
+                                     ^
+ INSERT INTO LSN_TBL VALUES ('ABCD/');
+ ERROR:  invalid input syntax for lsn: "ABCD/"
+ LINE 1: INSERT INTO LSN_TBL VALUES ('ABCD/');
+                                     ^
+ INSERT INTO LSN_TBL VALUES ('/ABCD');
+ ERROR:  invalid input syntax for lsn: "/ABCD"
+ LINE 1: INSERT INTO LSN_TBL VALUES ('/ABCD');
+                                     ^
+ DROP TABLE LSN_TBL;
+ -- Operators
+ SELECT lsn_eq('0/16AE7F8', '0/16AE7F8'); -- true
+  lsn_eq 
+ --------
+  t
+ (1 row)
+ 
+ SELECT lsn_ne('0/16AE7F8', '0/16AE7F7'); -- true
+  lsn_ne 
+ --------
+  t
+ (1 row)
+ 
+ SELECT lsn_lt('0/16AE7F7', '0/16AE7F8'); -- true
+  lsn_lt 
+ --------
+  t
+ (1 row)
+ 
+ SELECT lsn_gt('0/16AE7F8', '0/16AE7F7'); -- true
+  lsn_gt 
+ --------
+  t
+ (1 row)
+ 
+ SELECT lsn_mi('0/16AE7F7', '0/16AE7F8'); -- No negative results
+ ERROR:  LSN out of range
+ SELECT lsn_mi('0/16AE7F8', '0/16AE7F7'); -- correct
+  lsn_mi 
+ --------
+       1
+ (1 row)
+ 
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 13,19 **** test: tablespace
  # ----------
  # The first group of parallel tests
  # ----------
! test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes
  
  # Depends on things setup during char, varchar and text
  test: strings
--- 13,19 ----
  # ----------
  # The first group of parallel tests
  # ----------
! test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes lsn
  
  # Depends on things setup during char, varchar and text
  test: strings
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 19,24 **** test: uuid
--- 19,25 ----
  test: enum
  test: money
  test: rangetypes
+ test: lsn
  test: strings
  test: numerology
  test: point
*** /dev/null
--- b/src/test/regress/sql/lsn.sql
***************
*** 0 ****
--- 1,25 ----
+ --
+ -- LSN
+ --
+ 
+ CREATE TABLE LSN_TBL (f1 lsn);
+ 
+ -- Largest and smallest input
+ INSERT INTO LSN_TBL VALUES ('0/0');
+ INSERT INTO LSN_TBL VALUES ('FFFFFFFF/FFFFFFFF');
+ 
+ -- Incorrect input
+ INSERT INTO LSN_TBL VALUES ('G/0');
+ INSERT INTO LSN_TBL VALUES ('-1/0');
+ INSERT INTO LSN_TBL VALUES (' 0/12345678');
+ INSERT INTO LSN_TBL VALUES ('ABCD/');
+ INSERT INTO LSN_TBL VALUES ('/ABCD');
+ DROP TABLE LSN_TBL;
+ 
+ -- Operators
+ SELECT lsn_eq('0/16AE7F8', '0/16AE7F8'); -- true
+ SELECT lsn_ne('0/16AE7F8', '0/16AE7F7'); -- true
+ SELECT lsn_lt('0/16AE7F7', '0/16AE7F8'); -- true
+ SELECT lsn_gt('0/16AE7F8', '0/16AE7F7'); -- true
+ SELECT lsn_mi('0/16AE7F7', '0/16AE7F8'); -- No negative results
+ SELECT lsn_mi('0/16AE7F8', '0/16AE7F7'); -- correct
20140204_lsn_func_cleanup.patchtext/x-patch; charset=US-ASCII; name=20140204_lsn_func_cleanup.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 15884,15918 **** SELECT set_config('log_statement_stats', 'off', false);
         <entry>
          <literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal>
          </entry>
!        <entry><type>text</type></entry>
         <entry>Create a named point for performing restore (restricted to superusers)</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_current_xlog_insert_location()</function></literal>
          </entry>
!        <entry><type>text</type></entry>
         <entry>Get current transaction log insert location</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_current_xlog_location()</function></literal>
          </entry>
!        <entry><type>text</type></entry>
         <entry>Get current transaction log write location</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
          </entry>
!        <entry><type>text</type></entry>
         <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_stop_backup()</function></literal>
          </entry>
!        <entry><type>text</type></entry>
         <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
        </row>
        <row>
--- 15884,15918 ----
         <entry>
          <literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal>
          </entry>
!        <entry><type>lsn</type></entry>
         <entry>Create a named point for performing restore (restricted to superusers)</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_current_xlog_insert_location()</function></literal>
          </entry>
!        <entry><type>lsn</type></entry>
         <entry>Get current transaction log insert location</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_current_xlog_location()</function></literal>
          </entry>
!        <entry><type>lsn</type></entry>
         <entry>Get current transaction log write location</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
          </entry>
!        <entry><type>lsn</type></entry>
         <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_stop_backup()</function></literal>
          </entry>
!        <entry><type>lsn</type></entry>
         <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
        </row>
        <row>
***************
*** 15933,15958 **** SELECT set_config('log_statement_stats', 'off', false);
         <entry>
          <literal><function>pg_switch_xlog()</function></literal>
          </entry>
!        <entry><type>text</type></entry>
         <entry>Force switch to a new transaction log file (restricted to superusers)</entry>
        </row>
        <row>
         <entry>
!         <literal><function>pg_xlogfile_name(<parameter>location</> <type>text</>)</function></literal>
          </entry>
         <entry><type>text</type></entry>
         <entry>Convert transaction log location string to file name</entry>
        </row>
        <row>
         <entry>
!         <literal><function>pg_xlogfile_name_offset(<parameter>location</> <type>text</>)</function></literal>
          </entry>
         <entry><type>text</>, <type>integer</></entry>
         <entry>Convert transaction log location string to file name and decimal byte offset within file</entry>
        </row>
        <row>
         <entry>
!         <literal><function>pg_xlog_location_diff(<parameter>location</> <type>text</>, <parameter>location</> <type>text</>)</function></literal>
         </entry>
         <entry><type>numeric</></entry>
         <entry>Calculate the difference between two transaction log locations</entry>
--- 15933,15958 ----
         <entry>
          <literal><function>pg_switch_xlog()</function></literal>
          </entry>
!        <entry><type>lsn</type></entry>
         <entry>Force switch to a new transaction log file (restricted to superusers)</entry>
        </row>
        <row>
         <entry>
!         <literal><function>pg_xlogfile_name(<parameter>location</> <type>lsn</>)</function></literal>
          </entry>
         <entry><type>text</type></entry>
         <entry>Convert transaction log location string to file name</entry>
        </row>
        <row>
         <entry>
!         <literal><function>pg_xlogfile_name_offset(<parameter>location</> <type>lsn</>)</function></literal>
          </entry>
         <entry><type>text</>, <type>integer</></entry>
         <entry>Convert transaction log location string to file name and decimal byte offset within file</entry>
        </row>
        <row>
         <entry>
!         <literal><function>pg_xlog_location_diff(<parameter>location</> <type>lsn</>, <parameter>location</> <type>lsn</>)</function></literal>
         </entry>
         <entry><type>numeric</></entry>
         <entry>Calculate the difference between two transaction log locations</entry>
***************
*** 16107,16113 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <entry>
          <literal><function>pg_last_xlog_receive_location()</function></literal>
          </entry>
!        <entry><type>text</type></entry>
         <entry>Get last transaction log location received and synced to disk by
          streaming replication. While streaming replication is in progress
          this will increase monotonically. If recovery has completed this will
--- 16107,16113 ----
         <entry>
          <literal><function>pg_last_xlog_receive_location()</function></literal>
          </entry>
!        <entry><type>lsn</type></entry>
         <entry>Get last transaction log location received and synced to disk by
          streaming replication. While streaming replication is in progress
          this will increase monotonically. If recovery has completed this will
***************
*** 16121,16127 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <entry>
          <literal><function>pg_last_xlog_replay_location()</function></literal>
          </entry>
!        <entry><type>text</type></entry>
         <entry>Get last transaction log location replayed during recovery.
          If recovery is still in progress this will increase monotonically.
          If recovery has completed then this value will remain static at
--- 16121,16127 ----
         <entry>
          <literal><function>pg_last_xlog_replay_location()</function></literal>
          </entry>
!        <entry><type>lsn</type></entry>
         <entry>Get last transaction log location replayed during recovery.
          If recovery is still in progress this will increase monotonically.
          If recovery has completed then this value will remain static at
***************
*** 16335,16341 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
          <literal><function>pg_create_physical_replication_slot(<parameter>slotname</parameter> <type>text</type>, <parameter>plugin</parameter> <type>text</type>)</function></literal>
         </entry>
         <entry>
!         (<parameter>slotname</parameter> <type>text</type>, <parameter>xlog_position</parameter> <type>text</type>)
         </entry>
         <entry>
          Creates a new physical replication slot named
--- 16335,16341 ----
          <literal><function>pg_create_physical_replication_slot(<parameter>slotname</parameter> <type>text</type>, <parameter>plugin</parameter> <type>text</type>)</function></literal>
         </entry>
         <entry>
!         (<parameter>slotname</parameter> <type>text</type>, <parameter>xlog_position</parameter> <type>lsn</type>)
         </entry>
         <entry>
          Creates a new physical replication slot named
*** a/src/backend/access/transam/xlogfuncs.c
--- b/src/backend/access/transam/xlogfuncs.c
***************
*** 33,40 ****
  #include "utils/timestamp.h"
  #include "storage/fd.h"
  
- static void validate_xlog_location(char *str);
- 
  
  /*
   * pg_start_backup: set up for taking an on-line backup dump
--- 33,38 ----
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 810,816 **** COMMENT ON FUNCTION ts_debug(text) IS
  
  CREATE OR REPLACE FUNCTION
    pg_start_backup(label text, fast boolean DEFAULT false)
!   RETURNS text STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup';
  
  CREATE OR REPLACE FUNCTION
    json_populate_record(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
--- 810,816 ----
  
  CREATE OR REPLACE FUNCTION
    pg_start_backup(label text, fast boolean DEFAULT false)
!   RETURNS lsn STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup';
  
  CREATE OR REPLACE FUNCTION
    json_populate_record(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
*** a/src/backend/replication/slotfuncs.c
--- b/src/backend/replication/slotfuncs.c
***************
*** 141,148 **** pg_get_replication_slots(PG_FUNCTION_ARGS)
  		bool		active;
  		Oid			database;
  		const char *slot_name;
- 
- 		char		restart_lsn_s[MAXFNAMELEN];
  		int			i;
  
  		SpinLockAcquire(&slot->mutex);
--- 141,146 ----
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2971,2998 **** DATA(insert OID = 2171 ( pg_cancel_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v
  DESCR("cancel a server process' current query");
  DATA(insert OID = 2096 ( pg_terminate_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 16 "23" _null_ _null_ _null_ _null_ pg_terminate_backend _null_ _null_ _null_ ));
  DESCR("terminate a server process");
! DATA(insert OID = 2172 ( pg_start_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 25 "25 16" _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ ));
  DESCR("prepare for taking an online backup");
! DATA(insert OID = 2173 ( pg_stop_backup			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ ));
  DESCR("finish taking an online backup");
  DATA(insert OID = 3813 ( pg_is_in_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_in_backup _null_ _null_ _null_ ));
  DESCR("true if server is in online backup");
  DATA(insert OID = 3814 ( pg_backup_start_time		PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 1184 "" _null_ _null_ _null_ _null_ pg_backup_start_time _null_ _null_ _null_ ));
  DESCR("start time of an online backup");
! DATA(insert OID = 2848 ( pg_switch_xlog			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_switch_xlog _null_ _null_ _null_ ));
  DESCR("switch to new xlog file");
! DATA(insert OID = 3098 ( pg_create_restore_point	PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_create_restore_point _null_ _null_ _null_ ));
  DESCR("create a named restore point");
! DATA(insert OID = 2849 ( pg_current_xlog_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_location _null_ _null_ _null_ ));
  DESCR("current xlog write location");
! DATA(insert OID = 2852 ( pg_current_xlog_insert_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_insert_location _null_ _null_ _null_ ));
  DESCR("current xlog insert location");
! DATA(insert OID = 2850 ( pg_xlogfile_name_offset	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2249 "25" "{25,25,23}" "{i,o,o}" "{wal_location,file_name,file_offset}" _null_ pg_xlogfile_name_offset _null_ _null_ _null_ ));
  DESCR("xlog filename and byte offset, given an xlog location");
! DATA(insert OID = 2851 ( pg_xlogfile_name			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "25" _null_ _null_ _null_ _null_ pg_xlogfile_name _null_ _null_ _null_ ));
  DESCR("xlog filename, given an xlog location");
  
! DATA(insert OID = 3165 ( pg_xlog_location_diff		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "25 25" _null_ _null_ _null_ _null_ pg_xlog_location_diff _null_ _null_ _null_ ));
  DESCR("difference in bytes, given two xlog locations");
  
  DATA(insert OID = 3809 ( pg_export_snapshot		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_export_snapshot _null_ _null_ _null_ ));
--- 2971,2998 ----
  DESCR("cancel a server process' current query");
  DATA(insert OID = 2096 ( pg_terminate_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 16 "23" _null_ _null_ _null_ _null_ pg_terminate_backend _null_ _null_ _null_ ));
  DESCR("terminate a server process");
! DATA(insert OID = 2172 ( pg_start_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 3220 "25 16" _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ ));
  DESCR("prepare for taking an online backup");
! DATA(insert OID = 2173 ( pg_stop_backup			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ ));
  DESCR("finish taking an online backup");
  DATA(insert OID = 3813 ( pg_is_in_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_in_backup _null_ _null_ _null_ ));
  DESCR("true if server is in online backup");
  DATA(insert OID = 3814 ( pg_backup_start_time		PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 1184 "" _null_ _null_ _null_ _null_ pg_backup_start_time _null_ _null_ _null_ ));
  DESCR("start time of an online backup");
! DATA(insert OID = 2848 ( pg_switch_xlog			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_switch_xlog _null_ _null_ _null_ ));
  DESCR("switch to new xlog file");
! DATA(insert OID = 3098 ( pg_create_restore_point	PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 3220 "25" _null_ _null_ _null_ _null_ pg_create_restore_point _null_ _null_ _null_ ));
  DESCR("create a named restore point");
! DATA(insert OID = 2849 ( pg_current_xlog_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_current_xlog_location _null_ _null_ _null_ ));
  DESCR("current xlog write location");
! DATA(insert OID = 2852 ( pg_current_xlog_insert_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_current_xlog_insert_location _null_ _null_ _null_ ));
  DESCR("current xlog insert location");
! DATA(insert OID = 2850 ( pg_xlogfile_name_offset	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2249 "3220" "{3220,25,23}" "{i,o,o}" "{wal_location,file_name,file_offset}" _null_ pg_xlogfile_name_offset _null_ _null_ _null_ ));
  DESCR("xlog filename and byte offset, given an xlog location");
! DATA(insert OID = 2851 ( pg_xlogfile_name			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3220" _null_ _null_ _null_ _null_ pg_xlogfile_name _null_ _null_ _null_ ));
  DESCR("xlog filename, given an xlog location");
  
! DATA(insert OID = 3165 ( pg_xlog_location_diff		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "3220 3220" _null_ _null_ _null_ _null_ pg_xlog_location_diff _null_ _null_ _null_ ));
  DESCR("difference in bytes, given two xlog locations");
  
  DATA(insert OID = 3809 ( pg_export_snapshot		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_export_snapshot _null_ _null_ _null_ ));
***************
*** 3001,3009 **** DESCR("export a snapshot");
  DATA(insert OID = 3810 (  pg_is_in_recovery		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_in_recovery _null_ _null_ _null_ ));
  DESCR("true if server is in recovery");
  
! DATA(insert OID = 3820 ( pg_last_xlog_receive_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_last_xlog_receive_location _null_ _null_ _null_ ));
  DESCR("current xlog flush location");
! DATA(insert OID = 3821 ( pg_last_xlog_replay_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_last_xlog_replay_location _null_ _null_ _null_ ));
  DESCR("last xlog replay location");
  DATA(insert OID = 3830 ( pg_last_xact_replay_timestamp	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 1184 "" _null_ _null_ _null_ _null_ pg_last_xact_replay_timestamp _null_ _null_ _null_ ));
  DESCR("timestamp of last replay xact");
--- 3001,3009 ----
  DATA(insert OID = 3810 (  pg_is_in_recovery		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_in_recovery _null_ _null_ _null_ ));
  DESCR("true if server is in recovery");
  
! DATA(insert OID = 3820 ( pg_last_xlog_receive_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_last_xlog_receive_location _null_ _null_ _null_ ));
  DESCR("current xlog flush location");
! DATA(insert OID = 3821 ( pg_last_xlog_replay_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_last_xlog_replay_location _null_ _null_ _null_ ));
  DESCR("last xlog replay location");
  DATA(insert OID = 3830 ( pg_last_xact_replay_timestamp	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 1184 "" _null_ _null_ _null_ _null_ pg_last_xact_replay_timestamp _null_ _null_ _null_ ));
  DESCR("timestamp of last replay xact");
***************
*** 4800,4810 **** DATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0
  DESCR("SP-GiST support for quad tree over range");
  
  /* replication slots */
! DATA(insert OID = 3779 (  pg_create_physical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2249 "19" "{19,25,25}" "{i,o,o}" "{slotname,slotname,xlog_position}" _null_ pg_create_physical_replication_slot _null_ _null_ _null_ ));
  DESCR("create a physical replication slot");
  DATA(insert OID = 3780 (  pg_drop_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2278 "19" _null_ _null_ _null_ _null_ pg_drop_replication_slot _null_ _null_ _null_ ));
  DESCR("drop a replication slot");
! DATA(insert OID = 3781 (  pg_get_replication_slots	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{25,25,26,16,28,25}" "{o,o,o,o,o,o}" "{slot_name,slot_type,datoid,active,xmin,restart_lsn}" _null_ pg_get_replication_slots _null_ _null_ _null_ ));
  DESCR("information about replication slots currently in use");
  
  /* event triggers */
--- 4800,4810 ----
  DESCR("SP-GiST support for quad tree over range");
  
  /* replication slots */
! DATA(insert OID = 3779 (  pg_create_physical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2249 "19" "{19,25,3220}" "{i,o,o}" "{slotname,slotname,xlog_position}" _null_ pg_create_physical_replication_slot _null_ _null_ _null_ ));
  DESCR("create a physical replication slot");
  DATA(insert OID = 3780 (  pg_drop_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2278 "19" _null_ _null_ _null_ _null_ pg_drop_replication_slot _null_ _null_ _null_ ));
  DESCR("drop a replication slot");
! DATA(insert OID = 3781 (  pg_get_replication_slots	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{25,25,26,16,28,3220}" "{o,o,o,o,o,o}" "{slot_name,slot_type,datoid,active,xmin,restart_lsn}" _null_ pg_get_replication_slots _null_ _null_ _null_ ));
  DESCR("information about replication slots currently in use");
  
  /* event triggers */
#17Andres Freund
andres@2ndquadrant.com
In reply to: Michael Paquier (#16)
Re: should we add a XLogRecPtr/LSN SQL type?

Hi,

On 2014-02-04 10:23:14 +0900, Michael Paquier wrote:

On Tue, Feb 4, 2014 at 10:10 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Michael Paquier <michael.paquier@gmail.com> writes:

Please find attached a patch implementing lsn as a datatype, based on
the one Robert wrote a couple of years ago.

Patch contains regression tests as well as a bit of documentation.
Perhaps this is too late for 9.4, so if there are no objections I'll
simply add this patch to the next commit fest in June for 9.5.

I may have lost count, but aren't a bunch of the affected functions new
in 9.4? If so, there's a good argument to be made that we should get
this in now, rather than waiting and having an API change for those
functions in 9.5.

Yes, that sounds sensible.

+ /*----------------------------------------------------------
+  *	Relational operators for LSNs
+  *---------------------------------------------------------*/

Isn't it just operators? They aren't really relational...

*** 302,307 **** extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
--- 303,309 ----
#define PG_RETURN_CHAR(x)	 return CharGetDatum(x)
#define PG_RETURN_BOOL(x)	 return BoolGetDatum(x)
#define PG_RETURN_OID(x)	 return ObjectIdGetDatum(x)
+ #define PG_RETURN_LSN(x)	 return LogSeqNumGetDatum(x)
#define PG_RETURN_POINTER(x) return PointerGetDatum(x)
#define PG_RETURN_CSTRING(x) return CStringGetDatum(x)
#define PG_RETURN_NAME(x)	 return NameGetDatum(x)
*** a/src/include/postgres.h
--- b/src/include/postgres.h
***************
*** 484,489 **** typedef Datum *DatumPtr;
--- 484,503 ----
#define ObjectIdGetDatum(X) ((Datum) SET_4_BYTES(X))
/*
+  * DatumGetLogSeqNum
+  *		Returns log sequence number of a datum.
+  */
+ 
+ #define DatumGetLogSeqNum(X) ((XLogRecPtr) GET_8_BYTES(X))

I am not a fan of LogSegNum. I think at this point fewer people
understand that than LSN. There's also no reason to invent a third term
for LSNs. We'd have LSN, XLogRecPtr, and LogSeqNum.

*** a/src/backend/replication/slotfuncs.c
--- b/src/backend/replication/slotfuncs.c
***************
*** 141,148 **** pg_get_replication_slots(PG_FUNCTION_ARGS)
bool		active;
Oid			database;
const char *slot_name;
- 
- 		char		restart_lsn_s[MAXFNAMELEN];
int			i;
SpinLockAcquire(&slot->mutex);
--- 141,146 ----

Unrelated change.

Looks reasonable on a first look. Thanks!

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Michael Paquier
michael.paquier@gmail.com
In reply to: Andres Freund (#17)
Re: should we add a XLogRecPtr/LSN SQL type?

On Tue, Feb 4, 2014 at 6:15 PM, Andres Freund <andres@2ndquadrant.com> wrote:

+ /*----------------------------------------------------------
+  *  Relational operators for LSNs
+  *---------------------------------------------------------*/

Isn't it just operators? They aren't really relational...

Operators for LSNs?

*** 302,307 **** extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
--- 303,309 ----
#define PG_RETURN_CHAR(x)    return CharGetDatum(x)
#define PG_RETURN_BOOL(x)    return BoolGetDatum(x)
#define PG_RETURN_OID(x)     return ObjectIdGetDatum(x)
+ #define PG_RETURN_LSN(x)     return LogSeqNumGetDatum(x)
#define PG_RETURN_POINTER(x) return PointerGetDatum(x)
#define PG_RETURN_CSTRING(x) return CStringGetDatum(x)
#define PG_RETURN_NAME(x)    return NameGetDatum(x)
*** a/src/include/postgres.h
--- b/src/include/postgres.h
***************
*** 484,489 **** typedef Datum *DatumPtr;
--- 484,503 ----
#define ObjectIdGetDatum(X) ((Datum) SET_4_BYTES(X))
/*
+  * DatumGetLogSeqNum
+  *          Returns log sequence number of a datum.
+  */
+
+ #define DatumGetLogSeqNum(X) ((XLogRecPtr) GET_8_BYTES(X))

I am not a fan of LogSegNum. I think at this point fewer people
understand that than LSN. There's also no reason to invent a third term
for LSNs. We'd have LSN, XLogRecPtr, and LogSeqNum.

So let's go with DatumGetLSN and LSNGetDatum instead...

*** a/src/backend/replication/slotfuncs.c
--- b/src/backend/replication/slotfuncs.c
***************
*** 141,148 **** pg_get_replication_slots(PG_FUNCTION_ARGS)
bool            active;
Oid                     database;
const char *slot_name;
-
-             char            restart_lsn_s[MAXFNAMELEN];
int                     i;
SpinLockAcquire(&slot->mutex);
--- 141,146 ----

Unrelated change.

Funnily, the patch attached in my previous mail did not include all
the diffs, it is an error with filterdiff that I use to generate
context diff patches... My original branch includes the following
diffs as well in slotfuncs.c for the second patch:
diff --git a/src/backend/replication/slotfuncs.c
b/src/backend/replication/slotfuncs.c
index 98a860e..68ecdcd 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -141,8 +141,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
                bool            active;
                Oid                     database;
                const char *slot_name;
-
-               char            restart_lsn_s[MAXFNAMELEN];
                int                     i;

SpinLockAcquire(&slot->mutex);
@@ -164,9 +162,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)

memset(nulls, 0, sizeof(nulls));

-               snprintf(restart_lsn_s, sizeof(restart_lsn_s), "%X/%X",
-                                (uint32) (restart_lsn >> 32),
(uint32) restart_lsn);
-
                i = 0;
                values[i++] = CStringGetTextDatum(slot_name);
                if (database == InvalidOid)
@@ -180,7 +175,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
                else
                        nulls[i++] = true;
                if (restart_lsn != InvalidTransactionId)
-                       values[i++] = CStringGetTextDatum(restart_lsn_s);
+                       values[i++] = restart_lsn;
                else
                        nulls[i++] = true;

Anything else?
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Andres Freund
andres@2ndquadrant.com
In reply to: Michael Paquier (#18)
Re: should we add a XLogRecPtr/LSN SQL type?

On 2014-02-04 19:17:51 +0900, Michael Paquier wrote:

On Tue, Feb 4, 2014 at 6:15 PM, Andres Freund <andres@2ndquadrant.com> wrote:

+ /*----------------------------------------------------------
+  *  Relational operators for LSNs
+  *---------------------------------------------------------*/

Isn't it just operators? They aren't really relational...

Operators for LSNs?

Fine with me.

+ #define DatumGetLogSeqNum(X) ((XLogRecPtr) GET_8_BYTES(X))

I am not a fan of LogSegNum. I think at this point fewer people
understand that than LSN. There's also no reason to invent a third term
for LSNs. We'd have LSN, XLogRecPtr, and LogSeqNum.

So let's go with DatumGetLSN and LSNGetDatum instead...

Sup.

*** a/src/backend/replication/slotfuncs.c
--- b/src/backend/replication/slotfuncs.c
***************
*** 141,148 **** pg_get_replication_slots(PG_FUNCTION_ARGS)
bool            active;
Oid                     database;
const char *slot_name;
-
-             char            restart_lsn_s[MAXFNAMELEN];
int                     i;
SpinLockAcquire(&slot->mutex);
--- 141,146 ----

Unrelated change.

Funnily, the patch attached in my previous mail did not include all
the diffs, it is an error with filterdiff that I use to generate
context diff patches... My original branch includes the following

Ah, then it makes more sense.

diffs as well in slotfuncs.c for the second patch:
diff --git a/src/backend/replication/slotfuncs.c
b/src/backend/replication/slotfuncs.c
index 98a860e..68ecdcd 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -141,8 +141,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
bool            active;
Oid                     database;
const char *slot_name;
-
-               char            restart_lsn_s[MAXFNAMELEN];
int                     i;

SpinLockAcquire(&slot->mutex);
@@ -164,9 +162,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)

memset(nulls, 0, sizeof(nulls));

-               snprintf(restart_lsn_s, sizeof(restart_lsn_s), "%X/%X",
-                                (uint32) (restart_lsn >> 32),
(uint32) restart_lsn);
-
i = 0;
values[i++] = CStringGetTextDatum(slot_name);
if (database == InvalidOid)
@@ -180,7 +175,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
else
nulls[i++] = true;
if (restart_lsn != InvalidTransactionId)
-                       values[i++] = CStringGetTextDatum(restart_lsn_s);
+                       values[i++] = restart_lsn;
else
nulls[i++] = true;

Isn't that missing a LSNGetDatum()? Also, isn't it lacking the
corresponding pg_proc change?

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Michael Paquier
michael.paquier@gmail.com
In reply to: Andres Freund (#19)
Re: should we add a XLogRecPtr/LSN SQL type?

On Tue, Feb 4, 2014 at 7:22 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-04 19:17:51 +0900, Michael Paquier wrote:

@@ -180,7 +175,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
else
nulls[i++] = true;
if (restart_lsn != InvalidTransactionId)
-                       values[i++] = CStringGetTextDatum(restart_lsn_s);
+                       values[i++] = restart_lsn;
else
nulls[i++] = true;

Isn't that missing a LSNGetDatum()?

Oops yes. Will fix.

Also, isn't it lacking the corresponding pg_proc change?

restart_lsn is the 6th argument of pg_get_replication_slots, and the
list of arguments of this function is already changed like that in my
patch:
{25,25,26,16,28,25} => {25,25,26,16,28,3220}
Regards,
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Andres Freund
andres@2ndquadrant.com
In reply to: Michael Paquier (#20)
Re: should we add a XLogRecPtr/LSN SQL type?

On 2014-02-04 21:04:13 +0900, Michael Paquier wrote:

On Tue, Feb 4, 2014 at 7:22 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-04 19:17:51 +0900, Michael Paquier wrote:

@@ -180,7 +175,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
else
nulls[i++] = true;
if (restart_lsn != InvalidTransactionId)
-                       values[i++] = CStringGetTextDatum(restart_lsn_s);
+                       values[i++] = restart_lsn;
else
nulls[i++] = true;

Isn't that missing a LSNGetDatum()?

Oops yes. Will fix.

Also, isn't it lacking the corresponding pg_proc change?

restart_lsn is the 6th argument of pg_get_replication_slots, and the
list of arguments of this function is already changed like that in my
patch:
{25,25,26,16,28,25} => {25,25,26,16,28,3220}
Regards,

Ok.

I think the patch should also adapt pageinspect...

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Peter Eisentraut
peter_e@gmx.net
In reply to: Michael Paquier (#16)
Re: should we add a XLogRecPtr/LSN SQL type?

Perhaps this type should be called pglsn, since it's an
implementation-specific detail and not a universal concept like int,
point, or uuid.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Michael Paquier
michael.paquier@gmail.com
In reply to: Peter Eisentraut (#22)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Feb 5, 2014 at 5:26 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

Perhaps this type should be called pglsn, since it's an
implementation-specific detail and not a universal concept like int,
point, or uuid.

It makes sense. I'll update the patches according to that.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#23)
2 attachment(s)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Feb 5, 2014 at 8:59 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

I'll update the patches according to that.

Here are the updated patches with the following changes (according to
previous comments):
- Datatype is renamed to pglsn, documentation, file names, regressions
and APIs are updated as well.
- The DatumGet* and *GetDatum APIs are renamed with PGLSN (Should be
PgLsn? But that's a detail)
- pg_create_physical_replication_slot uses PGLSNGetDatum for its 6th argument
For pageinspect, only page_header is impacted and I think that this
should be a separated patch as it makes necessary to dump it to 1.2. I
can write it later once the core parts are decided.
Thanks,
--
Michael

Attachments:

20140205_lsn_datatype_v3.patchtext/x-patch; charset=US-ASCII; name=20140205_lsn_datatype_v3.patchDownload
*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
***************
*** 181,186 ****
--- 181,192 ----
        </row>
  
        <row>
+        <entry><type>pglsn</type></entry>
+        <entry></entry>
+        <entry><productname>PostgreSQL</productname> Log Sequence Number</entry>
+       </row>
+ 
+       <row>
         <entry><type>point</type></entry>
         <entry></entry>
         <entry>geometric point on a plane</entry>
***************
*** 4502,4507 **** SELECT * FROM pg_attribute
--- 4508,4528 ----
     </para>
    </sect1>
  
+   <sect1 id="datatype-pglsn">
+    <title><acronym>PGLSN Type</title>
+ 
+    <indexterm zone="datatype-pglsn">
+     <primary>PGLSN</primary>
+    </indexterm>
+ 
+    <para>
+     The <type>pglsn</type> data type can be used to store LSN (Log Sequence
+     Number) data which is a pointer to a location in the XLOG. This type is a
+     representation of XLogRecPtr and an internal system type of
+     <productname>PostgreSQL</productname>.
+    </para>
+   </sect1>
+ 
    <sect1 id="datatype-pseudo">
     <title>Pseudo-Types</title>
  
*** a/src/backend/utils/adt/Makefile
--- b/src/backend/utils/adt/Makefile
***************
*** 21,27 **** OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
  	enum.o float.o format_type.o \
  	geo_ops.o geo_selfuncs.o int.o int8.o json.o jsonfuncs.o like.o \
  	lockfuncs.o misc.o nabstime.o name.o numeric.o numutils.o \
! 	oid.o oracle_compat.o orderedsetaggs.o \
  	pseudotypes.o rangetypes.o rangetypes_gist.o \
  	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
  	tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
--- 21,27 ----
  	enum.o float.o format_type.o \
  	geo_ops.o geo_selfuncs.o int.o int8.o json.o jsonfuncs.o like.o \
  	lockfuncs.o misc.o nabstime.o name.o numeric.o numutils.o \
! 	oid.o oracle_compat.o orderedsetaggs.o pglsn.o \
  	pseudotypes.o rangetypes.o rangetypes_gist.o \
  	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
  	tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
*** /dev/null
--- b/src/backend/utils/adt/pglsn.c
***************
*** 0 ****
--- 1,180 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pglsn.c
+  *	  Internal PostgreSQL LSN operations
+  *
+  * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/adt/pglsn.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "funcapi.h"
+ #include "libpq/pqformat.h"
+ #include "utils/builtins.h"
+ #include "utils/pglsn.h"
+ 
+ #define MAXPGLSNLEN			17
+ #define MAXPGLSNCOMPONENT	8
+ 
+ /*----------------------------------------------------------
+  * Formatting and conversion routines.
+  *---------------------------------------------------------*/
+ 
+ Datum
+ pglsn_in(PG_FUNCTION_ARGS)
+ {
+ 	char	   *str = PG_GETARG_CSTRING(0);
+ 	int			len1, len2;
+ 	uint32		id, off;
+ 	XLogRecPtr	result;
+ 
+ 	/* Sanity check input format. */
+ 	len1 = strspn(str, "0123456789abcdefABCDEF");
+ 	if (len1 < 1 || len1 > MAXPGLSNCOMPONENT || str[len1] != '/')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ 				 errmsg("invalid input syntax for transaction log location: \"%s\"", str)));
+ 	len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
+ 	if (len2 < 1 || len2 > MAXPGLSNCOMPONENT || str[len1 + 1 + len2] != '\0')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ 				 errmsg("invalid input syntax for transaction log location: \"%s\"", str)));
+ 
+ 	/* Decode result. */
+ 	id = (uint32) strtoul(str, NULL, 16);
+ 	off = (uint32) strtoul(str + len1 + 1, NULL, 16);
+ 	result = (XLogRecPtr) ((uint64) id << 32) | off;
+ 
+ 	PG_RETURN_PGLSN(result);
+ }
+ 
+ Datum
+ pglsn_out(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr	lsn = (XLogRecPtr) PG_GETARG_PGLSN(0);
+ 	char		buf[MAXPGLSNLEN + 1];
+ 	char	   *result;
+ 	uint32		id, off;
+ 
+ 	/* Decode ID and offset */
+ 	id = (uint32) (lsn >> 32);
+ 	off = (uint32) lsn;
+ 
+ 	sprintf(buf, "%X/%X", id, off);
+ 	result = pstrdup(buf);
+ 	PG_RETURN_CSTRING(result);
+ }
+ 
+ Datum
+ pglsn_recv(PG_FUNCTION_ARGS)
+ {
+ 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+ 	XLogRecPtr	result;
+ 
+ 	result = pq_getmsgint64(buf);
+ 	PG_RETURN_PGLSN(result);
+ }
+ 
+ Datum
+ pglsn_send(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn = (XLogRecPtr) PG_GETARG_PGLSN(0);
+ 	StringInfoData buf;
+ 
+ 	pq_begintypsend(&buf);
+ 	pq_sendint64(&buf, lsn);
+ 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+ }
+ 
+ 
+ /*----------------------------------------------------------
+  *	Operators for PostgreSQL LSNs
+  *---------------------------------------------------------*/
+ 
+ Datum
+ pglsn_eq(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PGLSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PGLSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 == lsn2);
+ }
+ 
+ Datum
+ pglsn_ne(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PGLSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PGLSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 != lsn2);
+ }
+ 
+ Datum
+ pglsn_lt(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PGLSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PGLSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 < lsn2);
+ }
+ 
+ Datum
+ pglsn_gt(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PGLSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PGLSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 > lsn2);
+ }
+ 
+ Datum
+ pglsn_le(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PGLSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PGLSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 <= lsn2);
+ }
+ 
+ Datum
+ pglsn_ge(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PGLSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PGLSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 >= lsn2);
+ }
+ 
+ 
+ /*----------------------------------------------------------
+  *	Arithmetic operators on PostgreSQL LSNs.
+  *---------------------------------------------------------*/
+ 
+ Datum
+ pglsn_mi(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PGLSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PGLSN(1);
+ 	char		buf[256];
+ 	Datum		result;
+ 
+ 	/* Negative results are not allowed. */
+ 	if (lsn1 < lsn2)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ 				 errmsg("transaction log location out of range")));
+ 
+ 	/* Convert to numeric. */
+ 	sprintf(buf, UINT64_FORMAT, lsn1 - lsn2);
+ 	result = DirectFunctionCall3(numeric_in,
+ 								 CStringGetDatum(buf),
+ 								 ObjectIdGetDatum(0),
+ 								 Int32GetDatum(-1));
+ 
+ 	return result;
+ }
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
***************
*** 1592,1597 **** DESCR("less than or equal");
--- 1592,1613 ----
  DATA(insert OID = 2977 (  ">="	   PGNSP PGUID b f f 2950 2950 16 2976 2974 uuid_ge scalargtsel scalargtjoinsel ));
  DESCR("greater than or equal");
  
+ /* pglsn operators */
+ DATA(insert OID = 3222 (  "="	   PGNSP PGUID b f f 3220 3220 16 3222 3223 pglsn_eq eqsel eqjoinsel ));
+ DESCR("equal");
+ DATA(insert OID = 3223 (  "<>"	   PGNSP PGUID b f f 3220 3220 16 3223 3222 pglsn_ne neqsel neqjoinsel ));
+ DESCR("not equal");
+ DATA(insert OID = 3224 (  "<"	   PGNSP PGUID b f f 3220 3220 16 3225 3227 pglsn_lt scalarltsel scalarltjoinsel ));
+ DESCR("less than");
+ DATA(insert OID = 3225 (  ">"	   PGNSP PGUID b f f 3220 3220 16 3224 3226 pglsn_gt scalargtsel scalargtjoinsel ));
+ DESCR("greater than");
+ DATA(insert OID = 3226 (  "<="	   PGNSP PGUID b f f 3220 3220 16 3227 3225 pglsn_le scalarltsel scalarltjoinsel ));
+ DESCR("less than or equal");
+ DATA(insert OID = 3227 (  ">="	   PGNSP PGUID b f f 3220 3220 16 3226 3224 pglsn_ge scalargtsel scalargtjoinsel ));
+ DESCR("greater than or equal");
+ DATA(insert OID = 3228 (  "-"	   PGNSP PGUID b f f 3220 3220 1700    0	0 pglsn_mi - - ));
+ DESCR("minus");
+ 
  /* enum operators */
  DATA(insert OID = 3516 (  "="	   PGNSP PGUID b t t 3500 3500 16 3516 3517 enum_eq eqsel eqjoinsel ));
  DESCR("equal");
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4212,4217 **** DESCR("I/O");
--- 4212,4234 ----
  DATA(insert OID = 2963 (  uuid_hash		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "2950" _null_ _null_ _null_ _null_ uuid_hash _null_ _null_ _null_ ));
  DESCR("hash");
  
+ /* pglsn */
+ DATA(insert OID = 3229 (  pglsn_in		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3220 "2275" _null_ _null_ _null_ _null_ pglsn_in _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3230 (  pglsn_out		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3220" _null_ _null_ _null_ _null_ pglsn_out _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3231 (  pglsn_lt		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pglsn_lt _null_ _null_ _null_ ));
+ DATA(insert OID = 3232 (  pglsn_le		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pglsn_le _null_ _null_ _null_ ));
+ DATA(insert OID = 3233 (  pglsn_eq		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pglsn_eq _null_ _null_ _null_ ));
+ DATA(insert OID = 3234 (  pglsn_ge		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pglsn_ge _null_ _null_ _null_ ));
+ DATA(insert OID = 3235 (  pglsn_gt		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pglsn_gt _null_ _null_ _null_ ));
+ DATA(insert OID = 3236 (  pglsn_ne		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pglsn_ne _null_ _null_ _null_ ));
+ DATA(insert OID = 3237 (  pglsn_mi		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "3220 3220" _null_ _null_ _null_ _null_ pglsn_mi _null_ _null_ _null_ ));
+ DATA(insert OID = 3238 (  pglsn_recv	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3220 "2281" _null_ _null_ _null_ _null_ pglsn_recv _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3239 (  pglsn_send	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3220" _null_ _null_ _null_ _null_ pglsn_send _null_ _null_ _null_ ));
+ DESCR("I/O");
+ 
  /* enum related procs */
  DATA(insert OID = 3504 (  anyenum_in	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ ));
  DESCR("I/O");
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 577,582 **** DESCR("UUID datatype");
--- 577,587 ----
  #define UUIDOID 2950
  DATA(insert OID = 2951 ( _uuid			PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
  
+ /* pglsn */
+ DATA(insert OID = 3220 ( pglsn			PGNSP PGUID 8 t b U t t \054 0 0 3221 pglsn_in pglsn_out pglsn_recv pglsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+ DESCR("LSN datatype");
+ DATA(insert OID = 3221 ( _pglsn			PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+ 
  /* text search */
  DATA(insert OID = 3614 ( tsvector		PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
  DESCR("text representation for text search");
*** a/src/include/fmgr.h
--- b/src/include/fmgr.h
***************
*** 230,235 **** extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
--- 230,236 ----
  #define PG_GETARG_CHAR(n)	 DatumGetChar(PG_GETARG_DATUM(n))
  #define PG_GETARG_BOOL(n)	 DatumGetBool(PG_GETARG_DATUM(n))
  #define PG_GETARG_OID(n)	 DatumGetObjectId(PG_GETARG_DATUM(n))
+ #define PG_GETARG_PGLSN(n)	 DatumGetPGLSN(PG_GETARG_DATUM(n))
  #define PG_GETARG_POINTER(n) DatumGetPointer(PG_GETARG_DATUM(n))
  #define PG_GETARG_CSTRING(n) DatumGetCString(PG_GETARG_DATUM(n))
  #define PG_GETARG_NAME(n)	 DatumGetName(PG_GETARG_DATUM(n))
***************
*** 302,307 **** extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
--- 303,309 ----
  #define PG_RETURN_CHAR(x)	 return CharGetDatum(x)
  #define PG_RETURN_BOOL(x)	 return BoolGetDatum(x)
  #define PG_RETURN_OID(x)	 return ObjectIdGetDatum(x)
+ #define PG_RETURN_PGLSN(x)	 return PGLSNGetDatum(x)
  #define PG_RETURN_POINTER(x) return PointerGetDatum(x)
  #define PG_RETURN_CSTRING(x) return CStringGetDatum(x)
  #define PG_RETURN_NAME(x)	 return NameGetDatum(x)
*** a/src/include/postgres.h
--- b/src/include/postgres.h
***************
*** 484,489 **** typedef Datum *DatumPtr;
--- 484,503 ----
  #define ObjectIdGetDatum(X) ((Datum) SET_4_BYTES(X))
  
  /*
+  * DatumGetPGLSN
+  *		Returns PostgreSQL log sequence number of a datum.
+  */
+ 
+ #define DatumGetPGLSN(X) ((XLogRecPtr) GET_8_BYTES(X))
+ 
+ /*
+  * PGLSNGetDatum
+  *		Returns datum representation for a PostgreSQL log sequence number.
+  */
+ 
+ #define PGLSNGetDatum(X) ((Datum) SET_8_BYTES(X))
+ 
+ /*
   * DatumGetTransactionId
   *		Returns transaction identifier value of a datum.
   */
*** /dev/null
--- b/src/include/utils/pglsn.h
***************
*** 0 ****
--- 1,34 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pglsn.h
+  *		Declarations for operations on log sequence numbers (LSNs) of
+  *		PostgreSQL.
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/utils/pglsn.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef PGLSN_H
+ #define PGLSN_H
+ 
+ #include "fmgr.h"
+ 
+ extern Datum pglsn_in(PG_FUNCTION_ARGS);
+ extern Datum pglsn_out(PG_FUNCTION_ARGS);
+ extern Datum pglsn_recv(PG_FUNCTION_ARGS);
+ extern Datum pglsn_send(PG_FUNCTION_ARGS);
+ 
+ extern Datum pglsn_eq(PG_FUNCTION_ARGS);
+ extern Datum pglsn_ne(PG_FUNCTION_ARGS);
+ extern Datum pglsn_lt(PG_FUNCTION_ARGS);
+ extern Datum pglsn_gt(PG_FUNCTION_ARGS);
+ extern Datum pglsn_le(PG_FUNCTION_ARGS);
+ extern Datum pglsn_ge(PG_FUNCTION_ARGS);
+ 
+ extern Datum pglsn_mi(PG_FUNCTION_ARGS);
+ 
+ #endif   /* PGLSN_H */
*** /dev/null
--- b/src/test/regress/expected/pglsn.out
***************
*** 0 ****
--- 1,62 ----
+ --
+ -- PGLSN
+ --
+ CREATE TABLE PGLSN_TBL (f1 pglsn);
+ -- Largest and smallest input
+ INSERT INTO PGLSN_TBL VALUES ('0/0');
+ INSERT INTO PGLSN_TBL VALUES ('FFFFFFFF/FFFFFFFF');
+ -- Incorrect input
+ INSERT INTO PGLSN_TBL VALUES ('G/0');
+ ERROR:  invalid input syntax for transaction log location: "G/0"
+ LINE 1: INSERT INTO PGLSN_TBL VALUES ('G/0');
+                                       ^
+ INSERT INTO PGLSN_TBL VALUES ('-1/0');
+ ERROR:  invalid input syntax for transaction log location: "-1/0"
+ LINE 1: INSERT INTO PGLSN_TBL VALUES ('-1/0');
+                                       ^
+ INSERT INTO PGLSN_TBL VALUES (' 0/12345678');
+ ERROR:  invalid input syntax for transaction log location: " 0/12345678"
+ LINE 1: INSERT INTO PGLSN_TBL VALUES (' 0/12345678');
+                                       ^
+ INSERT INTO PGLSN_TBL VALUES ('ABCD/');
+ ERROR:  invalid input syntax for transaction log location: "ABCD/"
+ LINE 1: INSERT INTO PGLSN_TBL VALUES ('ABCD/');
+                                       ^
+ INSERT INTO PGLSN_TBL VALUES ('/ABCD');
+ ERROR:  invalid input syntax for transaction log location: "/ABCD"
+ LINE 1: INSERT INTO PGLSN_TBL VALUES ('/ABCD');
+                                       ^
+ DROP TABLE PGLSN_TBL;
+ -- Operators
+ SELECT pglsn_eq('0/16AE7F8', '0/16AE7F8'); -- true
+  pglsn_eq 
+ ----------
+  t
+ (1 row)
+ 
+ SELECT pglsn_ne('0/16AE7F8', '0/16AE7F7'); -- true
+  pglsn_ne 
+ ----------
+  t
+ (1 row)
+ 
+ SELECT pglsn_lt('0/16AE7F7', '0/16AE7F8'); -- true
+  pglsn_lt 
+ ----------
+  t
+ (1 row)
+ 
+ SELECT pglsn_gt('0/16AE7F8', '0/16AE7F7'); -- true
+  pglsn_gt 
+ ----------
+  t
+ (1 row)
+ 
+ SELECT pglsn_mi('0/16AE7F7', '0/16AE7F8'); -- No negative results
+ ERROR:  transaction log location out of range
+ SELECT pglsn_mi('0/16AE7F8', '0/16AE7F7'); -- correct
+  pglsn_mi 
+ ----------
+         1
+ (1 row)
+ 
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 13,19 **** test: tablespace
  # ----------
  # The first group of parallel tests
  # ----------
! test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes
  
  # Depends on things setup during char, varchar and text
  test: strings
--- 13,19 ----
  # ----------
  # The first group of parallel tests
  # ----------
! test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes pglsn
  
  # Depends on things setup during char, varchar and text
  test: strings
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 19,24 **** test: uuid
--- 19,25 ----
  test: enum
  test: money
  test: rangetypes
+ test: pglsn
  test: strings
  test: numerology
  test: point
*** /dev/null
--- b/src/test/regress/sql/pglsn.sql
***************
*** 0 ****
--- 1,25 ----
+ --
+ -- PGLSN
+ --
+ 
+ CREATE TABLE PGLSN_TBL (f1 pglsn);
+ 
+ -- Largest and smallest input
+ INSERT INTO PGLSN_TBL VALUES ('0/0');
+ INSERT INTO PGLSN_TBL VALUES ('FFFFFFFF/FFFFFFFF');
+ 
+ -- Incorrect input
+ INSERT INTO PGLSN_TBL VALUES ('G/0');
+ INSERT INTO PGLSN_TBL VALUES ('-1/0');
+ INSERT INTO PGLSN_TBL VALUES (' 0/12345678');
+ INSERT INTO PGLSN_TBL VALUES ('ABCD/');
+ INSERT INTO PGLSN_TBL VALUES ('/ABCD');
+ DROP TABLE PGLSN_TBL;
+ 
+ -- Operators
+ SELECT pglsn_eq('0/16AE7F8', '0/16AE7F8'); -- true
+ SELECT pglsn_ne('0/16AE7F8', '0/16AE7F7'); -- true
+ SELECT pglsn_lt('0/16AE7F7', '0/16AE7F8'); -- true
+ SELECT pglsn_gt('0/16AE7F8', '0/16AE7F7'); -- true
+ SELECT pglsn_mi('0/16AE7F7', '0/16AE7F8'); -- No negative results
+ SELECT pglsn_mi('0/16AE7F8', '0/16AE7F7'); -- correct
20140205_lsn_func_cleanup_v2.patchtext/x-patch; charset=US-ASCII; name=20140205_lsn_func_cleanup_v2.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index dca24fc..49c20e2 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -5244,7 +5244,7 @@
 
      <row>
       <entry><structfield>restart_lsn</structfield></entry>
-      <entry><type>text</type></entry>
+      <entry><type>pglsn</type></entry>
       <entry></entry>
       <entry>The address (<literal>LSN</literal>) of oldest WAL which still
       might be required by the consumer of this slot and thus won't be
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index be548d7..c7ac74b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15884,35 +15884,35 @@ SELECT set_config('log_statement_stats', 'off', false);
        <entry>
         <literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pglsn</type></entry>
        <entry>Create a named point for performing restore (restricted to superusers)</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_current_xlog_insert_location()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pglsn</type></entry>
        <entry>Get current transaction log insert location</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_current_xlog_location()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pglsn</type></entry>
        <entry>Get current transaction log write location</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pglsn</type></entry>
        <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_stop_backup()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pglsn</type></entry>
        <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
       </row>
       <row>
@@ -15933,26 +15933,26 @@ SELECT set_config('log_statement_stats', 'off', false);
        <entry>
         <literal><function>pg_switch_xlog()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pglsn</type></entry>
        <entry>Force switch to a new transaction log file (restricted to superusers)</entry>
       </row>
       <row>
        <entry>
-        <literal><function>pg_xlogfile_name(<parameter>location</> <type>text</>)</function></literal>
+        <literal><function>pg_xlogfile_name(<parameter>location</> <type>pglsn</>)</function></literal>
         </entry>
        <entry><type>text</type></entry>
        <entry>Convert transaction log location string to file name</entry>
       </row>
       <row>
        <entry>
-        <literal><function>pg_xlogfile_name_offset(<parameter>location</> <type>text</>)</function></literal>
+        <literal><function>pg_xlogfile_name_offset(<parameter>location</> <type>pglsn</>)</function></literal>
         </entry>
        <entry><type>text</>, <type>integer</></entry>
        <entry>Convert transaction log location string to file name and decimal byte offset within file</entry>
       </row>
       <row>
        <entry>
-        <literal><function>pg_xlog_location_diff(<parameter>location</> <type>text</>, <parameter>location</> <type>text</>)</function></literal>
+        <literal><function>pg_xlog_location_diff(<parameter>location</> <type>pglsn</>, <parameter>location</> <type>pglsn</>)</function></literal>
        </entry>
        <entry><type>numeric</></entry>
        <entry>Calculate the difference between two transaction log locations</entry>
@@ -16107,7 +16107,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
        <entry>
         <literal><function>pg_last_xlog_receive_location()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pglsn</type></entry>
        <entry>Get last transaction log location received and synced to disk by
         streaming replication. While streaming replication is in progress
         this will increase monotonically. If recovery has completed this will
@@ -16121,7 +16121,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
        <entry>
         <literal><function>pg_last_xlog_replay_location()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pglsn</type></entry>
        <entry>Get last transaction log location replayed during recovery.
         If recovery is still in progress this will increase monotonically.
         If recovery has completed then this value will remain static at
@@ -16335,7 +16335,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_create_physical_replication_slot(<parameter>slotname</parameter> <type>text</type>, <parameter>plugin</parameter> <type>text</type>)</function></literal>
        </entry>
        <entry>
-        (<parameter>slotname</parameter> <type>text</type>, <parameter>xlog_position</parameter> <type>text</type>)
+        (<parameter>slotname</parameter> <type>text</type>, <parameter>xlog_position</parameter> <type>pglsn</type>)
        </entry>
        <entry>
         Creates a new physical replication slot named
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 9133179..830586c 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -33,8 +33,6 @@
 #include "utils/timestamp.h"
 #include "storage/fd.h"
 
-static void validate_xlog_location(char *str);
-
 
 /*
  * pg_start_backup: set up for taking an on-line backup dump
@@ -52,7 +50,6 @@ pg_start_backup(PG_FUNCTION_ARGS)
 	bool		fast = PG_GETARG_BOOL(1);
 	char	   *backupidstr;
 	XLogRecPtr	startpoint;
-	char		startxlogstr[MAXFNAMELEN];
 
 	backupidstr = text_to_cstring(backupid);
 
@@ -63,9 +60,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL);
 
-	snprintf(startxlogstr, sizeof(startxlogstr), "%X/%X",
-			 (uint32) (startpoint >> 32), (uint32) startpoint);
-	PG_RETURN_TEXT_P(cstring_to_text(startxlogstr));
+	PG_RETURN_PGLSN(startpoint);
 }
 
 /*
@@ -85,7 +80,6 @@ Datum
 pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
-	char		stopxlogstr[MAXFNAMELEN];
 
 	if (!superuser() && !has_rolreplication(GetUserId()))
 		ereport(ERROR,
@@ -94,9 +88,7 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 
 	stoppoint = do_pg_stop_backup(NULL, true, NULL);
 
-	snprintf(stopxlogstr, sizeof(stopxlogstr), "%X/%X",
-			 (uint32) (stoppoint >> 32), (uint32) stoppoint);
-	PG_RETURN_TEXT_P(cstring_to_text(stopxlogstr));
+	PG_RETURN_PGLSN(stoppoint);
 }
 
 /*
@@ -124,9 +116,7 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 	/*
 	 * As a convenience, return the WAL location of the switch record
 	 */
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (switchpoint >> 32), (uint32) switchpoint);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PGLSN(switchpoint);
 }
 
 /*
@@ -169,9 +159,7 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	/*
 	 * As a convenience, return the WAL location of the restore point record
 	 */
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (restorepoint >> 32), (uint32) restorepoint);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PGLSN(restorepoint);
 }
 
 /*
@@ -185,7 +173,6 @@ Datum
 pg_current_xlog_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
-	char		location[MAXFNAMELEN];
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -195,9 +182,7 @@ pg_current_xlog_location(PG_FUNCTION_ARGS)
 
 	current_recptr = GetXLogWriteRecPtr();
 
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (current_recptr >> 32), (uint32) current_recptr);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PGLSN(current_recptr);
 }
 
 /*
@@ -209,7 +194,6 @@ Datum
 pg_current_xlog_insert_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
-	char		location[MAXFNAMELEN];
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -219,9 +203,7 @@ pg_current_xlog_insert_location(PG_FUNCTION_ARGS)
 
 	current_recptr = GetXLogInsertRecPtr();
 
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (current_recptr >> 32), (uint32) current_recptr);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PGLSN(current_recptr);
 }
 
 /*
@@ -234,16 +216,13 @@ Datum
 pg_last_xlog_receive_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
-	char		location[MAXFNAMELEN];
 
 	recptr = GetWalRcvWriteRecPtr(NULL, NULL);
 
 	if (recptr == 0)
 		PG_RETURN_NULL();
 
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (recptr >> 32), (uint32) recptr);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PGLSN(recptr);
 }
 
 /*
@@ -256,16 +235,13 @@ Datum
 pg_last_xlog_replay_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
-	char		location[MAXFNAMELEN];
 
 	recptr = GetXLogReplayRecPtr(NULL);
 
 	if (recptr == 0)
 		PG_RETURN_NULL();
 
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (recptr >> 32), (uint32) recptr);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PGLSN(recptr);
 }
 
 /*
@@ -279,13 +255,11 @@ pg_last_xlog_replay_location(PG_FUNCTION_ARGS)
 Datum
 pg_xlogfile_name_offset(PG_FUNCTION_ARGS)
 {
-	text	   *location = PG_GETARG_TEXT_P(0);
-	char	   *locationstr;
 	uint32		hi,
 				lo;
 	XLogSegNo	xlogsegno;
 	uint32		xrecoff;
-	XLogRecPtr	locationpoint;
+	XLogRecPtr	locationpoint = PG_GETARG_PGLSN(0);
 	char		xlogfilename[MAXFNAMELEN];
 	Datum		values[2];
 	bool		isnull[2];
@@ -300,20 +274,6 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS)
 				 errhint("pg_xlogfile_name_offset() cannot be executed during recovery.")));
 
 	/*
-	 * Read input and parse
-	 */
-	locationstr = text_to_cstring(location);
-
-	validate_xlog_location(locationstr);
-
-	if (sscanf(locationstr, "%X/%X", &hi, &lo) != 2)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("could not parse transaction log location \"%s\"",
-						locationstr)));
-	locationpoint = ((uint64) hi) << 32 | lo;
-
-	/*
 	 * Construct a tuple descriptor for the result row.  This must match this
 	 * function's pg_proc entry!
 	 */
@@ -359,12 +319,10 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS)
 Datum
 pg_xlogfile_name(PG_FUNCTION_ARGS)
 {
-	text	   *location = PG_GETARG_TEXT_P(0);
-	char	   *locationstr;
 	uint32		hi,
 				lo;
 	XLogSegNo	xlogsegno;
-	XLogRecPtr	locationpoint;
+	XLogRecPtr	locationpoint = PG_GETARG_PGLSN(0);
 	char		xlogfilename[MAXFNAMELEN];
 
 	if (RecoveryInProgress())
@@ -373,17 +331,6 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 				 errmsg("recovery is in progress"),
 		 errhint("pg_xlogfile_name() cannot be executed during recovery.")));
 
-	locationstr = text_to_cstring(location);
-
-	validate_xlog_location(locationstr);
-
-	if (sscanf(locationstr, "%X/%X", &hi, &lo) != 2)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("could not parse transaction log location \"%s\"",
-						locationstr)));
-	locationpoint = ((uint64) hi) << 32 | lo;
-
 	XLByteToPrevSeg(locationpoint, xlogsegno);
 	XLogFileName(xlogfilename, ThisTimeLineID, xlogsegno);
 
@@ -482,77 +429,27 @@ pg_is_in_recovery(PG_FUNCTION_ARGS)
 }
 
 /*
- * Validate the text form of a transaction log location.
- * (Just using sscanf() input allows incorrect values such as
- * negatives, so we have to be a bit more careful about that).
- */
-static void
-validate_xlog_location(char *str)
-{
-#define MAXLSNCOMPONENT		8
-
-	int			len1,
-				len2;
-
-	len1 = strspn(str, "0123456789abcdefABCDEF");
-	if (len1 < 1 || len1 > MAXLSNCOMPONENT || str[len1] != '/')
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for transaction log location: \"%s\"", str)));
-
-	len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
-	if (len2 < 1 || len2 > MAXLSNCOMPONENT || str[len1 + 1 + len2] != '\0')
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for transaction log location: \"%s\"", str)));
-}
-
-/*
  * Compute the difference in bytes between two WAL locations.
  */
 Datum
 pg_xlog_location_diff(PG_FUNCTION_ARGS)
 {
-	text	   *location1 = PG_GETARG_TEXT_P(0);
-	text	   *location2 = PG_GETARG_TEXT_P(1);
-	char	   *str1,
-			   *str2;
-	XLogRecPtr	loc1,
-				loc2;
+	XLogRecPtr	loc1 = PG_GETARG_PGLSN(0);
+	XLogRecPtr	loc2 = PG_GETARG_PGLSN(1);
 	Numeric		result;
 	uint64		bytes1,
 				bytes2;
 	uint32		hi,
 				lo;
 
-	/*
-	 * Read and parse input
-	 */
-	str1 = text_to_cstring(location1);
-	str2 = text_to_cstring(location2);
-
-	validate_xlog_location(str1);
-	validate_xlog_location(str2);
-
-	if (sscanf(str1, "%X/%X", &hi, &lo) != 2)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-		   errmsg("could not parse transaction log location \"%s\"", str1)));
-	loc1 = ((uint64) hi) << 32 | lo;
-
-	if (sscanf(str2, "%X/%X", &hi, &lo) != 2)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-		   errmsg("could not parse transaction log location \"%s\"", str2)));
-	loc2 = ((uint64) hi) << 32 | lo;
-
 	bytes1 = (uint64) loc1;
 	bytes2 = (uint64) loc2;
 
 	/*
 	 * result = bytes1 - bytes2.
 	 *
-	 * XXX: this won't handle values higher than 2^63 correctly.
+	 * XXX: this won't handle values higher than 2^63 correctly and can
+	 * return negative values.
 	 */
 	result = DatumGetNumeric(DirectFunctionCall2(numeric_sub,
 			DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) bytes1)),
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f02efec..04f8afb 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -810,7 +810,7 @@ COMMENT ON FUNCTION ts_debug(text) IS
 
 CREATE OR REPLACE FUNCTION
   pg_start_backup(label text, fast boolean DEFAULT false)
-  RETURNS text STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup';
+  RETURNS pglsn STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup';
 
 CREATE OR REPLACE FUNCTION
   json_populate_record(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 98a860e..f960113 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -141,8 +141,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
 		bool		active;
 		Oid			database;
 		const char *slot_name;
-
-		char		restart_lsn_s[MAXFNAMELEN];
 		int			i;
 
 		SpinLockAcquire(&slot->mutex);
@@ -164,9 +162,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
 
 		memset(nulls, 0, sizeof(nulls));
 
-		snprintf(restart_lsn_s, sizeof(restart_lsn_s), "%X/%X",
-				 (uint32) (restart_lsn >> 32), (uint32) restart_lsn);
-
 		i = 0;
 		values[i++] = CStringGetTextDatum(slot_name);
 		if (database == InvalidOid)
@@ -180,7 +175,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
 		else
 			nulls[i++] = true;
 		if (restart_lsn != InvalidTransactionId)
-			values[i++] = CStringGetTextDatum(restart_lsn_s);
+			values[i++] = PGLSNGetDatum(restart_lsn);
 		else
 			nulls[i++] = true;
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ece2692..feec455 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2971,28 +2971,28 @@ DATA(insert OID = 2171 ( pg_cancel_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v
 DESCR("cancel a server process' current query");
 DATA(insert OID = 2096 ( pg_terminate_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 16 "23" _null_ _null_ _null_ _null_ pg_terminate_backend _null_ _null_ _null_ ));
 DESCR("terminate a server process");
-DATA(insert OID = 2172 ( pg_start_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 25 "25 16" _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ ));
+DATA(insert OID = 2172 ( pg_start_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 3220 "25 16" _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ ));
 DESCR("prepare for taking an online backup");
-DATA(insert OID = 2173 ( pg_stop_backup			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ ));
+DATA(insert OID = 2173 ( pg_stop_backup			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ ));
 DESCR("finish taking an online backup");
 DATA(insert OID = 3813 ( pg_is_in_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_in_backup _null_ _null_ _null_ ));
 DESCR("true if server is in online backup");
 DATA(insert OID = 3814 ( pg_backup_start_time		PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 1184 "" _null_ _null_ _null_ _null_ pg_backup_start_time _null_ _null_ _null_ ));
 DESCR("start time of an online backup");
-DATA(insert OID = 2848 ( pg_switch_xlog			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_switch_xlog _null_ _null_ _null_ ));
+DATA(insert OID = 2848 ( pg_switch_xlog			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_switch_xlog _null_ _null_ _null_ ));
 DESCR("switch to new xlog file");
-DATA(insert OID = 3098 ( pg_create_restore_point	PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_create_restore_point _null_ _null_ _null_ ));
+DATA(insert OID = 3098 ( pg_create_restore_point	PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 3220 "25" _null_ _null_ _null_ _null_ pg_create_restore_point _null_ _null_ _null_ ));
 DESCR("create a named restore point");
-DATA(insert OID = 2849 ( pg_current_xlog_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_location _null_ _null_ _null_ ));
+DATA(insert OID = 2849 ( pg_current_xlog_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_current_xlog_location _null_ _null_ _null_ ));
 DESCR("current xlog write location");
-DATA(insert OID = 2852 ( pg_current_xlog_insert_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_insert_location _null_ _null_ _null_ ));
+DATA(insert OID = 2852 ( pg_current_xlog_insert_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_current_xlog_insert_location _null_ _null_ _null_ ));
 DESCR("current xlog insert location");
-DATA(insert OID = 2850 ( pg_xlogfile_name_offset	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2249 "25" "{25,25,23}" "{i,o,o}" "{wal_location,file_name,file_offset}" _null_ pg_xlogfile_name_offset _null_ _null_ _null_ ));
+DATA(insert OID = 2850 ( pg_xlogfile_name_offset	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2249 "3220" "{3220,25,23}" "{i,o,o}" "{wal_location,file_name,file_offset}" _null_ pg_xlogfile_name_offset _null_ _null_ _null_ ));
 DESCR("xlog filename and byte offset, given an xlog location");
-DATA(insert OID = 2851 ( pg_xlogfile_name			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "25" _null_ _null_ _null_ _null_ pg_xlogfile_name _null_ _null_ _null_ ));
+DATA(insert OID = 2851 ( pg_xlogfile_name			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3220" _null_ _null_ _null_ _null_ pg_xlogfile_name _null_ _null_ _null_ ));
 DESCR("xlog filename, given an xlog location");
 
-DATA(insert OID = 3165 ( pg_xlog_location_diff		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "25 25" _null_ _null_ _null_ _null_ pg_xlog_location_diff _null_ _null_ _null_ ));
+DATA(insert OID = 3165 ( pg_xlog_location_diff		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "3220 3220" _null_ _null_ _null_ _null_ pg_xlog_location_diff _null_ _null_ _null_ ));
 DESCR("difference in bytes, given two xlog locations");
 
 DATA(insert OID = 3809 ( pg_export_snapshot		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_export_snapshot _null_ _null_ _null_ ));
@@ -3001,9 +3001,9 @@ DESCR("export a snapshot");
 DATA(insert OID = 3810 (  pg_is_in_recovery		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_in_recovery _null_ _null_ _null_ ));
 DESCR("true if server is in recovery");
 
-DATA(insert OID = 3820 ( pg_last_xlog_receive_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_last_xlog_receive_location _null_ _null_ _null_ ));
+DATA(insert OID = 3820 ( pg_last_xlog_receive_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_last_xlog_receive_location _null_ _null_ _null_ ));
 DESCR("current xlog flush location");
-DATA(insert OID = 3821 ( pg_last_xlog_replay_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_last_xlog_replay_location _null_ _null_ _null_ ));
+DATA(insert OID = 3821 ( pg_last_xlog_replay_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_last_xlog_replay_location _null_ _null_ _null_ ));
 DESCR("last xlog replay location");
 DATA(insert OID = 3830 ( pg_last_xact_replay_timestamp	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 1184 "" _null_ _null_ _null_ _null_ pg_last_xact_replay_timestamp _null_ _null_ _null_ ));
 DESCR("timestamp of last replay xact");
@@ -4800,11 +4800,11 @@ DATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0
 DESCR("SP-GiST support for quad tree over range");
 
 /* replication slots */
-DATA(insert OID = 3779 (  pg_create_physical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2249 "19" "{19,25,25}" "{i,o,o}" "{slotname,slotname,xlog_position}" _null_ pg_create_physical_replication_slot _null_ _null_ _null_ ));
+DATA(insert OID = 3779 (  pg_create_physical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2249 "19" "{19,25,3220}" "{i,o,o}" "{slotname,slotname,xlog_position}" _null_ pg_create_physical_replication_slot _null_ _null_ _null_ ));
 DESCR("create a physical replication slot");
 DATA(insert OID = 3780 (  pg_drop_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2278 "19" _null_ _null_ _null_ _null_ pg_drop_replication_slot _null_ _null_ _null_ ));
 DESCR("drop a replication slot");
-DATA(insert OID = 3781 (  pg_get_replication_slots	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{25,25,26,16,28,25}" "{o,o,o,o,o,o}" "{slot_name,slot_type,datoid,active,xmin,restart_lsn}" _null_ pg_get_replication_slots _null_ _null_ _null_ ));
+DATA(insert OID = 3781 (  pg_get_replication_slots	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{25,25,26,16,28,3220}" "{o,o,o,o,o,o}" "{slot_name,slot_type,datoid,active,xmin,restart_lsn}" _null_ pg_get_replication_slots _null_ _null_ _null_ ));
 DESCR("information about replication slots currently in use");
 
 /* event triggers */
#25Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#24)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Feb 5, 2014 at 9:38 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Wed, Feb 5, 2014 at 8:59 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

I'll update the patches according to that.

Here are the updated patches with the following changes (according to
previous comments):
- Datatype is renamed to pglsn, documentation, file names, regressions
and APIs are updated as well.
- The DatumGet* and *GetDatum APIs are renamed with PGLSN (Should be
PgLsn? But that's a detail)
- pg_create_physical_replication_slot uses PGLSNGetDatum for its 6th argument
For pageinspect, only page_header is impacted and I think that this
should be a separated patch as it makes necessary to dump it to 1.2. I
can write it later once the core parts are decided.

I just forgot to mention that the 2nd patch does not use context diffs
but git diffs because of filterdiff not able to catch all the new
content of slotfuncs.c.
Regards,
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Robert Haas
robertmhaas@gmail.com
In reply to: Peter Eisentraut (#22)
Re: should we add a XLogRecPtr/LSN SQL type?

On Tue, Feb 4, 2014 at 3:26 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

Perhaps this type should be called pglsn, since it's an
implementation-specific detail and not a universal concept like int,
point, or uuid.

If we're going to do that, I suggest pg_lsn rather than pglsn. We
already have pg_node_tree, so using underscores for separation would
be more consistent.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Peter Eisentraut
peter_e@gmx.net
In reply to: Robert Haas (#26)
Re: should we add a XLogRecPtr/LSN SQL type?

On 2/5/14, 1:31 PM, Robert Haas wrote:

On Tue, Feb 4, 2014 at 3:26 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

Perhaps this type should be called pglsn, since it's an
implementation-specific detail and not a universal concept like int,
point, or uuid.

If we're going to do that, I suggest pg_lsn rather than pglsn. We
already have pg_node_tree, so using underscores for separation would
be more consistent.

Yes, that's a good precedent in multiple ways.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28Michael Paquier
michael.paquier@gmail.com
In reply to: Peter Eisentraut (#27)
2 attachment(s)
Re: should we add a XLogRecPtr/LSN SQL type?

On Thu, Feb 6, 2014 at 3:48 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/5/14, 1:31 PM, Robert Haas wrote:

On Tue, Feb 4, 2014 at 3:26 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

Perhaps this type should be called pglsn, since it's an
implementation-specific detail and not a universal concept like int,
point, or uuid.

If we're going to do that, I suggest pg_lsn rather than pglsn. We
already have pg_node_tree, so using underscores for separation would
be more consistent.

Yes, that's a good precedent in multiple ways.

Here are updated patches to use pg_lsn instead of pglsn...
--
Michael

Attachments:

20140206_lsn_datatype_v4.patchtext/x-diff; charset=US-ASCII; name=20140206_lsn_datatype_v4.patchDownload
*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
***************
*** 181,186 ****
--- 181,192 ----
        </row>
  
        <row>
+        <entry><type>pg_lsn</type></entry>
+        <entry></entry>
+        <entry><productname>PostgreSQL</productname> Log Sequence Number</entry>
+       </row>
+ 
+       <row>
         <entry><type>point</type></entry>
         <entry></entry>
         <entry>geometric point on a plane</entry>
***************
*** 4502,4507 **** SELECT * FROM pg_attribute
--- 4508,4528 ----
     </para>
    </sect1>
  
+   <sect1 id="datatype-pg-lsn">
+    <title><acronym>PG_LSN Type</title>
+ 
+    <indexterm zone="datatype-pg-lsn">
+     <primary>PG_LSN</primary>
+    </indexterm>
+ 
+    <para>
+     The <type>pg_lsn</type> data type can be used to store LSN (Log Sequence
+     Number) data which is a pointer to a location in the XLOG. This type is a
+     representation of XLogRecPtr and an internal system type of
+     <productname>PostgreSQL</productname>.
+    </para>
+   </sect1>
+ 
    <sect1 id="datatype-pseudo">
     <title>Pseudo-Types</title>
  
*** a/src/backend/utils/adt/Makefile
--- b/src/backend/utils/adt/Makefile
***************
*** 21,27 **** OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
  	enum.o float.o format_type.o \
  	geo_ops.o geo_selfuncs.o int.o int8.o json.o jsonfuncs.o like.o \
  	lockfuncs.o misc.o nabstime.o name.o numeric.o numutils.o \
! 	oid.o oracle_compat.o orderedsetaggs.o \
  	pseudotypes.o rangetypes.o rangetypes_gist.o \
  	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
  	tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
--- 21,27 ----
  	enum.o float.o format_type.o \
  	geo_ops.o geo_selfuncs.o int.o int8.o json.o jsonfuncs.o like.o \
  	lockfuncs.o misc.o nabstime.o name.o numeric.o numutils.o \
! 	oid.o oracle_compat.o orderedsetaggs.o pg_lsn.o \
  	pseudotypes.o rangetypes.o rangetypes_gist.o \
  	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
  	tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
*** /dev/null
--- b/src/backend/utils/adt/pg_lsn.c
***************
*** 0 ****
--- 1,180 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_lsn.c
+  *	  Internal PostgreSQL LSN operations
+  *
+  * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/adt/pg_lsn.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "funcapi.h"
+ #include "libpq/pqformat.h"
+ #include "utils/builtins.h"
+ #include "utils/pg_lsn.h"
+ 
+ #define MAXPG_LSNLEN			17
+ #define MAXPG_LSNCOMPONENT	8
+ 
+ /*----------------------------------------------------------
+  * Formatting and conversion routines.
+  *---------------------------------------------------------*/
+ 
+ Datum
+ pg_lsn_in(PG_FUNCTION_ARGS)
+ {
+ 	char	   *str = PG_GETARG_CSTRING(0);
+ 	int			len1, len2;
+ 	uint32		id, off;
+ 	XLogRecPtr	result;
+ 
+ 	/* Sanity check input format. */
+ 	len1 = strspn(str, "0123456789abcdefABCDEF");
+ 	if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || str[len1] != '/')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ 				 errmsg("invalid input syntax for transaction log location: \"%s\"", str)));
+ 	len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
+ 	if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ 				 errmsg("invalid input syntax for transaction log location: \"%s\"", str)));
+ 
+ 	/* Decode result. */
+ 	id = (uint32) strtoul(str, NULL, 16);
+ 	off = (uint32) strtoul(str + len1 + 1, NULL, 16);
+ 	result = (XLogRecPtr) ((uint64) id << 32) | off;
+ 
+ 	PG_RETURN_PG_LSN(result);
+ }
+ 
+ Datum
+ pg_lsn_out(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr	lsn = (XLogRecPtr) PG_GETARG_PG_LSN(0);
+ 	char		buf[MAXPG_LSNLEN + 1];
+ 	char	   *result;
+ 	uint32		id, off;
+ 
+ 	/* Decode ID and offset */
+ 	id = (uint32) (lsn >> 32);
+ 	off = (uint32) lsn;
+ 
+ 	sprintf(buf, "%X/%X", id, off);
+ 	result = pstrdup(buf);
+ 	PG_RETURN_CSTRING(result);
+ }
+ 
+ Datum
+ pg_lsn_recv(PG_FUNCTION_ARGS)
+ {
+ 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+ 	XLogRecPtr	result;
+ 
+ 	result = pq_getmsgint64(buf);
+ 	PG_RETURN_PG_LSN(result);
+ }
+ 
+ Datum
+ pg_lsn_send(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn = (XLogRecPtr) PG_GETARG_PG_LSN(0);
+ 	StringInfoData buf;
+ 
+ 	pq_begintypsend(&buf);
+ 	pq_sendint64(&buf, lsn);
+ 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+ }
+ 
+ 
+ /*----------------------------------------------------------
+  *	Operators for PostgreSQL LSNs
+  *---------------------------------------------------------*/
+ 
+ Datum
+ pg_lsn_eq(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 == lsn2);
+ }
+ 
+ Datum
+ pg_lsn_ne(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 != lsn2);
+ }
+ 
+ Datum
+ pg_lsn_lt(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 < lsn2);
+ }
+ 
+ Datum
+ pg_lsn_gt(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 > lsn2);
+ }
+ 
+ Datum
+ pg_lsn_le(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 <= lsn2);
+ }
+ 
+ Datum
+ pg_lsn_ge(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PG_LSN(1);
+ 
+ 	PG_RETURN_BOOL(lsn1 >= lsn2);
+ }
+ 
+ 
+ /*----------------------------------------------------------
+  *	Arithmetic operators on PostgreSQL LSNs.
+  *---------------------------------------------------------*/
+ 
+ Datum
+ pg_lsn_mi(PG_FUNCTION_ARGS)
+ {
+ 	XLogRecPtr lsn1 = (XLogRecPtr) PG_GETARG_PG_LSN(0);
+ 	XLogRecPtr lsn2 = (XLogRecPtr) PG_GETARG_PG_LSN(1);
+ 	char		buf[256];
+ 	Datum		result;
+ 
+ 	/* Negative results are not allowed. */
+ 	if (lsn1 < lsn2)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ 				 errmsg("transaction log location out of range")));
+ 
+ 	/* Convert to numeric. */
+ 	sprintf(buf, UINT64_FORMAT, lsn1 - lsn2);
+ 	result = DirectFunctionCall3(numeric_in,
+ 								 CStringGetDatum(buf),
+ 								 ObjectIdGetDatum(0),
+ 								 Int32GetDatum(-1));
+ 
+ 	return result;
+ }
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
***************
*** 1592,1597 **** DESCR("less than or equal");
--- 1592,1613 ----
  DATA(insert OID = 2977 (  ">="	   PGNSP PGUID b f f 2950 2950 16 2976 2974 uuid_ge scalargtsel scalargtjoinsel ));
  DESCR("greater than or equal");
  
+ /* pg_lsn operators */
+ DATA(insert OID = 3222 (  "="	   PGNSP PGUID b f f 3220 3220 16 3222 3223 pg_lsn_eq eqsel eqjoinsel ));
+ DESCR("equal");
+ DATA(insert OID = 3223 (  "<>"	   PGNSP PGUID b f f 3220 3220 16 3223 3222 pg_lsn_ne neqsel neqjoinsel ));
+ DESCR("not equal");
+ DATA(insert OID = 3224 (  "<"	   PGNSP PGUID b f f 3220 3220 16 3225 3227 pg_lsn_lt scalarltsel scalarltjoinsel ));
+ DESCR("less than");
+ DATA(insert OID = 3225 (  ">"	   PGNSP PGUID b f f 3220 3220 16 3224 3226 pg_lsn_gt scalargtsel scalargtjoinsel ));
+ DESCR("greater than");
+ DATA(insert OID = 3226 (  "<="	   PGNSP PGUID b f f 3220 3220 16 3227 3225 pg_lsn_le scalarltsel scalarltjoinsel ));
+ DESCR("less than or equal");
+ DATA(insert OID = 3227 (  ">="	   PGNSP PGUID b f f 3220 3220 16 3226 3224 pg_lsn_ge scalargtsel scalargtjoinsel ));
+ DESCR("greater than or equal");
+ DATA(insert OID = 3228 (  "-"	   PGNSP PGUID b f f 3220 3220 1700    0	0 pg_lsn_mi - - ));
+ DESCR("minus");
+ 
  /* enum operators */
  DATA(insert OID = 3516 (  "="	   PGNSP PGUID b t t 3500 3500 16 3516 3517 enum_eq eqsel eqjoinsel ));
  DESCR("equal");
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4212,4217 **** DESCR("I/O");
--- 4212,4234 ----
  DATA(insert OID = 2963 (  uuid_hash		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "2950" _null_ _null_ _null_ _null_ uuid_hash _null_ _null_ _null_ ));
  DESCR("hash");
  
+ /* pg_lsn */
+ DATA(insert OID = 3229 (  pg_lsn_in		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3220 "2275" _null_ _null_ _null_ _null_ pg_lsn_in _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3230 (  pg_lsn_out		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3220" _null_ _null_ _null_ _null_ pg_lsn_out _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3231 (  pg_lsn_lt		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pg_lsn_lt _null_ _null_ _null_ ));
+ DATA(insert OID = 3232 (  pg_lsn_le		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pg_lsn_le _null_ _null_ _null_ ));
+ DATA(insert OID = 3233 (  pg_lsn_eq		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pg_lsn_eq _null_ _null_ _null_ ));
+ DATA(insert OID = 3234 (  pg_lsn_ge		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pg_lsn_ge _null_ _null_ _null_ ));
+ DATA(insert OID = 3235 (  pg_lsn_gt		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pg_lsn_gt _null_ _null_ _null_ ));
+ DATA(insert OID = 3236 (  pg_lsn_ne		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3220 3220" _null_ _null_ _null_ _null_ pg_lsn_ne _null_ _null_ _null_ ));
+ DATA(insert OID = 3237 (  pg_lsn_mi		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "3220 3220" _null_ _null_ _null_ _null_ pg_lsn_mi _null_ _null_ _null_ ));
+ DATA(insert OID = 3238 (  pg_lsn_recv	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3220 "2281" _null_ _null_ _null_ _null_ pg_lsn_recv _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3239 (  pg_lsn_send	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3220" _null_ _null_ _null_ _null_ pg_lsn_send _null_ _null_ _null_ ));
+ DESCR("I/O");
+ 
  /* enum related procs */
  DATA(insert OID = 3504 (  anyenum_in	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ ));
  DESCR("I/O");
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 577,582 **** DESCR("UUID datatype");
--- 577,587 ----
  #define UUIDOID 2950
  DATA(insert OID = 2951 ( _uuid			PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
  
+ /* pg_lsn */
+ DATA(insert OID = 3220 ( pg_lsn			PGNSP PGUID 8 t b U t t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+ DESCR("PostgreSQL LSN datatype");
+ DATA(insert OID = 3221 ( _pg_lsn			PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+ 
  /* text search */
  DATA(insert OID = 3614 ( tsvector		PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
  DESCR("text representation for text search");
*** a/src/include/fmgr.h
--- b/src/include/fmgr.h
***************
*** 230,235 **** extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
--- 230,236 ----
  #define PG_GETARG_CHAR(n)	 DatumGetChar(PG_GETARG_DATUM(n))
  #define PG_GETARG_BOOL(n)	 DatumGetBool(PG_GETARG_DATUM(n))
  #define PG_GETARG_OID(n)	 DatumGetObjectId(PG_GETARG_DATUM(n))
+ #define PG_GETARG_PG_LSN(n)	 DatumGetPgLsn(PG_GETARG_DATUM(n))
  #define PG_GETARG_POINTER(n) DatumGetPointer(PG_GETARG_DATUM(n))
  #define PG_GETARG_CSTRING(n) DatumGetCString(PG_GETARG_DATUM(n))
  #define PG_GETARG_NAME(n)	 DatumGetName(PG_GETARG_DATUM(n))
***************
*** 302,307 **** extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
--- 303,309 ----
  #define PG_RETURN_CHAR(x)	 return CharGetDatum(x)
  #define PG_RETURN_BOOL(x)	 return BoolGetDatum(x)
  #define PG_RETURN_OID(x)	 return ObjectIdGetDatum(x)
+ #define PG_RETURN_PG_LSN(x)	 return PgLsnGetDatum(x)
  #define PG_RETURN_POINTER(x) return PointerGetDatum(x)
  #define PG_RETURN_CSTRING(x) return CStringGetDatum(x)
  #define PG_RETURN_NAME(x)	 return NameGetDatum(x)
*** a/src/include/postgres.h
--- b/src/include/postgres.h
***************
*** 484,489 **** typedef Datum *DatumPtr;
--- 484,503 ----
  #define ObjectIdGetDatum(X) ((Datum) SET_4_BYTES(X))
  
  /*
+  * DatumGetPgLsn
+  *		Returns PostgreSQL log sequence number of a datum.
+  */
+ 
+ #define DatumGetPgLsn(X) ((XLogRecPtr) GET_8_BYTES(X))
+ 
+ /*
+  * PG_LSNGetDatum
+  *		Returns datum representation for a PostgreSQL log sequence number.
+  */
+ 
+ #define PgLsnGetDatum(X) ((Datum) SET_8_BYTES(X))
+ 
+ /*
   * DatumGetTransactionId
   *		Returns transaction identifier value of a datum.
   */
*** /dev/null
--- b/src/include/utils/pg_lsn.h
***************
*** 0 ****
--- 1,34 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_lsn.h
+  *		Declarations for operations on log sequence numbers (LSNs) of
+  *		PostgreSQL.
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/utils/pg_lsn.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef PG_LSN_H
+ #define PG_LSN_H
+ 
+ #include "fmgr.h"
+ 
+ extern Datum pg_lsn_in(PG_FUNCTION_ARGS);
+ extern Datum pg_lsn_out(PG_FUNCTION_ARGS);
+ extern Datum pg_lsn_recv(PG_FUNCTION_ARGS);
+ extern Datum pg_lsn_send(PG_FUNCTION_ARGS);
+ 
+ extern Datum pg_lsn_eq(PG_FUNCTION_ARGS);
+ extern Datum pg_lsn_ne(PG_FUNCTION_ARGS);
+ extern Datum pg_lsn_lt(PG_FUNCTION_ARGS);
+ extern Datum pg_lsn_gt(PG_FUNCTION_ARGS);
+ extern Datum pg_lsn_le(PG_FUNCTION_ARGS);
+ extern Datum pg_lsn_ge(PG_FUNCTION_ARGS);
+ 
+ extern Datum pg_lsn_mi(PG_FUNCTION_ARGS);
+ 
+ #endif   /* PG_LSN_H */
*** /dev/null
--- b/src/test/regress/expected/pg_lsn.out
***************
*** 0 ****
--- 1,62 ----
+ --
+ -- PG_LSN
+ --
+ CREATE TABLE PG_LSN_TBL (f1 pg_lsn);
+ -- Largest and smallest input
+ INSERT INTO PG_LSN_TBL VALUES ('0/0');
+ INSERT INTO PG_LSN_TBL VALUES ('FFFFFFFF/FFFFFFFF');
+ -- Incorrect input
+ INSERT INTO PG_LSN_TBL VALUES ('G/0');
+ ERROR:  invalid input syntax for transaction log location: "G/0"
+ LINE 1: INSERT INTO PG_LSN_TBL VALUES ('G/0');
+                                       ^
+ INSERT INTO PG_LSN_TBL VALUES ('-1/0');
+ ERROR:  invalid input syntax for transaction log location: "-1/0"
+ LINE 1: INSERT INTO PG_LSN_TBL VALUES ('-1/0');
+                                       ^
+ INSERT INTO PG_LSN_TBL VALUES (' 0/12345678');
+ ERROR:  invalid input syntax for transaction log location: " 0/12345678"
+ LINE 1: INSERT INTO PG_LSN_TBL VALUES (' 0/12345678');
+                                       ^
+ INSERT INTO PG_LSN_TBL VALUES ('ABCD/');
+ ERROR:  invalid input syntax for transaction log location: "ABCD/"
+ LINE 1: INSERT INTO PG_LSN_TBL VALUES ('ABCD/');
+                                       ^
+ INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
+ ERROR:  invalid input syntax for transaction log location: "/ABCD"
+ LINE 1: INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
+                                       ^
+ DROP TABLE PG_LSN_TBL;
+ -- Operators
+ SELECT pg_lsn_eq('0/16AE7F8', '0/16AE7F8'); -- true
+  pg_lsn_eq 
+ ----------
+  t
+ (1 row)
+ 
+ SELECT pg_lsn_ne('0/16AE7F8', '0/16AE7F7'); -- true
+  pg_lsn_ne 
+ ----------
+  t
+ (1 row)
+ 
+ SELECT pg_lsn_lt('0/16AE7F7', '0/16AE7F8'); -- true
+  pg_lsn_lt 
+ ----------
+  t
+ (1 row)
+ 
+ SELECT pg_lsn_gt('0/16AE7F8', '0/16AE7F7'); -- true
+  pg_lsn_gt 
+ ----------
+  t
+ (1 row)
+ 
+ SELECT pg_lsn_mi('0/16AE7F7', '0/16AE7F8'); -- No negative results
+ ERROR:  transaction log location out of range
+ SELECT pg_lsn_mi('0/16AE7F8', '0/16AE7F7'); -- correct
+  pg_lsn_mi 
+ ----------
+         1
+ (1 row)
+ 
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 13,19 **** test: tablespace
  # ----------
  # The first group of parallel tests
  # ----------
! test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes
  
  # Depends on things setup during char, varchar and text
  test: strings
--- 13,19 ----
  # ----------
  # The first group of parallel tests
  # ----------
! test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes pg_lsn
  
  # Depends on things setup during char, varchar and text
  test: strings
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 19,24 **** test: uuid
--- 19,25 ----
  test: enum
  test: money
  test: rangetypes
+ test: pg_lsn
  test: strings
  test: numerology
  test: point
*** /dev/null
--- b/src/test/regress/sql/pg_lsn.sql
***************
*** 0 ****
--- 1,25 ----
+ --
+ -- PG_LSN
+ --
+ 
+ CREATE TABLE PG_LSN_TBL (f1 pg_lsn);
+ 
+ -- Largest and smallest input
+ INSERT INTO PG_LSN_TBL VALUES ('0/0');
+ INSERT INTO PG_LSN_TBL VALUES ('FFFFFFFF/FFFFFFFF');
+ 
+ -- Incorrect input
+ INSERT INTO PG_LSN_TBL VALUES ('G/0');
+ INSERT INTO PG_LSN_TBL VALUES ('-1/0');
+ INSERT INTO PG_LSN_TBL VALUES (' 0/12345678');
+ INSERT INTO PG_LSN_TBL VALUES ('ABCD/');
+ INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
+ DROP TABLE PG_LSN_TBL;
+ 
+ -- Operators
+ SELECT pg_lsn_eq('0/16AE7F8', '0/16AE7F8'); -- true
+ SELECT pg_lsn_ne('0/16AE7F8', '0/16AE7F7'); -- true
+ SELECT pg_lsn_lt('0/16AE7F7', '0/16AE7F8'); -- true
+ SELECT pg_lsn_gt('0/16AE7F8', '0/16AE7F7'); -- true
+ SELECT pg_lsn_mi('0/16AE7F7', '0/16AE7F8'); -- No negative results
+ SELECT pg_lsn_mi('0/16AE7F8', '0/16AE7F7'); -- correct
20140206_lsn_func_cleanup_v3.patchtext/x-diff; charset=US-ASCII; name=20140206_lsn_func_cleanup_v3.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index dca24fc..908f947 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -5244,7 +5244,7 @@
 
      <row>
       <entry><structfield>restart_lsn</structfield></entry>
-      <entry><type>text</type></entry>
+      <entry><type>pg_lsn</type></entry>
       <entry></entry>
       <entry>The address (<literal>LSN</literal>) of oldest WAL which still
       might be required by the consumer of this slot and thus won't be
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index be548d7..a639662 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15884,35 +15884,35 @@ SELECT set_config('log_statement_stats', 'off', false);
        <entry>
         <literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pg_lsn</type></entry>
        <entry>Create a named point for performing restore (restricted to superusers)</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_current_xlog_insert_location()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pg_lsn</type></entry>
        <entry>Get current transaction log insert location</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_current_xlog_location()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pg_lsn</type></entry>
        <entry>Get current transaction log write location</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pg_lsn</type></entry>
        <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_stop_backup()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pg_lsn</type></entry>
        <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
       </row>
       <row>
@@ -15933,26 +15933,26 @@ SELECT set_config('log_statement_stats', 'off', false);
        <entry>
         <literal><function>pg_switch_xlog()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pg_lsn</type></entry>
        <entry>Force switch to a new transaction log file (restricted to superusers)</entry>
       </row>
       <row>
        <entry>
-        <literal><function>pg_xlogfile_name(<parameter>location</> <type>text</>)</function></literal>
+        <literal><function>pg_xlogfile_name(<parameter>location</> <type>pg_lsn</>)</function></literal>
         </entry>
        <entry><type>text</type></entry>
        <entry>Convert transaction log location string to file name</entry>
       </row>
       <row>
        <entry>
-        <literal><function>pg_xlogfile_name_offset(<parameter>location</> <type>text</>)</function></literal>
+        <literal><function>pg_xlogfile_name_offset(<parameter>location</> <type>pg_lsn</>)</function></literal>
         </entry>
        <entry><type>text</>, <type>integer</></entry>
        <entry>Convert transaction log location string to file name and decimal byte offset within file</entry>
       </row>
       <row>
        <entry>
-        <literal><function>pg_xlog_location_diff(<parameter>location</> <type>text</>, <parameter>location</> <type>text</>)</function></literal>
+        <literal><function>pg_xlog_location_diff(<parameter>location</> <type>pg_lsn</>, <parameter>location</> <type>pg_lsn</>)</function></literal>
        </entry>
        <entry><type>numeric</></entry>
        <entry>Calculate the difference between two transaction log locations</entry>
@@ -16107,7 +16107,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
        <entry>
         <literal><function>pg_last_xlog_receive_location()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pg_lsn</type></entry>
        <entry>Get last transaction log location received and synced to disk by
         streaming replication. While streaming replication is in progress
         this will increase monotonically. If recovery has completed this will
@@ -16121,7 +16121,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
        <entry>
         <literal><function>pg_last_xlog_replay_location()</function></literal>
         </entry>
-       <entry><type>text</type></entry>
+       <entry><type>pg_lsn</type></entry>
        <entry>Get last transaction log location replayed during recovery.
         If recovery is still in progress this will increase monotonically.
         If recovery has completed then this value will remain static at
@@ -16335,7 +16335,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_create_physical_replication_slot(<parameter>slotname</parameter> <type>text</type>, <parameter>plugin</parameter> <type>text</type>)</function></literal>
        </entry>
        <entry>
-        (<parameter>slotname</parameter> <type>text</type>, <parameter>xlog_position</parameter> <type>text</type>)
+        (<parameter>slotname</parameter> <type>text</type>, <parameter>xlog_position</parameter> <type>pg_lsn</type>)
        </entry>
        <entry>
         Creates a new physical replication slot named
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 9133179..058f9a2 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -33,8 +33,6 @@
 #include "utils/timestamp.h"
 #include "storage/fd.h"
 
-static void validate_xlog_location(char *str);
-
 
 /*
  * pg_start_backup: set up for taking an on-line backup dump
@@ -52,7 +50,6 @@ pg_start_backup(PG_FUNCTION_ARGS)
 	bool		fast = PG_GETARG_BOOL(1);
 	char	   *backupidstr;
 	XLogRecPtr	startpoint;
-	char		startxlogstr[MAXFNAMELEN];
 
 	backupidstr = text_to_cstring(backupid);
 
@@ -63,9 +60,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL);
 
-	snprintf(startxlogstr, sizeof(startxlogstr), "%X/%X",
-			 (uint32) (startpoint >> 32), (uint32) startpoint);
-	PG_RETURN_TEXT_P(cstring_to_text(startxlogstr));
+	PG_RETURN_PG_LSN(startpoint);
 }
 
 /*
@@ -85,7 +80,6 @@ Datum
 pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
-	char		stopxlogstr[MAXFNAMELEN];
 
 	if (!superuser() && !has_rolreplication(GetUserId()))
 		ereport(ERROR,
@@ -94,9 +88,7 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 
 	stoppoint = do_pg_stop_backup(NULL, true, NULL);
 
-	snprintf(stopxlogstr, sizeof(stopxlogstr), "%X/%X",
-			 (uint32) (stoppoint >> 32), (uint32) stoppoint);
-	PG_RETURN_TEXT_P(cstring_to_text(stopxlogstr));
+	PG_RETURN_PG_LSN(stoppoint);
 }
 
 /*
@@ -124,9 +116,7 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 	/*
 	 * As a convenience, return the WAL location of the switch record
 	 */
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (switchpoint >> 32), (uint32) switchpoint);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PG_LSN(switchpoint);
 }
 
 /*
@@ -169,9 +159,7 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	/*
 	 * As a convenience, return the WAL location of the restore point record
 	 */
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (restorepoint >> 32), (uint32) restorepoint);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PG_LSN(restorepoint);
 }
 
 /*
@@ -185,7 +173,6 @@ Datum
 pg_current_xlog_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
-	char		location[MAXFNAMELEN];
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -195,9 +182,7 @@ pg_current_xlog_location(PG_FUNCTION_ARGS)
 
 	current_recptr = GetXLogWriteRecPtr();
 
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (current_recptr >> 32), (uint32) current_recptr);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PG_LSN(current_recptr);
 }
 
 /*
@@ -209,7 +194,6 @@ Datum
 pg_current_xlog_insert_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
-	char		location[MAXFNAMELEN];
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -219,9 +203,7 @@ pg_current_xlog_insert_location(PG_FUNCTION_ARGS)
 
 	current_recptr = GetXLogInsertRecPtr();
 
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (current_recptr >> 32), (uint32) current_recptr);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PG_LSN(current_recptr);
 }
 
 /*
@@ -234,16 +216,13 @@ Datum
 pg_last_xlog_receive_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
-	char		location[MAXFNAMELEN];
 
 	recptr = GetWalRcvWriteRecPtr(NULL, NULL);
 
 	if (recptr == 0)
 		PG_RETURN_NULL();
 
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (recptr >> 32), (uint32) recptr);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PG_LSN(recptr);
 }
 
 /*
@@ -256,16 +235,13 @@ Datum
 pg_last_xlog_replay_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
-	char		location[MAXFNAMELEN];
 
 	recptr = GetXLogReplayRecPtr(NULL);
 
 	if (recptr == 0)
 		PG_RETURN_NULL();
 
-	snprintf(location, sizeof(location), "%X/%X",
-			 (uint32) (recptr >> 32), (uint32) recptr);
-	PG_RETURN_TEXT_P(cstring_to_text(location));
+	PG_RETURN_PG_LSN(recptr);
 }
 
 /*
@@ -279,13 +255,11 @@ pg_last_xlog_replay_location(PG_FUNCTION_ARGS)
 Datum
 pg_xlogfile_name_offset(PG_FUNCTION_ARGS)
 {
-	text	   *location = PG_GETARG_TEXT_P(0);
-	char	   *locationstr;
 	uint32		hi,
 				lo;
 	XLogSegNo	xlogsegno;
 	uint32		xrecoff;
-	XLogRecPtr	locationpoint;
+	XLogRecPtr	locationpoint = PG_GETARG_PG_LSN(0);
 	char		xlogfilename[MAXFNAMELEN];
 	Datum		values[2];
 	bool		isnull[2];
@@ -300,20 +274,6 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS)
 				 errhint("pg_xlogfile_name_offset() cannot be executed during recovery.")));
 
 	/*
-	 * Read input and parse
-	 */
-	locationstr = text_to_cstring(location);
-
-	validate_xlog_location(locationstr);
-
-	if (sscanf(locationstr, "%X/%X", &hi, &lo) != 2)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("could not parse transaction log location \"%s\"",
-						locationstr)));
-	locationpoint = ((uint64) hi) << 32 | lo;
-
-	/*
 	 * Construct a tuple descriptor for the result row.  This must match this
 	 * function's pg_proc entry!
 	 */
@@ -359,12 +319,10 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS)
 Datum
 pg_xlogfile_name(PG_FUNCTION_ARGS)
 {
-	text	   *location = PG_GETARG_TEXT_P(0);
-	char	   *locationstr;
 	uint32		hi,
 				lo;
 	XLogSegNo	xlogsegno;
-	XLogRecPtr	locationpoint;
+	XLogRecPtr	locationpoint = PG_GETARG_PG_LSN(0);
 	char		xlogfilename[MAXFNAMELEN];
 
 	if (RecoveryInProgress())
@@ -373,17 +331,6 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 				 errmsg("recovery is in progress"),
 		 errhint("pg_xlogfile_name() cannot be executed during recovery.")));
 
-	locationstr = text_to_cstring(location);
-
-	validate_xlog_location(locationstr);
-
-	if (sscanf(locationstr, "%X/%X", &hi, &lo) != 2)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("could not parse transaction log location \"%s\"",
-						locationstr)));
-	locationpoint = ((uint64) hi) << 32 | lo;
-
 	XLByteToPrevSeg(locationpoint, xlogsegno);
 	XLogFileName(xlogfilename, ThisTimeLineID, xlogsegno);
 
@@ -482,77 +429,27 @@ pg_is_in_recovery(PG_FUNCTION_ARGS)
 }
 
 /*
- * Validate the text form of a transaction log location.
- * (Just using sscanf() input allows incorrect values such as
- * negatives, so we have to be a bit more careful about that).
- */
-static void
-validate_xlog_location(char *str)
-{
-#define MAXLSNCOMPONENT		8
-
-	int			len1,
-				len2;
-
-	len1 = strspn(str, "0123456789abcdefABCDEF");
-	if (len1 < 1 || len1 > MAXLSNCOMPONENT || str[len1] != '/')
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for transaction log location: \"%s\"", str)));
-
-	len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
-	if (len2 < 1 || len2 > MAXLSNCOMPONENT || str[len1 + 1 + len2] != '\0')
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for transaction log location: \"%s\"", str)));
-}
-
-/*
  * Compute the difference in bytes between two WAL locations.
  */
 Datum
 pg_xlog_location_diff(PG_FUNCTION_ARGS)
 {
-	text	   *location1 = PG_GETARG_TEXT_P(0);
-	text	   *location2 = PG_GETARG_TEXT_P(1);
-	char	   *str1,
-			   *str2;
-	XLogRecPtr	loc1,
-				loc2;
+	XLogRecPtr	loc1 = PG_GETARG_PG_LSN(0);
+	XLogRecPtr	loc2 = PG_GETARG_PG_LSN(1);
 	Numeric		result;
 	uint64		bytes1,
 				bytes2;
 	uint32		hi,
 				lo;
 
-	/*
-	 * Read and parse input
-	 */
-	str1 = text_to_cstring(location1);
-	str2 = text_to_cstring(location2);
-
-	validate_xlog_location(str1);
-	validate_xlog_location(str2);
-
-	if (sscanf(str1, "%X/%X", &hi, &lo) != 2)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-		   errmsg("could not parse transaction log location \"%s\"", str1)));
-	loc1 = ((uint64) hi) << 32 | lo;
-
-	if (sscanf(str2, "%X/%X", &hi, &lo) != 2)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-		   errmsg("could not parse transaction log location \"%s\"", str2)));
-	loc2 = ((uint64) hi) << 32 | lo;
-
 	bytes1 = (uint64) loc1;
 	bytes2 = (uint64) loc2;
 
 	/*
 	 * result = bytes1 - bytes2.
 	 *
-	 * XXX: this won't handle values higher than 2^63 correctly.
+	 * XXX: this won't handle values higher than 2^63 correctly and can
+	 * return negative values.
 	 */
 	result = DatumGetNumeric(DirectFunctionCall2(numeric_sub,
 			DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) bytes1)),
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f02efec..a7c6a4e 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -810,7 +810,7 @@ COMMENT ON FUNCTION ts_debug(text) IS
 
 CREATE OR REPLACE FUNCTION
   pg_start_backup(label text, fast boolean DEFAULT false)
-  RETURNS text STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup';
+  RETURNS pg_lsn STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup';
 
 CREATE OR REPLACE FUNCTION
   json_populate_record(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 98a860e..fd26948 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -141,8 +141,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
 		bool		active;
 		Oid			database;
 		const char *slot_name;
-
-		char		restart_lsn_s[MAXFNAMELEN];
 		int			i;
 
 		SpinLockAcquire(&slot->mutex);
@@ -164,9 +162,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
 
 		memset(nulls, 0, sizeof(nulls));
 
-		snprintf(restart_lsn_s, sizeof(restart_lsn_s), "%X/%X",
-				 (uint32) (restart_lsn >> 32), (uint32) restart_lsn);
-
 		i = 0;
 		values[i++] = CStringGetTextDatum(slot_name);
 		if (database == InvalidOid)
@@ -180,7 +175,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
 		else
 			nulls[i++] = true;
 		if (restart_lsn != InvalidTransactionId)
-			values[i++] = CStringGetTextDatum(restart_lsn_s);
+			values[i++] = PgLsnGetDatum(restart_lsn);
 		else
 			nulls[i++] = true;
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ac72646..11c1e1a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2971,28 +2971,28 @@ DATA(insert OID = 2171 ( pg_cancel_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v
 DESCR("cancel a server process' current query");
 DATA(insert OID = 2096 ( pg_terminate_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 16 "23" _null_ _null_ _null_ _null_ pg_terminate_backend _null_ _null_ _null_ ));
 DESCR("terminate a server process");
-DATA(insert OID = 2172 ( pg_start_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 25 "25 16" _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ ));
+DATA(insert OID = 2172 ( pg_start_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 3220 "25 16" _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ ));
 DESCR("prepare for taking an online backup");
-DATA(insert OID = 2173 ( pg_stop_backup			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ ));
+DATA(insert OID = 2173 ( pg_stop_backup			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ ));
 DESCR("finish taking an online backup");
 DATA(insert OID = 3813 ( pg_is_in_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_in_backup _null_ _null_ _null_ ));
 DESCR("true if server is in online backup");
 DATA(insert OID = 3814 ( pg_backup_start_time		PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 1184 "" _null_ _null_ _null_ _null_ pg_backup_start_time _null_ _null_ _null_ ));
 DESCR("start time of an online backup");
-DATA(insert OID = 2848 ( pg_switch_xlog			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_switch_xlog _null_ _null_ _null_ ));
+DATA(insert OID = 2848 ( pg_switch_xlog			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_switch_xlog _null_ _null_ _null_ ));
 DESCR("switch to new xlog file");
-DATA(insert OID = 3098 ( pg_create_restore_point	PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_create_restore_point _null_ _null_ _null_ ));
+DATA(insert OID = 3098 ( pg_create_restore_point	PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 3220 "25" _null_ _null_ _null_ _null_ pg_create_restore_point _null_ _null_ _null_ ));
 DESCR("create a named restore point");
-DATA(insert OID = 2849 ( pg_current_xlog_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_location _null_ _null_ _null_ ));
+DATA(insert OID = 2849 ( pg_current_xlog_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_current_xlog_location _null_ _null_ _null_ ));
 DESCR("current xlog write location");
-DATA(insert OID = 2852 ( pg_current_xlog_insert_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_insert_location _null_ _null_ _null_ ));
+DATA(insert OID = 2852 ( pg_current_xlog_insert_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_current_xlog_insert_location _null_ _null_ _null_ ));
 DESCR("current xlog insert location");
-DATA(insert OID = 2850 ( pg_xlogfile_name_offset	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2249 "25" "{25,25,23}" "{i,o,o}" "{wal_location,file_name,file_offset}" _null_ pg_xlogfile_name_offset _null_ _null_ _null_ ));
+DATA(insert OID = 2850 ( pg_xlogfile_name_offset	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2249 "3220" "{3220,25,23}" "{i,o,o}" "{wal_location,file_name,file_offset}" _null_ pg_xlogfile_name_offset _null_ _null_ _null_ ));
 DESCR("xlog filename and byte offset, given an xlog location");
-DATA(insert OID = 2851 ( pg_xlogfile_name			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "25" _null_ _null_ _null_ _null_ pg_xlogfile_name _null_ _null_ _null_ ));
+DATA(insert OID = 2851 ( pg_xlogfile_name			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3220" _null_ _null_ _null_ _null_ pg_xlogfile_name _null_ _null_ _null_ ));
 DESCR("xlog filename, given an xlog location");
 
-DATA(insert OID = 3165 ( pg_xlog_location_diff		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "25 25" _null_ _null_ _null_ _null_ pg_xlog_location_diff _null_ _null_ _null_ ));
+DATA(insert OID = 3165 ( pg_xlog_location_diff		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "3220 3220" _null_ _null_ _null_ _null_ pg_xlog_location_diff _null_ _null_ _null_ ));
 DESCR("difference in bytes, given two xlog locations");
 
 DATA(insert OID = 3809 ( pg_export_snapshot		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_export_snapshot _null_ _null_ _null_ ));
@@ -3001,9 +3001,9 @@ DESCR("export a snapshot");
 DATA(insert OID = 3810 (  pg_is_in_recovery		PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_in_recovery _null_ _null_ _null_ ));
 DESCR("true if server is in recovery");
 
-DATA(insert OID = 3820 ( pg_last_xlog_receive_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_last_xlog_receive_location _null_ _null_ _null_ ));
+DATA(insert OID = 3820 ( pg_last_xlog_receive_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_last_xlog_receive_location _null_ _null_ _null_ ));
 DESCR("current xlog flush location");
-DATA(insert OID = 3821 ( pg_last_xlog_replay_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_last_xlog_replay_location _null_ _null_ _null_ ));
+DATA(insert OID = 3821 ( pg_last_xlog_replay_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_last_xlog_replay_location _null_ _null_ _null_ ));
 DESCR("last xlog replay location");
 DATA(insert OID = 3830 ( pg_last_xact_replay_timestamp	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 1184 "" _null_ _null_ _null_ _null_ pg_last_xact_replay_timestamp _null_ _null_ _null_ ));
 DESCR("timestamp of last replay xact");
@@ -4800,11 +4800,11 @@ DATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0
 DESCR("SP-GiST support for quad tree over range");
 
 /* replication slots */
-DATA(insert OID = 3779 (  pg_create_physical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2249 "19" "{19,25,25}" "{i,o,o}" "{slotname,slotname,xlog_position}" _null_ pg_create_physical_replication_slot _null_ _null_ _null_ ));
+DATA(insert OID = 3779 (  pg_create_physical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2249 "19" "{19,25,3220}" "{i,o,o}" "{slotname,slotname,xlog_position}" _null_ pg_create_physical_replication_slot _null_ _null_ _null_ ));
 DESCR("create a physical replication slot");
 DATA(insert OID = 3780 (  pg_drop_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2278 "19" _null_ _null_ _null_ _null_ pg_drop_replication_slot _null_ _null_ _null_ ));
 DESCR("drop a replication slot");
-DATA(insert OID = 3781 (  pg_get_replication_slots	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{25,25,26,16,28,25}" "{o,o,o,o,o,o}" "{slot_name,slot_type,datoid,active,xmin,restart_lsn}" _null_ pg_get_replication_slots _null_ _null_ _null_ ));
+DATA(insert OID = 3781 (  pg_get_replication_slots	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{25,25,26,16,28,3220}" "{o,o,o,o,o,o}" "{slot_name,slot_type,datoid,active,xmin,restart_lsn}" _null_ pg_get_replication_slots _null_ _null_ _null_ ));
 DESCR("information about replication slots currently in use");
 
 /* event triggers */
#29Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#28)
Re: should we add a XLogRecPtr/LSN SQL type?

On Thu, Feb 6, 2014 at 11:26 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Here are updated patches to use pg_lsn instead of pglsn...

Should I register this patch somewhere to avoid having it lost in the void?
Regards,
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#29)
Re: should we add a XLogRecPtr/LSN SQL type?

On Fri, Feb 14, 2014 at 9:32 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 6, 2014 at 11:26 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Here are updated patches to use pg_lsn instead of pglsn...

Should I register this patch somewhere to avoid having it lost in the void?
Regards,

Well, I committed this, but the buildfarm's deeply unhappy with it.
Apparently the use of GET_8_BYTES() and SET_8_BYTES() is no good on
some platforms... and I'm not sure what to do about that, right
off-hand.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Andres Freund
andres@2ndquadrant.com
In reply to: Robert Haas (#30)
Re: should we add a XLogRecPtr/LSN SQL type?

On 2014-02-19 09:24:03 -0500, Robert Haas wrote:

On Fri, Feb 14, 2014 at 9:32 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 6, 2014 at 11:26 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Here are updated patches to use pg_lsn instead of pglsn...

Should I register this patch somewhere to avoid having it lost in the void?
Regards,

Well, I committed this, but the buildfarm's deeply unhappy with it.
Apparently the use of GET_8_BYTES() and SET_8_BYTES() is no good on
some platforms... and I'm not sure what to do about that, right
off-hand.

The relevant bit probably is:

pg_lsn.c: In function 'pg_lsn_out':
pg_lsn.c:59:2: warning: implicit declaration of function 'GET_8_BYTES' [-Wimplicit-function-declaration]

GET_8_BYTES only exists for 64bit systems.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#32Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#31)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Feb 19, 2014 at 9:30 AM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-19 09:24:03 -0500, Robert Haas wrote:

On Fri, Feb 14, 2014 at 9:32 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 6, 2014 at 11:26 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Here are updated patches to use pg_lsn instead of pglsn...

Should I register this patch somewhere to avoid having it lost in the void?
Regards,

Well, I committed this, but the buildfarm's deeply unhappy with it.
Apparently the use of GET_8_BYTES() and SET_8_BYTES() is no good on
some platforms... and I'm not sure what to do about that, right
off-hand.

The relevant bit probably is:

pg_lsn.c: In function 'pg_lsn_out':
pg_lsn.c:59:2: warning: implicit declaration of function 'GET_8_BYTES' [-Wimplicit-function-declaration]

GET_8_BYTES only exists for 64bit systems.

Right, I got that far. So it looks like float8, int8, timestamp,
timestamptz, and money all have behavior contingent on
USE_FLOAT8_BYVAL, making that symbol a misnomer in every way. But
since we've already marched pretty far down that path I suppose we
should keep marching.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#33Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#32)
Re: should we add a XLogRecPtr/LSN SQL type?

Robert Haas <robertmhaas@gmail.com> writes:

On Wed, Feb 19, 2014 at 9:30 AM, Andres Freund <andres@2ndquadrant.com> wrote:

GET_8_BYTES only exists for 64bit systems.

Right, I got that far. So it looks like float8, int8, timestamp,
timestamptz, and money all have behavior contingent on
USE_FLOAT8_BYVAL, making that symbol a misnomer in every way. But
since we've already marched pretty far down that path I suppose we
should keep marching.

You need somebody to help you with getting that working on 32-bit
platforms? Because it needs to get fixed, or reverted, PDQ.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#33)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Feb 19, 2014 at 10:03 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

On Wed, Feb 19, 2014 at 9:30 AM, Andres Freund <andres@2ndquadrant.com> wrote:

GET_8_BYTES only exists for 64bit systems.

Right, I got that far. So it looks like float8, int8, timestamp,
timestamptz, and money all have behavior contingent on
USE_FLOAT8_BYVAL, making that symbol a misnomer in every way. But
since we've already marched pretty far down that path I suppose we
should keep marching.

You need somebody to help you with getting that working on 32-bit
platforms? Because it needs to get fixed, or reverted, PDQ.

Hopefully the commit I just pushed will fix it. It now works on my
machine with and without --disable-float8-byval.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Greg Stark
stark@mit.edu
In reply to: Robert Haas (#34)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Feb 19, 2014 at 3:11 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Hopefully the commit I just pushed will fix it. It now works on my
machine with and without --disable-float8-byval.

It builds and passes here on 32bits

--
greg

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36Robert Haas
robertmhaas@gmail.com
In reply to: Greg Stark (#35)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Feb 19, 2014 at 11:03 AM, Greg Stark <stark@mit.edu> wrote:

On Wed, Feb 19, 2014 at 3:11 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Hopefully the commit I just pushed will fix it. It now works on my
machine with and without --disable-float8-byval.

It builds and passes here on 32bits

The buildfarm seems happy so far, too.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#28)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Feb 5, 2014 at 9:26 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 6, 2014 at 3:48 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/5/14, 1:31 PM, Robert Haas wrote:

On Tue, Feb 4, 2014 at 3:26 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

Perhaps this type should be called pglsn, since it's an
implementation-specific detail and not a universal concept like int,
point, or uuid.

If we're going to do that, I suggest pg_lsn rather than pglsn. We
already have pg_node_tree, so using underscores for separation would
be more consistent.

Yes, that's a good precedent in multiple ways.

Here are updated patches to use pg_lsn instead of pglsn...

OK, so I think this stuff is all committed now, with assorted changes.
Thanks for your work on this.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#38Andres Freund
andres@2ndquadrant.com
In reply to: Robert Haas (#37)
Re: should we add a XLogRecPtr/LSN SQL type?

On 2014-02-19 12:47:40 -0500, Robert Haas wrote:

On Wed, Feb 5, 2014 at 9:26 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Yes, that's a good precedent in multiple ways.

Here are updated patches to use pg_lsn instead of pglsn...

OK, so I think this stuff is all committed now, with assorted changes.
Thanks for your work on this.

cool, thanks you two.

I wonder if pg_stat_replication shouldn't be updated to use it as well?
SELECT * FROM pg_attribute WHERE attname ~ '(location|lsn)'; only shows
that as names that are possible candidates for conversion.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#39Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#37)
Re: should we add a XLogRecPtr/LSN SQL type?

On Thu, Feb 20, 2014 at 2:47 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Feb 5, 2014 at 9:26 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 6, 2014 at 3:48 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/5/14, 1:31 PM, Robert Haas wrote:

On Tue, Feb 4, 2014 at 3:26 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

Perhaps this type should be called pglsn, since it's an
implementation-specific detail and not a universal concept like int,
point, or uuid.

If we're going to do that, I suggest pg_lsn rather than pglsn. We
already have pg_node_tree, so using underscores for separation would
be more consistent.

Yes, that's a good precedent in multiple ways.

Here are updated patches to use pg_lsn instead of pglsn...

OK, so I think this stuff is all committed now, with assorted changes.
Thanks for your work on this.

Thanks!
Oops, it looks like I am coming after the battle (time difference does
not help). I'll be more careful to test such patches on 32b platforms
as well in the future.
Regards,
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#39)
1 attachment(s)
Re: should we add a XLogRecPtr/LSN SQL type?

On Thu, Feb 20, 2014 at 9:43 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 20, 2014 at 2:47 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Feb 5, 2014 at 9:26 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 6, 2014 at 3:48 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/5/14, 1:31 PM, Robert Haas wrote:

On Tue, Feb 4, 2014 at 3:26 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

Perhaps this type should be called pglsn, since it's an
implementation-specific detail and not a universal concept like int,
point, or uuid.

If we're going to do that, I suggest pg_lsn rather than pglsn. We
already have pg_node_tree, so using underscores for separation would
be more consistent.

Yes, that's a good precedent in multiple ways.

Here are updated patches to use pg_lsn instead of pglsn...

OK, so I think this stuff is all committed now, with assorted changes.
Thanks for your work on this.

Thanks!
Oops, it looks like I am coming after the battle (time difference does
not help). I'll be more careful to test such patches on 32b platforms
as well in the future.

After re-reading the code, I found two incorrect comments in the new
regression tests. Patch fixing them is attached.
Thanks,
--
Michael

Attachments:

0001-pg_lsn_tiny_fixes.patchtext/x-patch; charset=US-ASCII; name=0001-pg_lsn_tiny_fixes.patchDownload
diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c
index e2b528a..fae12e1 100644
--- a/src/backend/utils/adt/pg_lsn.c
+++ b/src/backend/utils/adt/pg_lsn.c
@@ -19,7 +19,7 @@
 #include "utils/pg_lsn.h"
 
 #define MAXPG_LSNLEN			17
-#define MAXPG_LSNCOMPONENT	8
+#define MAXPG_LSNCOMPONENT		8
 
 /*----------------------------------------------------------
  * Formatting and conversion routines.
diff --git a/src/test/regress/expected/pg_lsn.out b/src/test/regress/expected/pg_lsn.out
index 01d2983..504768c 100644
--- a/src/test/regress/expected/pg_lsn.out
+++ b/src/test/regress/expected/pg_lsn.out
@@ -52,13 +52,13 @@ SELECT '0/16AE7F8' > pg_lsn '0/16AE7F7';
  t
 (1 row)
 
-SELECT '0/16AE7F7'::pg_lsn - '0/16AE7F8'::pg_lsn; -- No negative results
+SELECT '0/16AE7F7'::pg_lsn - '0/16AE7F8'::pg_lsn;
  ?column? 
 ----------
        -1
 (1 row)
 
-SELECT '0/16AE7F8'::pg_lsn - '0/16AE7F7'::pg_lsn; -- correct
+SELECT '0/16AE7F8'::pg_lsn - '0/16AE7F7'::pg_lsn;
  ?column? 
 ----------
         1
diff --git a/src/test/regress/sql/pg_lsn.sql b/src/test/regress/sql/pg_lsn.sql
index dddafb3..1634d37 100644
--- a/src/test/regress/sql/pg_lsn.sql
+++ b/src/test/regress/sql/pg_lsn.sql
@@ -21,5 +21,5 @@ SELECT '0/16AE7F8' = '0/16AE7F8'::pg_lsn;
 SELECT '0/16AE7F8'::pg_lsn != '0/16AE7F7';
 SELECT '0/16AE7F7' < '0/16AE7F8'::pg_lsn;
 SELECT '0/16AE7F8' > pg_lsn '0/16AE7F7';
-SELECT '0/16AE7F7'::pg_lsn - '0/16AE7F8'::pg_lsn; -- No negative results
-SELECT '0/16AE7F8'::pg_lsn - '0/16AE7F7'::pg_lsn; -- correct
+SELECT '0/16AE7F7'::pg_lsn - '0/16AE7F8'::pg_lsn;
+SELECT '0/16AE7F8'::pg_lsn - '0/16AE7F7'::pg_lsn;
#41Michael Paquier
michael.paquier@gmail.com
In reply to: Andres Freund (#38)
1 attachment(s)
Re: should we add a XLogRecPtr/LSN SQL type?

On Thu, Feb 20, 2014 at 8:55 AM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-19 12:47:40 -0500, Robert Haas wrote:

On Wed, Feb 5, 2014 at 9:26 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Yes, that's a good precedent in multiple ways.

Here are updated patches to use pg_lsn instead of pglsn...

OK, so I think this stuff is all committed now, with assorted changes.
Thanks for your work on this.

cool, thanks you two.

I wonder if pg_stat_replication shouldn't be updated to use it as well?
SELECT * FROM pg_attribute WHERE attname ~ '(location|lsn)'; only shows
that as names that are possible candidates for conversion.

I was sure to have forgotten some views or functions in the previous
patch... Please find attached a patch making pg_stat_replication use
pg_lsn instead of the existing text fields.
Regards,
--
Michael

Attachments:

0002-pg_lsn_for_replication.patchtext/x-patch; charset=US-ASCII; name=0002-pg_lsn_for_replication.patchDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index a37e6b6..370857a 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1490,24 +1490,24 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
     </row>
     <row>
      <entry><structfield>sent_location</></entry>
-     <entry><type>text</></entry>
+     <entry><type>pg_lsn</></entry>
      <entry>Last transaction log position sent on this connection</entry>
     </row>
     <row>
      <entry><structfield>write_location</></entry>
-     <entry><type>text</></entry>
+     <entry><type>pg_lsn</></entry>
      <entry>Last transaction log position written to disk by this standby
       server</entry>
     </row>
     <row>
      <entry><structfield>flush_location</></entry>
-     <entry><type>text</></entry>
+     <entry><type>pg_lsn</></entry>
      <entry>Last transaction log position flushed to disk by this standby
       server</entry>
     </row>
     <row>
      <entry><structfield>replay_location</></entry>
-     <entry><type>text</></entry>
+     <entry><type>pg_lsn</></entry>
      <entry>Last transaction log position replayed into the database on this
       standby server</entry>
     </row>
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 06b22e2..048367a 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -67,6 +67,7 @@
 #include "utils/builtins.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
+#include "utils/pg_lsn.h"
 #include "utils/ps_status.h"
 #include "utils/resowner.h"
 #include "utils/timeout.h"
@@ -2137,7 +2138,6 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 	{
 		/* use volatile pointer to prevent code rearrangement */
 		volatile WalSnd *walsnd = &WalSndCtl->walsnds[i];
-		char		location[MAXFNAMELEN];
 		XLogRecPtr	sentPtr;
 		XLogRecPtr	write;
 		XLogRecPtr	flush;
@@ -2171,28 +2171,19 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		else
 		{
 			values[1] = CStringGetTextDatum(WalSndGetStateString(state));
-
-			snprintf(location, sizeof(location), "%X/%X",
-					 (uint32) (sentPtr >> 32), (uint32) sentPtr);
-			values[2] = CStringGetTextDatum(location);
+			values[2] = LSNGetDatum(sentPtr);
 
 			if (write == 0)
 				nulls[3] = true;
-			snprintf(location, sizeof(location), "%X/%X",
-					 (uint32) (write >> 32), (uint32) write);
-			values[3] = CStringGetTextDatum(location);
+			values[3] = LSNGetDatum(write);
 
 			if (flush == 0)
 				nulls[4] = true;
-			snprintf(location, sizeof(location), "%X/%X",
-					 (uint32) (flush >> 32), (uint32) flush);
-			values[4] = CStringGetTextDatum(location);
+			values[4] = LSNGetDatum(flush);
 
 			if (apply == 0)
 				nulls[5] = true;
-			snprintf(location, sizeof(location), "%X/%X",
-					 (uint32) (apply >> 32), (uint32) apply);
-			values[5] = CStringGetTextDatum(location);
+			values[5] = LSNGetDatum(apply);
 
 			values[6] = Int32GetDatum(sync_priority[i]);
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 11c1e1a..a2cc19f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2634,7 +2634,7 @@ DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 0 0 f
 DESCR("statistics: currently active backend IDs");
 DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active backends");
-DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,25,25,25,25,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
+DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active replication");
 DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
 DESCR("statistics: current backend PID");
#42Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#40)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Feb 19, 2014 at 8:06 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 20, 2014 at 9:43 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 20, 2014 at 2:47 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Feb 5, 2014 at 9:26 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 6, 2014 at 3:48 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/5/14, 1:31 PM, Robert Haas wrote:

On Tue, Feb 4, 2014 at 3:26 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

Perhaps this type should be called pglsn, since it's an
implementation-specific detail and not a universal concept like int,
point, or uuid.

If we're going to do that, I suggest pg_lsn rather than pglsn. We
already have pg_node_tree, so using underscores for separation would
be more consistent.

Yes, that's a good precedent in multiple ways.

Here are updated patches to use pg_lsn instead of pglsn...

OK, so I think this stuff is all committed now, with assorted changes.
Thanks for your work on this.

Thanks!
Oops, it looks like I am coming after the battle (time difference does
not help). I'll be more careful to test such patches on 32b platforms
as well in the future.

After re-reading the code, I found two incorrect comments in the new
regression tests. Patch fixing them is attached.

Thanks, committed. But I left out the whitespace change you included.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#43Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#41)
Re: should we add a XLogRecPtr/LSN SQL type?

On Wed, Feb 19, 2014 at 8:27 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 20, 2014 at 8:55 AM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-19 12:47:40 -0500, Robert Haas wrote:

On Wed, Feb 5, 2014 at 9:26 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Yes, that's a good precedent in multiple ways.

Here are updated patches to use pg_lsn instead of pglsn...

OK, so I think this stuff is all committed now, with assorted changes.
Thanks for your work on this.

cool, thanks you two.

I wonder if pg_stat_replication shouldn't be updated to use it as well?
SELECT * FROM pg_attribute WHERE attname ~ '(location|lsn)'; only shows
that as names that are possible candidates for conversion.

I was sure to have forgotten some views or functions in the previous
patch... Please find attached a patch making pg_stat_replication use
pg_lsn instead of the existing text fields.
Regards,

Committed. Do we want to do anything about pageinspect?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#43)
Re: should we add a XLogRecPtr/LSN SQL type?

On Tue, Feb 25, 2014 at 12:41 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Feb 19, 2014 at 8:27 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 20, 2014 at 8:55 AM, Andres Freund <andres@2ndquadrant.com>

wrote:

On 2014-02-19 12:47:40 -0500, Robert Haas wrote:

On Wed, Feb 5, 2014 at 9:26 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Yes, that's a good precedent in multiple ways.

Here are updated patches to use pg_lsn instead of pglsn...

OK, so I think this stuff is all committed now, with assorted changes.
Thanks for your work on this.

cool, thanks you two.

I wonder if pg_stat_replication shouldn't be updated to use it as well?
SELECT * FROM pg_attribute WHERE attname ~ '(location|lsn)'; only shows
that as names that are possible candidates for conversion.

I was sure to have forgotten some views or functions in the previous
patch... Please find attached a patch making pg_stat_replication use
pg_lsn instead of the existing text fields.
Regards,

Committed. Do we want to do anything about pageinspect?

Thanks. We're dealing with that on another thread, I'll send an updated
patch there.
--
Michael