On Sun, Feb 2, 2014 at 12:07 AM, Andres Freund <and...@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 <t...@sss.pgh.pa.us> wrote: >> > Andres Freund <and...@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
*** 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
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers