After some further testing, there is a small change required to the new
SQL - it requires a LEFT OUTER JOIN rather than a simple join as some of
the sub-tables may be empty and that causes erroneous empty results.

I've re-created the patch sets which can be applied against the git master.

Jim



On 09/06/2016 18:19, NSS Ltd wrote:
> Oops, typo :
>
> alter table mailboxes add constraint uniq_guid unique (guid);
>
> (removed 'check' which is wrong syntax)
>
>
> On 09/06/2016 16:14, NSS Ltd wrote:
>> It looks like my earlier patch does address this and also adds the
>> x-guid to the status command as well.
>>
>> I have 2 patch files plus a necessary change to the database tables :
>>
>> -- add uuid extension into PostgreSQL database
>> CREATE EXTENSION "uuid-ossp";
>> -- alter mailboxes table to add a guid and set a unique constraint on it
>> alter table mailboxes add guid uuid not null default uuid_generate_v4();
>> alter table mailboxes add constraint uniq_guid check unique (guid);
>>
>> This will automatically create a random uuid for each mailbox and
>> enforce the uniqueness (which should be the default if the uuid
>> generation is any good).
>>
>> I've cleaned up my original patch set and removed some redundant debug
>> and fragments of other stuff I was testing.  This gives 2 patch files as
>> the code has 2 distinct parts.  They apply with some offsets against the
>> current master :
>>
>> Add uuid handling to the PostgreSQL code.  uuid.patch.  This could stand
>> by itself, though it's purpose is to lay the proper PostgreSQL handling
>> of uuid columns.
>>
>> Add x-guid handling to STATUS and LIST commands.  xguid.patch.  This
>> requires uuid.patch first.
>>
>> I have this running on a production server (again).  Lots of my users
>> are using clients which use the x-guid, so this is needed for general
>> use as far as I can tell.
>>
>> Jim
>>
>>
>>
>>
>> On 09/06/2016 14:59, NSS Ltd wrote:
>>> OK, I knew this was familiar.  I sent a patch in April 2015 but it's not
>>> merged (some others I sent for some different things are merged).
>>>
>>> I need to check the code to become familiar again, but is it possible to
>>> get this merged in?  It may need some cleanup, but I believe it was
>>> working on my custom build OK for the last year, so it should be good to
>>> merge in after review.
>>>
>>> Jim
>>>
>>>
>>>
>>>
>>> On 09/06/2016 14:29, NSS Ltd wrote:
>>>> Hi,
>>>>
>>>> I've just updated to the latest git master and see a number of back-off
>>>> due to imap syntax error messages.
>>>>
>>>> The offending command seems to be :
>>>>
>>>> list "" "*" return (status (x-guid) children)
>>>>
>>>> And the 'status' seems to be the problem.
>>>>
>>>> 114 BAD Unknown return option: status
>>>>
>>>> Before digging into this, is this a known issue/something being worked
>>>> on?  If it's open/new, can you point me to what may be required to
>>>> resolve (e.g. new code/changes required)?
>>>>
>>>> Thanks
>>>>
>>>> Jim
>>>>
>>>> (There's something familiar about this - I'm not sure if this is the
>>>> same issue I'd had a few years back.)
>>>>
>>>>
>>>>
>>>>
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30/125 First line: 114 list
>>>> "" "*" return (status (x-guid) children)
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30 IMAP::runCommands, 1 commands
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30/125 Execution time 0ms
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30/125 Finished
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30 IMAP::runCommands, 1 commands
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30/125 Retired
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30/125 114 BAD Unknown return
>>>> option: status
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30 IMAP::runCommands, 0 commands
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30 IMAP::runCommands, 0 commands
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30/126 IMAP Command: 115 
>>>> unselect
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30/126 First line: 115 unselect
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30 IMAP::runCommands, 1 commands
>>>> Jun  9 09:19:19 aox Archiveopteryx: 9236/2/30 Delaying next IMAP command
>>>> for 12 seconds (because of 12 syntax errors)
>>>>

diff -wur aox-with-uuid/imap/handlers/listext.cpp 
../aox/imap/handlers/listext.cpp
--- aox-with-uuid/imap/handlers/listext.cpp     2016-06-10 14:52:24.508560046 
-0400
+++ ../aox/imap/handlers/listext.cpp    2016-06-10 14:43:26.722093943 -0400
@@ -19,19 +19,35 @@
     : public Garbage
 {
 public:
+       class statusInfo : public Garbage {
+       public:
+               uint messages;
+               uint unseen;
+               uint recent;
+               int64 nextmodseq;
+               EString guid;
+               
+                statusInfo() :
+                       messages(0), unseen(0), recent(0), nextmodseq(0) {
+                                       // Empty
+               }
+       };
+
     ListextData():
-        selectQuery( 0 ), permissionsQuery( 0 ),
-        reference( 0 ),
+        selectQuery( 0 ), permissionsQuery( 0 ), statusQuery( 0 ),
+        reference( 0 ), mailboxStatus (0),
         state( 0 ),
         extended( false ),
         returnSubscribed( false ), returnChildren( false ),
         selectSubscribed( false ), selectRemote( false ),
-        selectRecursiveMatch( false )
+        selectRecursiveMatch( false ), returnStatus( false )
     {}
 
     Query * selectQuery;
     Query * permissionsQuery;
+       Query * statusQuery;
     Mailbox * reference;
+       statusInfo * mailboxStatus;
     EString referenceName;
     UStringList patterns;
     uint state;
@@ -64,12 +80,38 @@
     EString previousResponse;
     List<Response> responses;
 
+       // Status Selection Object
+       class statusSelection : public Garbage {
+               public:
+               bool getRecent;
+               bool getMessages;
+               bool getUidNext;
+               bool getUidValidity;
+               bool getUnseen;
+               bool getHighestModSeq;
+               bool getXGuid;
+               
+
+                       statusSelection() :
+                       getRecent(false), getMessages(false), getUidNext(false),
+                       getUidValidity(false), getUnseen (false), 
getHighestModSeq (false),
+                       getXGuid ( false ) {
+                       // Empty Constructor
+               }
+       };
+
+       statusSelection statusRequired;
+       
+
+       
+
     bool extended;
     bool returnSubscribed;
     bool returnChildren;
     bool selectSubscribed;
     bool selectRemote;
     bool selectRecursiveMatch;
+       bool returnStatus;
 };
 
 
@@ -179,7 +221,7 @@
         uint bn = 1;
         EString sel;
         if ( d->selectSubscribed && d->selectRecursiveMatch ) {
-            sel = "select mb.id, mb.name, s.id as sid, "
+            sel = "with q1 as (select mb.id, mb.name, s.id as sid, "
                   "exists(select cmb.id from mailboxes cmb "
                   "join subscriptions cs on"
                   " (cmb.id=cs.mailbox and cs.owner=$1) "
@@ -194,14 +236,14 @@
             bn = 2;
         }
         else if ( d->selectSubscribed ) {
-            sel = "select mb.id, mb.name, s.id as sid from mailboxes mb "
+            sel = "with q1 as (select mb.id, mb.name, s.id as sid from 
mailboxes mb "
                   "join subscriptions s on (mb.id=s.mailbox and s.owner=$1) "
                   "where ";
             d->selectQuery->bind( 1, imap()->user()->id() );
             bn = 2;
         }
         else {
-            sel = "select mb.id, mb.name";
+            sel = "with q1 as (select mb.id, mb.name";
             if ( d->returnSubscribed ) {
                 sel.append( ", s.id as sid from mailboxes mb "
                             "left join subscriptions s on"
@@ -263,6 +305,17 @@
             ++i;
         }
         sel.append( " order by lower(mb.name)||' '" );
+               
+               // Complete Sub-Query And Add Our Own For Additional Status Info
+               sel.append( "), "
+                                       "q2 as (select q1.id, mb.guid, 
uidnext-first_recent as recent from q1 join mailboxes mb using (id)), "
+                                       "q3 as (select q1.id, count(uid) as 
unseen from q1 join mailbox_messages mm on (q1.id=mm.mailbox) where not seen 
group by id), "
+                                       "q4 as (select q1.id, count(*) as 
messages from q1 join mailbox_messages mm on (q1.id=mm.mailbox) group by id) ");
+               
+               // Now Return All Of That As A Single Query Compatible With
+               // The makeResponse Call From Before We Added The Status Feature
+               sel.append( "select * from q1 LEFT OUTER JOIN q2 using (id) 
LEFT OUTER JOIN q3 using (id) LEFT OUTER JOIN q4 using (id)");
+               
         d->selectQuery->setString( sel );
         d->selectQuery->execute();
 
@@ -389,10 +442,46 @@
         d->returnSubscribed = true;
     else if ( option == "children" )
         d->returnChildren = true;
-    else
+       else if ( "status" == option ) {
+               // Status Return Takes A Number Of Status Attributes We Need To 
Parse
+        d->returnStatus = true;
+               
+               require(" (");  // Status Must Be Followed By A SPACE Then (
+               
+               addStatusItem(atom().lower());  // First Status Item Is 
Mandatory
+               while ( present( " " ) )
+            addStatusItem( atom().lower() );   // Handle Any Further Items
+                       
+               require(")");   // To Complete Status, A Closing Bracket Is 
Mandatory
+               
+    } else
         error( Bad, "Unknown return option: " + option );
 }
 
+void Listext::addStatusItem( const EString & statusSelected ) {
+       if ("x-guid" == statusSelected) {
+               // X-GUID
+               d->statusRequired.getXGuid = true;
+       } else if ("messages" == statusSelected) {
+               d->statusRequired.getMessages = true;
+       } else if ("recent" == statusSelected) {
+               d->statusRequired.getRecent = true;
+       } else if ("uidnext" == statusSelected) {
+               d->statusRequired.getUidNext = true;
+       } else if ("uidvalidity" == statusSelected) {
+               d->statusRequired.getUidValidity = true;
+       } else if ("unseen" == statusSelected) {
+               d->statusRequired.getUnseen = true;
+       } else if ("highestmodseq" == statusSelected) {
+               d->statusRequired.getHighestModSeq = true;
+       } else {
+               // Nothing Valid Left - May Just Be We Don't Handle It Rather 
Than
+               // The RFC Stating It Is Illegal/Invalid.  We Log It So, If 
Debug Is
+               // Active, There Will Be A Record Of This Happening To Help
+               // Track It Down.
+               error( Bad, "Unknown status item: " + statusSelected );
+       }
+}
 
 /*! Parses the selection \a option, or emits a suitable error. \a
     option must be lower-cased. */
@@ -451,6 +540,68 @@
 
     EString name = imapQuoted( mailbox );
 
+       // Need To Append Any Extended Attributes Status ?
+       if (d->returnStatus) {
+               // yes - add them to ext
+               ext.append(" (");
+               EString preAppend = "";
+
+               if (d->statusRequired.getXGuid && row->hasColumn("guid") && 
!row->isNull("guid")) {
+                       // We want the guid and the column exists and is not 
null, so it is good to retrieve
+                       EString guid = row->getUUID("guid");
+                       ext.append(preAppend + "X-GUID " + guid);
+                       log("retrieved guid as " + guid);
+                       preAppend = " ";
+               }
+
+               if (d->statusRequired.getRecent && row->hasColumn("recent") && 
!row->isNull("recent")) {
+                       int recent = row->getInt("recent");
+                       ext.append(preAppend + "RECENT ");
+                       ext.appendNumber(recent);
+                       preAppend = " ";
+               }
+       
+               if (d->statusRequired.getUnseen && row->hasColumn("unseen") && 
!row->isNull("unseen")) {
+                       int unseen = row->getInt("unseen");
+                       ext.append(preAppend + "UNSEEN ");
+                       ext.appendNumber(unseen);
+                       preAppend = " ";
+               }
+
+               if (d->statusRequired.getMessages && row->hasColumn("messages") 
&& !row->isNull("messages")) {
+                       int messages = row->getInt("messages");
+                       ext.append(preAppend + "MESSAGES ");
+                       ext.appendNumber(messages);
+                       preAppend = " ";
+               }
+               
+               if (d->statusRequired.getUidNext) {
+                       int uidnext = mailbox->uidnext();
+                       ext.append(preAppend + "UIDNEXT ");
+                       ext.appendNumber(uidnext);
+                       preAppend = " ";
+               }               
+               
+               if (d->statusRequired.getUidValidity) {
+                       int uidvalidity = mailbox->uidvalidity();
+                       ext.append(preAppend + "UIDVALIDITY ");
+                       ext.appendNumber(uidvalidity);
+                       preAppend = " ";
+               }
+               
+               if (d->statusRequired.getHighestModSeq) {
+                       int64 highest = mailbox->nextModSeq();
+                       if (highest > 1)
+                               highest--;
+                               
+                       ext.append(preAppend + "HIGHESTMODSEQ ");
+                       ext.appendNumber(highest);
+                       preAppend = " ";
+               }
+               
+               ext.append(")");
+       }
+
     EString r = "LIST (" + a.join( " " ) + ") \"/\" " + name + ext;
 
     if ( r == d->previousResponse )
diff -wur aox-with-uuid/imap/handlers/listext.h ../aox/imap/handlers/listext.h
--- aox-with-uuid/imap/handlers/listext.h       2016-06-10 14:52:24.508560046 
-0400
+++ ../aox/imap/handlers/listext.h      2016-06-09 10:28:45.986794435 -0400
@@ -20,6 +20,7 @@
 private:
     void addReturnOption( const EString & );
     void addSelectOption( const EString & );
+       void addStatusItem( const EString & );
 
     void makeResponse( class Row * );
 
diff -wur aox-with-uuid/imap/handlers/status.cpp ../aox/imap/handlers/status.cpp
--- aox-with-uuid/imap/handlers/status.cpp      2016-06-10 14:52:24.508560046 
-0400
+++ ../aox/imap/handlers/status.cpp     2016-06-09 10:28:45.986794435 -0400
@@ -19,16 +19,17 @@
     StatusData() :
         messages( false ), uidnext( false ), uidvalidity( false ),
         recent( false ), unseen( false ),
-        modseq( false ),
+        modseq( false ), x_guid (false),
         mailbox( 0 ),
         unseenCount( 0 ), messageCount( 0 ), recentCount( 0 ),
         cacheState( 0 )
         {}
-    bool messages, uidnext, uidvalidity, recent, unseen, modseq;
+    bool messages, uidnext, uidvalidity, recent, unseen, modseq, x_guid;
     Mailbox * mailbox;
     Query * unseenCount;
     Query * messageCount;
     Query * recentCount;
+       Query * mailbox_x_guid;
     uint cacheState;
 
     class CacheItem
@@ -37,16 +38,19 @@
     public:
         CacheItem():
             hasMessages( false ), hasUnseen( false ), hasRecent( false ),
+                       has_x_guid (false),
             messages( 0 ), unseen( 0 ), recent( 0 ),
             nextmodseq( 0 ), mailbox( 0 )
             {}
         bool hasMessages;
         bool hasUnseen;
         bool hasRecent;
+               bool has_x_guid;
         uint messages;
         uint unseen;
         uint recent;
         int64 nextmodseq;
+               EString x_guid;
         Mailbox * mailbox;
     };
 
@@ -73,6 +77,7 @@
                 i->hasMessages = false;
                 i->hasUnseen = false;
                 i->hasRecent = false;
+                               i->has_x_guid = false;
             }
             return i;
         }
@@ -154,6 +159,8 @@
             d->unseen = true;
         else if ( item == "highestmodseq" )
             d->modseq = true;
+        else if ( "x-guid" == item )
+            d->x_guid = true;
         else
             error( Bad, "Unknown STATUS item: " + item );
 
@@ -189,13 +196,15 @@
 
     // second part. see if anything has happened, and feed the cache if
     // so. make sure we feed the cache at once.
-    if ( d->unseenCount || d->recentCount || d->messageCount ) {
+    if ( d->unseenCount || d->recentCount || d->messageCount || 
d->mailbox_x_guid) {
         if ( d->unseenCount && !d->unseenCount->done() )
             return;
         if ( d->messageCount && !d->messageCount->done() )
             return;
         if ( d->recentCount && !d->recentCount->done() )
             return;
+               if ( d->mailbox_x_guid && !d->mailbox_x_guid->done() )
+                       return;
     }
     if ( !::cache )
         ::cache = new StatusData::StatusCache;
@@ -233,7 +242,18 @@
             }
         }
     }
-
+    if ( d->mailbox_x_guid ) {
+               // Retrieve Mailbox GUID Value And Place Into Cache Item
+        while ( d->mailbox_x_guid->hasResults() ) {
+            Row * r = d->mailbox_x_guid->nextRow();
+            StatusData::CacheItem * ci =
+                ::cache->find( r->getInt( "mailbox" ) );
+            if ( ci ) {
+                ci->has_x_guid = true;
+                ci->x_guid = r->getEString( "guid" );
+            }
+        }
+    }
     // third part. are we processing the first command in a STATUS
     // loop? if so, see if we ought to preload the cache.
     if ( mailboxGroup() && d->cacheState < 3 ) {
@@ -244,9 +264,9 @@
             while ( i ) {
                 StatusData::CacheItem * ci = ::cache->provide( i );
                 bool need = false;
-                if ( d->unseen || d->recent || d->messages )
+                if ( d->unseen || d->recent || d->messages || d->x_guid )
                     need = true;
-                if ( ci->hasUnseen || ci->hasRecent || ci->hasMessages )
+                if ( ci->hasUnseen || ci->hasRecent || ci->hasMessages || 
ci->has_x_guid )
                     need = false;
                 if ( need )
                     mailboxes.add( i->id() );
@@ -282,6 +302,16 @@
                 d->messageCount->bind( 1, mailboxes );
                 d->messageCount->execute();
             }
+                       if ( d->x_guid ) {
+                               // Run Query To Get Mailbox GUID Value
+                d->mailbox_x_guid
+                    = new Query( "select guid "
+                                 "from mailboxes where id=any($1) "
+                                 , this );
+                d->mailbox_x_guid->bind( 1, mailboxes );
+                d->mailbox_x_guid->execute();
+            }
+                       
             d->cacheState = 2;
         }
         if ( d->cacheState == 2 ) {
@@ -296,6 +326,8 @@
                     ci->hasRecent = true;
                 if ( ci && d->messageCount )
                     ci->hasMessages = true;
+                if ( ci && d->mailbox_x_guid )
+                    ci->has_x_guid = true;
                 ++i;
             }
             // and drop the queries
@@ -303,6 +335,7 @@
             d->unseenCount = 0;
             d->recentCount = 0;
             d->messageCount = 0;
+                       d->mailbox_x_guid = NULL;       // Null is surely 
better than 0?
         }
     }
 
@@ -356,13 +389,26 @@
         d->messageCount->execute();
     }
 
-    if ( d->unseenCount || d->recentCount || d->messageCount ) {
+       // Individual guid query ?
+    if ( d->x_guid && !d->mailbox_x_guid && !i->has_x_guid ) {
+        d->mailbox_x_guid
+            = new Query( "select guid "
+                                                "from mailboxes where id=$1 "
+                         , this );
+        d->mailbox_x_guid->bind( 1, d->mailbox->id() );
+        d->mailbox_x_guid->execute();
+    }
+
+
+    if ( d->unseenCount || d->recentCount || d->messageCount || 
d->mailbox_x_guid ) {
         if ( d->unseenCount && !d->unseenCount->done() )
             return;
         if ( d->messageCount && !d->messageCount->done() )
             return;
         if ( d->recentCount && !d->recentCount->done() )
             return;
+        if ( d->mailbox_x_guid && !d->mailbox_x_guid->done() )
+            return;
     }
 
     // fifth part: return the payload.
@@ -396,6 +442,9 @@
         status.append( "HIGHESTMODSEQ " + fn( hms ) );
     }
 
+    if ( d->x_guid && i->has_x_guid )
+        status.append( "X-GUID " + i->x_guid );
+
     respond( "STATUS " + imapQuoted( d->mailbox ) +
              " (" + status.join( " " ) + ")" );
     finish();
diff -wur aox/aox/db.cpp aox-with-uuid/aox/db.cpp
--- aox/aox/db.cpp      2016-06-10 14:53:07.718688321 -0400
+++ aox-with-uuid/aox/db.cpp    2016-06-10 14:52:40.169331384 -0400
@@ -751,6 +751,9 @@
         case Column::Timestamp:
             s.append( "(timestamptz)" );
             break;
+               case Column::Uuid:
+                       s.append( "(uuid)");
+                       break;
         case Column::Null:
             s.append( "null" );
             break;
diff -wur aox/core/estring.h aox-with-uuid/core/estring.h
--- aox/core/estring.h  2016-06-10 14:53:07.726688715 -0400
+++ aox-with-uuid/core/estring.h        2016-06-10 14:52:40.173331582 -0400
@@ -40,6 +40,7 @@
     EString & operator=( const EString & );
     EString & operator=( const char * );
     EString & operator+=( const EString & str ) { append( str ); return *this; 
}
+       EString & operator+=( const int64 & num ) { appendNumber(num); return 
*this; }
 
     void operator delete( void * );
 
diff -wur aox/db/pgmessage.cpp aox-with-uuid/db/pgmessage.cpp
--- aox/db/pgmessage.cpp        2016-06-10 14:53:07.770690882 -0400
+++ aox-with-uuid/db/pgmessage.cpp      2016-06-10 14:52:40.173331582 -0400
@@ -8,6 +8,7 @@
 #include "buffer.h"
 #include "query.h"
 
+#include "stdio.h"
 
 static bool haveAskedForCitext;
 static int citextOid;
@@ -189,6 +190,53 @@
     return s;
 }
 
+EString PgServerMessage::decodeUUID( uint x ) {
+       EString s = "";
+       
+       if (16 != x) {
+               log("uuid decode should request 16 bytes but instead requested 
" + x );
+               throw Syntax;
+       }
+       
+       uint32 data1 = ( (*buf)[0] << 24 ) |
+            ( (*buf)[1] << 16 ) |
+            ( (*buf)[2] <<  8 ) |
+            ( (*buf)[3]       );
+    buf->remove( 4 );
+    n += 4;
+       
+       uint32 data2 = ((*buf)[0] << 8) | (*buf)[1];
+       buf->remove( 2 );
+    n += 2;
+       
+       uint32 data3 = ((*buf)[0] << 8) | (*buf)[1];
+       buf->remove( 2 );
+    n += 2;
+       
+       uint32 data4 = ((*buf)[0] << 8) | (*buf)[1];
+       buf->remove( 2 );
+    n += 2;
+       
+       uint32 data5 = ((*buf)[0] << 8) | (*buf)[1];
+       buf->remove( 2 );
+    n += 2;
+       
+       
+       uint32 data6 = ( (*buf)[0] << 24 ) |
+            ( (*buf)[1] << 16 ) |
+            ( (*buf)[2] <<  8 ) |
+            ( (*buf)[3]       );
+    buf->remove( 4 );
+    n += 4;
+       
+       char result[37];
+       sprintf( result, "%08x-%04x-%04x-%04x-%04x%08x", data1, data2, data3, 
data4, data5, data6);
+       
+       s = result;
+
+       return s;
+}
+
 
 /*! This function is used by subclasses to assert that they have decoded
     the entire contents of the message. If the size of the decoded data
@@ -898,6 +946,9 @@
         case 1184:
             cv->type = Column::Timestamp;
             break;
+               case 2950:
+                       cv->type = Column::Uuid;
+                       break;
         default:
             if ( it->type == ::citextOid ) { // CITEXT
                 cv->type = Column::Bytes;
@@ -978,6 +1029,9 @@
         case Column::Timestamp:
             cv->s = decodeByten( length );
             break;
+               case Column::Uuid:
+                       cv->s = decodeUUID( length );
+                       break;
         case Column::Null:
             // nothing needed
             break;
diff -wur aox/db/pgmessage.h aox-with-uuid/db/pgmessage.h
--- aox/db/pgmessage.h  2016-06-10 14:53:07.770690882 -0400
+++ aox-with-uuid/db/pgmessage.h        2016-06-10 14:52:40.173331582 -0400
@@ -33,6 +33,7 @@
     char decodeByte();
     EString decodeString();
     EString decodeByten( uint );
+       EString decodeUUID(uint);
     void end();
 };
 
diff -wur aox/db/query.cpp aox-with-uuid/db/query.cpp
--- aox/db/query.cpp    2016-06-10 14:53:07.770690882 -0400
+++ aox-with-uuid/db/query.cpp  2016-06-10 14:52:40.173331582 -0400
@@ -861,6 +861,19 @@
     return c->bi;
 }
 
+/*! Returns the string value of the column named \a f i if it exists
+    and is NOT NULL, and an empty string otherwise.
+*/
+
+EString Row::getUUID( const char * f ) const
+{
+    const Column * c = fetch( f, Column::Uuid, true );
+    if ( !c )
+        return "";
+    if ( c->type != Column::Uuid )
+        return "";
+    return c->s;
+}
 
 /*! Returns the string value of the column named \a f i if it exists
     and is NOT NULL, and an empty string otherwise.
@@ -1015,6 +1028,9 @@
     case Timestamp:
         n = "timestamptz";
         break;
+       case Uuid:
+               n = "uuid";
+               break;
     case Null:
         n = "null";
         break;
diff -wur aox/db/query.h aox-with-uuid/db/query.h
--- aox/db/query.h      2016-06-10 14:53:07.770690882 -0400
+++ aox-with-uuid/db/query.h    2016-06-10 14:52:40.173331582 -0400
@@ -129,7 +129,7 @@
     : public Garbage
 {
 public:
-    enum Type { Unknown, Boolean, Integer, Bigint, Bytes, Timestamp, Null };
+    enum Type { Unknown, Boolean, Integer, Bigint, Bytes, Timestamp, Null, 
Uuid };
 
     Type type;
     EString s;
@@ -153,6 +153,7 @@
     bool getBoolean( const char * ) const;
     EString getEString( const char * ) const;
     UString getUString( const char * ) const;
+       EString getUUID( const char * ) const;
     bool hasColumn( const char * ) const;
     Column::Type columnType( const char * ) const;
 

Reply via email to