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;