I recently enabled build and runtime tests for the Postgres backend in
the Debian package (previously only SQLite was tested) and wanted to
do the same for ECPG.  However, it appears that BYTEA does not work.

GNU/Linux (amd64), SQLClient from Git master (db1ff66), Performance
0.6.0, Base 1.31.1, PostgreSQL 17.4:

$ LC_ALL=C pg_virtualenv -i "-U postgres -A trust" ./obj/testECPG
Creating new PostgreSQL cluster 17/regress ...
Warning: connection to the database failed, disabling startup checks:
psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: FATAL: 
 role "yavor" does not exist

2025-04-15 20:24:18.599 testECPG[6548:6548] Duration 0.016981 for connection 
(success).
2025-04-15 20:24:18.603 testECPG[6548:6548] Raising an exception, -400, table 
"xxx" does not exist on line 293
2025-04-15 20:24:18.611 testECPG[6548:6548] Duration 0.00762999 for statement 
create table xxx ( k char(40), char1 char(1), boolval BOOL, intval int, when1 
timestamp with time zone, when2 timestamp, b bytea); affected -1 records
2025-04-15 20:24:18.612 testECPG[6548:6548] Raising an exception, -400, syntax 
error at or near "\" on line 292
./obj/testECPG: Uncaught exception SQLException, reason: SQL Error: 
SQLCODE=(-400): syntax error at or near "\" on line 292
*** /tmp/pg_virtualenv.Bt0T23/log/postgresql-17-regress.log (last 100 lines) ***
2025-04-15 20:24:11.847 EEST [6504] LOG:  starting PostgreSQL 17.4 (Debian 
17.4-2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 
64-bit
2025-04-15 20:24:11.848 EEST [6504] LOG:  listening on IPv6 address "::1", port 
5432
2025-04-15 20:24:11.848 EEST [6504] LOG:  listening on IPv4 address 
"127.0.0.1", port 5432
2025-04-15 20:24:11.848 EEST [6504] LOG:  listening on Unix socket 
"/tmp/.s.PGSQL.5432"
2025-04-15 20:24:11.854 EEST [6507] LOG:  database system was shut down at 
2025-04-15 20:24:11 EEST
2025-04-15 20:24:11.866 EEST [6504] LOG:  database system is ready to accept 
connections
2025-04-15 20:24:12.924 EEST [6514] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:13.454 EEST [6517] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:13.983 EEST [6520] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:14.511 EEST [6523] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:15.033 EEST [6526] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:15.568 EEST [6529] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:16.100 EEST [6532] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:16.631 EEST [6535] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:17.159 EEST [6538] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:17.687 EEST [6541] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:18.216 EEST [6544] yavor@template1 FATAL:  role "yavor" does 
not exist
2025-04-15 20:24:18.602 EEST [6552] postgres@template1 ERROR:  table "xxx" does 
not exist
2025-04-15 20:24:18.602 EEST [6552] postgres@template1 STATEMENT:  drop table 
xxx
2025-04-15 20:24:18.611 EEST [6552] postgres@template1 ERROR:  syntax error at 
or near "\" at character 134
2025-04-15 20:24:18.611 EEST [6552] postgres@template1 STATEMENT:  insert into 
xxx (k, char1, boolval, intval, when1, when2, b) values ('hello', 'X', TRUE, 1, 
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 
'?'\\000\\001\\002\\003\\004\\005\\006\\007\\010\\011\\012\\013\\014\\015\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037
 
!"#$%&\'()*+,-./0123456789\\072;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\177\\200\\201\\202\\203\\204\\205\\206\\207\\210\\211\\212\\213\\214\\215\\216\\217\\220\\221\\222\\223\\224\\225\\226\\227\\230\\231\\232\\233\\234\\235\\236\\237\\240\\241\\242\\243\\244\\245\\246\\247\\250\\251\\252\\253\\254\\255\\256\\257\\260\\261\\262\\263\\264\\265\\266\\267\\270\\271\\272\\273\\274\\275\\276\\277\\300\\301\\302\\303\\304\\305\\306\\307\\310\\311\\312\\313\\314\\315\\316\\317\\320\\321\\322\\323\\324\\325\\326\\327\\330\\331\\332\\333\\334\\335\\336\\337\\340\\341\\342\\343\\344\\345\\346\\347\\350\\351
 
\\352\\353\\354\\355\\356\\357\\360\\361\\362\\363\\364\\365\\366\\367\\370\\371\\372\\373\\374\\375\\376\\377'$1')
Dropping cluster 17/regress ...

According to the documentation, the BYTEA type can be used only when
bytea_output is set to "hex":

https://www.postgresql.org/docs/current/ecpg-variables.html#ECPG-SPECIAL-TYPES-BYTEA

So an obvious solution is to use the same method implementations as in
the Postgres backend bundle.  There's one more issue: the switch
statement in -backendQuery:recordType:listType: expects negative
values for non-standard types but that's no longer the case.  If I
uncomment the printf statement I get:

k type:1 scale:40
char1 type:1 scale:1
boolval type:16 scale:65531
intval type:4 scale:65531
when1 type:0 scale:65531
when2 type:9 scale:65531
b type:0 scale:65531
k type:1 scale:40
char1 type:1 scale:1
boolval type:16 scale:65531
intval type:4 scale:65531
when1 type:0 scale:65531
when2 type:9 scale:65531
b type:0 scale:65531

So I ammended it accordingly (it's still a hack as the comment says).
I consider it a serious shortcoming that ECPG handles only standard
SQL types and not other types supported by PostgreSQL.

With the patch applied, I get:

$ LC_ALL=C pg_virtualenv -i "-U postgres -A trust" ./obj/testECPG
Creating new PostgreSQL cluster 17/regress ...
Warning: connection to the database failed, disabling startup checks:
psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: FATAL: 
 role "yavor" does not exist

2025-04-15 21:14:48.923 testECPG[8549:8549] Duration 0.018278 for connection 
(success).
2025-04-15 21:14:48.928 testECPG[8549:8549] Raising an exception, -400, table 
"xxx" does not exist on line 272
2025-04-15 21:14:48.936 testECPG[8549:8549] Duration 0.00765002 for statement 
create table xxx ( k char(40), char1 char(1), boolval BOOL, intval int, when1 
timestamp with time zone, when2 timestamp, b bytea); affected -1 records
2025-04-15 21:14:48.938 testECPG[8549:8549] Duration 0.00203705 for statement 
insert into xxx (k, char1, boolval, intval, when1, when2, b) values ('hello', 
'X', TRUE, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, '?'''?'); affected -1 
records
2025-04-15 21:14:48.939 testECPG[8549:8549] Duration 0.000791073 for statement 
insert into xxx (k, char1, boolval, intval, when1, when2, b) values ('hello', 
'X', TRUE, 1, '2025-04-15 21:14:48.938 +0300', '2025-04-15 21:14:48.938 +0300', 
'?'''?'); affected -1 records
2025-04-15 21:14:48.941 testECPG[8549:8549] Duration 0.001966 for query select 
* from xxx;  produced 2 records
2025-04-15 21:14:48.945 testECPG[8549:8549] Duration 0.00317395 for statement 
drop table xxx; affected -1 records
2025-04-15 21:14:48.945 testECPG[8549:8549] Records - (("hello                  
                 ", X, 1, 1, "2025-04-15 21:14:48 +0300", "2025-04-15 21:14:48 
+0300", <00010203 04050607 08090a0b 0c0d0e0f 10111213 14151617 18191a1b 
1c1d1e1f 20212223 24252627 28292a2b 2c2d2e2f 30313233 34353637 38393a3b 
3c3d3e3f 40414243 44454647 48494a4b 4c4d4e4f 50515253 54555657 58595a5b 
5c5d5e5f 60616263 64656667 68696a6b 6c6d6e6f 70717273 74757677 78797a7b 
7c7d7e7f 80818283 84858687 88898a8b 8c8d8e8f 90919293 94959697 98999a9b 
9c9d9e9f a0a1a2a3 a4a5a6a7 a8a9aaab acadaeaf b0b1b2b3 b4b5b6b7 b8b9babb 
bcbdbebf c0c1c2c3 c4c5c6c7 c8c9cacb cccdcecf d0d1d2d3 d4d5d6d7 d8d9dadb 
dcdddedf e0e1e2e3 e4e5e6e7 e8e9eaeb ecedeeef f0f1f2f3 f4f5f6f7 f8f9fafb 
fcfdfeff>), ("hello                                   ", X, 1, 1, "2025-04-15 
21:14:48 +0300", "2025-04-15 21:14:48 +0300", <>))
Dropping cluster 17/regress ...

It probably makes sense to create a common subclass and let Postgres
and ECPG inherit from it because 4 method implementations are exactly
the same (ECPG does not need to handle escaped blobs but it does no
harm to have that code there).

The second patch is unrelated but I use the opportunity to propose it.
It is not very good if test programs fail but exit successfully -- I
have to crawl the logs to see if tests passed or not, and that's a lot
of manual work.
>From 5cb046dc4c744f6fb92eccad12dcd5c91e321a09 Mon Sep 17 00:00:00 2001
From: Yavor Doganov <[email protected]>
Date: Tue, 15 Apr 2025 21:18:25 +0300
Subject: [PATCH 1/2] Fix ECPG BYTEA handling

---
 ChangeLog |  13 +++
 ECPG.pgm  | 241 ++++++++++++++++++++----------------------------------
 2 files changed, 101 insertions(+), 153 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f241ddf..52e0db1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2025-04-15  Yavor Doganov  <[email protected]>
+
+       * ECPG.pgm (hackForPrepare): Remove; unnecessary with recent
+       PostgreSQL.  All users updated.
+       ([SQLClientECPG blobFromData:]): Remove method.
+       ([SQLClientECPG backendExecute:]): Use "'?'''?'" as marker.
+       ([SQLClientECPG backendQuery:recordType:listType:]): Check
+       date/timestamp when returned type is zero, resort to BYTEA.
+       ([SQLClientECPG copyEscapedBLOB:into:])
+       ([SQLClientECPG lengthOfEscapedBLOB:])
+       ([SQLClientECPG dataFromBlob:]): Copy method definitions from the
+       Postgres backend to handle BYTEA properly.
+
 2024-01-26 Richard Frith-Macdonald  <[email protected]>
 
        * Postgres.m: Fix error in parsing milliseconds in timestamp.
diff --git a/ECPG.pgm b/ECPG.pgm
index dfcaf01..119bb5c 100644
--- a/ECPG.pgm
+++ b/ECPG.pgm
@@ -49,7 +49,6 @@
 @end
 
 @interface     SQLClientECPG(Embedded)
-- (const char *) blobFromData: (NSData*)data;
 - (NSData *) dataFromBlob: (const char *)blob;
 - (BOOL) dbFromDate: (NSDate*)d toBuffer: (char*)b length: (int)l;
 - (BOOL) dbFromString: (NSString*)s toBuffer: (char*)b length: (int)l;
@@ -57,26 +56,6 @@
 - (NSString*) dbToStringFromBuffer: (char*)b length: (int)l;
 @end
 
-/* This looks like a Postgres specific issue/feature/bug - for some
- * reason, ':' inside a string often causes the string to be
- * horribly mutilated before being handed to the database
- * ... probably postgres is trying to replace variables or something
- * - avoid all these problems by replacing ':' with its octal code
- * \\072.
- */
-static NSString        *
-hackForPrepare(NSString *s)
-{
-  NSRange      r;
-
-  r = [s rangeOfString: @":"];
-  if (r.length > 0)
-    {
-      s = [s stringByReplacingString: @":" withString: @"\\072"];
-    }
-  return s;
-}
-
 
 EXEC SQL INCLUDE sql3types;
 EXEC SQL INCLUDE sqlca;
@@ -266,13 +245,13 @@ static NSDate     *future = nil;
                  format: @"Statement produced null string"];
     }
 
-  statement = (char*)[hackForPrepare(stmt) UTF8String];
+  statement = (char*)[stmt UTF8String];
   length = strlen(statement);
   statement = (char*)[self insertBLOBs: info
                         intoStatement: statement
                                length: length
-                           withMarker: "'''"
-                               length: 3
+                           withMarker: "'?'''?'"
+                               length: 7
                                giving: &length];
   handle = (char*)[[self clientName] UTF8String];
 
@@ -396,7 +375,7 @@ static unsigned int trim(char *str)
                  format: @"Statement produced null string"];
     }
 
-  query = (char*)[hackForPrepare(stmt) UTF8String];
+  query = (char*)[stmt UTF8String];
   handle = (char*)[[self clientName] UTF8String];
   records = [[ltype alloc] initWithCapacity: 32];
 
@@ -463,12 +442,13 @@ static unsigned int trim(char *str)
                  else
                    {
                      /*
-                      * HACK ... for some reason date/time data seems to
-                      * get a negative type returned, so we check any
-                      * negative time to see if it is really date/time
-                      * and bodg the type code to fit.
+                      * HACK ... old PosgreSQL versions returned
+                      * negative numbers for types not declared in
+                      * the sql3types.h enum but newer versions
+                      * return 0.  So we check if it is really
+                      * date/time and bodge the type code to fit.
                       */
-                     if (type < 0)
+                     if (type == 0)
                        {
                          EXEC SQL GET DESCRIPTOR myDesc VALUE :index 
                            :dtiCode = DATETIME_INTERVAL_CODE;
@@ -476,6 +456,10 @@ static unsigned int trim(char *str)
                            {
                              type = SQL3_DATE_TIME_TIMESTAMP;
                            }
+                          else
+                            {
+                              type = -17; // Assume BYTEA
+                            }
                        }
 
                      aString = 0;
@@ -567,8 +551,9 @@ static unsigned int trim(char *str)
 
                          case -17:
                            /*
-                            * HACK ... BYTEA type determined by experiment.
-                            * who knows how/why this might change.
+                            * HACK ... BYTEA type determined by
+                            * experiment for older PostgreSQL
+                            * versions.
                             */
                            EXEC SQL GET DESCRIPTOR myDesc VALUE :index
                              :aString = DATA;
@@ -699,38 +684,34 @@ static unsigned int trim(char *str)
   unsigned             length = 0;
   unsigned             i;
 
+  ptr[length++] = 'E';
   ptr[length++] = '\'';
   for (i = 0; i < sLen; i++)
     {
       unsigned char    c = src[i];
 
-      if (c < 32 || c > 126 || c == ':')
-       {
-         ptr[length] = '\\';
-         ptr[length+1] = '\\';
-         ptr[length + 4] = (c & 7) + '0';
-         c >>= 3;
-         ptr[length + 3] = (c & 7) + '0';
-         c >>= 3;
-         ptr[length + 2] = (c & 7) + '0';
-         length += 5;
-       }
+      if (c < 32 || c > 126 || c == '\'')
+        {
+          ptr[length] = '\\';
+          ptr[length+1] = '\\';
+          ptr[length + 4] = (c & 7) + '0';
+          c >>= 3;
+          ptr[length + 3] = (c & 7) + '0';
+          c >>= 3;
+          ptr[length + 2] = (c & 7) + '0';
+          length += 5;
+        }
       else if (c == '\\')
-       {
-         ptr[length++] = '\\';
-         ptr[length++] = '\\';
-         ptr[length++] = '\\';
-         ptr[length++] = '\\';
-       }
-      else if (c == '\'')
-       {
-         ptr[length++] = '\\';
-         ptr[length++] = '\'';
-       }
+        {
+          ptr[length++] = '\\';
+          ptr[length++] = '\\';
+          ptr[length++] = '\\';
+          ptr[length++] = '\\';
+        }
       else
-       {
-         ptr[length++] = c;
-       }
+        {
+          ptr[length++] = c;
+        }
     }
   ptr[length++] = '\'';
   return length;
@@ -740,134 +721,88 @@ static unsigned int trim(char *str)
 {
   unsigned int sLen = [blob length];
   unsigned char        *src = (unsigned char*)[blob bytes];
-  unsigned int length = 2;
+  unsigned int length = sLen + 2;
   unsigned int i;
 
+  length++;         // Allow for leading 'E'
   for (i = 0; i < sLen; i++)
     {
       unsigned char    c = src[i];
 
-      if (c < 32 || c > 126 || c == ':')
-       {
-         length += 5;
-       }
+      if (c < 32 || c > 126 || c == '\'')
+        {
+          length += 4;
+        }
       else if (c == '\\')
-       {
-         length += 4;
-       }
-      else if (c == '\'')
-       {
-         length += 2;
-       }
-      else
-       {
-         length += 1;
-       }
+        {
+          length += 3;
+        }
     }
   return length;
 }
 
-- (const char *) blobFromData: (NSData*)data
+- (NSData *) dataFromBlob: (const char *)blob
 {
   NSMutableData        *md;
-  unsigned     sLen = [data length];
-  unsigned char        *src = (unsigned char*)[data bytes];
+  unsigned     sLen = strlen(blob == 0 ? "" : blob);
   unsigned     dLen = 0;
   unsigned char        *dst;
   unsigned     i;
 
-  for (i = 0; i < sLen; i++)
+  if (sLen > 1 && '\\' == blob[0] && 'x' == blob[1])
     {
-      unsigned char    c = src[i];
-
-      if (c < 32 || c > 126)
-       {
-         dLen += 4;
-       }
-      else if (c == 92)
+      dLen = (sLen - 2) / 2;
+      dst = (unsigned char*)NSAllocateCollectable(dLen, 0);
+      md = [NSMutableData dataWithBytesNoCopy: dst length: dLen];
+      dLen = 0;
+      for (i = 2; i < sLen; i += 2)
        {
-         dLen += 2;
-       }
-      else
-       {
-         dLen += 1;
-       }
-    }
-  md = [NSMutableData dataWithLength: dLen + 1];
-  dst = (unsigned char*)[md mutableBytes];
+         unsigned      hi = blob[i];
+         unsigned      lo = blob[i + 1];
 
-  dLen = 0;
-  for (i = 0; i < sLen; i++)
-    {
-      unsigned char    c = src[i];
-
-      if (c < 32 || c > 126)
-       {
-         dst[dLen] = '\\';
-         dst[dLen + 3] = (c & 7) + '0';
-         c >>= 3;
-         dst[dLen + 2] = (c & 7) + '0';
-         c >>= 3;
-         dst[dLen + 1] = (c & 7) + '0';
-         dLen += 4;
-       }
-      else if (c == 92)
-       {
-         dst[dLen++] = '\\';
-         dst[dLen++] = '\\';
-       }
-      else
-       {
-         dst[dLen++] = c;
+         hi = (hi > '9') ? (hi - 'a' + 10) : (hi - '0');
+         lo = (lo > '9') ? (lo - 'a' + 10) : (lo - '0');
+         dst[dLen++] = (hi << 4) + lo;
        }
     }
-  dst[dLen] = '\0';
-  return (const char*)dst;     // Owned by autoreleased NSMutableData
-}
-
-- (NSData *) dataFromBlob: (const char *)blob
-{
-  NSMutableData        *md;
-  unsigned     sLen = strlen(blob == 0 ? "" : blob);
-  unsigned     dLen = 0;
-  unsigned char        *dst;
-  unsigned     i;
-
-  for (i = 0; i < sLen; i++)
+  else
     {
-      unsigned c = blob[i];
-
-      dLen++;
-      if (c == '\\')
+      for (i = 0; i < sLen; i++)
        {
-         c = blob[++i];
-         if (c != '\\')
+         unsigned      c = blob[i];
+
+         dLen++;
+         if (c == '\\')
            {
-             i += 2;   // Skip 2 digits octal
+             c = blob[++i];
+             if (c != '\\')
+               {
+                 i += 2;       // Skip 2 digits octal
+               }
            }
        }
-    }
-  md = [NSMutableData dataWithLength: dLen];
-  dst = (unsigned char*)[md mutableBytes];
-
-  dLen = 0;
-  for (i = 0; i < sLen; i++)
-    {
-      unsigned c = blob[i];
 
-      if (c == '\\')
+      dst = (unsigned char*)NSAllocateCollectable(i, dLen);
+      md = [NSMutableData dataWithBytesNoCopy: dst length: dLen];
+      dLen = 0;
+      for (i = 0; i < sLen; i++)
        {
-         c = blob[++i];
-         if (c != '\\')
+         unsigned      c = blob[i];
+
+         if (c == '\\')
            {
-             c = c - '0';
-             c <<= 3;
-             c += blob[++i] - '0';
-             c <<= 3;
-             c += blob[++i] - '0';
+             c = blob[++i];
+             if (c != '\\')
+               {
+                 c = c - '0';
+                 c <<= 3;
+                 c += blob[++i] - '0';
+                 c <<= 3;
+                 c += blob[++i] - '0';
+               }
            }
+         dst[dLen++] = c;
        }
-      dst[dLen++] = c;
     }
   return md;
 }
-- 
2.49.0

>From fa062d2b0c47cf0e9c64a5e9adb943af1cb58e1a Mon Sep 17 00:00:00 2001
From: Yavor Doganov <[email protected]>
Date: Tue, 15 Apr 2025 21:41:36 +0300
Subject: [PATCH 2/2] Make test programs' exit status the number of failed
 tests

---
 ChangeLog      |  6 ++++++
 testECPG.m     |  6 +++++-
 testJDBC.m     |  8 +++++++-
 testMySQL.m    |  6 +++++-
 testPostgres.m | 15 ++++++++++++++-
 testSQLite.m   |  6 +++++-
 6 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 52e0db1..3651bfd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2025-04-15  Yavor Doganov  <[email protected]>
 
+       * testECPG.m (main): Make exit status the number of failed tests.
+       * testJDBC.m (main): Likewise.
+       * testMySQL.m (main): Likewise.
+       * testPostgres.m (main): Likewise.
+       * testSQLite.m (main): Likewise.
+
        * ECPG.pgm (hackForPrepare): Remove; unnecessary with recent
        PostgreSQL.  All users updated.
        ([SQLClientECPG blobFromData:]): Remove method.
diff --git a/testECPG.m b/testECPG.m
index c6aaeae..a6b16c8 100644
--- a/testECPG.m
+++ b/testECPG.m
@@ -37,6 +37,7 @@ main()
   unsigned char                dbuf[256];
   unsigned int         i;
   NSData               *data;
+  int                  result = 0;
 
   defs = [NSUserDefaults standardUserDefaults];
   [defs registerDefaults:
@@ -110,6 +111,7 @@ main()
   if ([records count] != 2)
     {
       NSLog(@"Expected 2 records but got %" PRIuPTR "", [records count]);
+      result++;
     }
   else
     {
@@ -118,17 +120,19 @@ main()
        {
          NSLog(@"Retrieved data does not match saved data %@ %@",
            data, [record objectForKey: @"b"]);
+         result++;
        }
       record = [records objectAtIndex: 1];
       if ([[record objectForKey: @"b"] isEqual: [NSData data]] == NO)
        {
          NSLog(@"Retrieved empty data does not match saved data");
+         result++;
        }
     }
 
   NSLog(@"Records - %@", records);
 
   [pool release];
-  return 0;
+  return result;
 }
 
diff --git a/testJDBC.m b/testJDBC.m
index 040bb4f..da615d7 100644
--- a/testJDBC.m
+++ b/testJDBC.m
@@ -39,6 +39,7 @@ main()
   unsigned int         i;
   NSData               *data;
   NSString             *name;
+  int                  result = 0;
 
   defs = [NSUserDefaults standardUserDefaults];
   [defs registerDefaults:
@@ -251,6 +252,7 @@ main()
       if ([records count] != 3)
        {
          NSLog(@"Expected 3 records but got %"PRIuPTR, [records count]);
+         result++;
        }
       else
        {
@@ -259,20 +261,24 @@ main()
            {
              NSLog(@"Retrieved data does not match saved data %@ %@",
                data, [record objectForKey: @"b"]);
+             result++;
            }
          record = [records objectAtIndex: 1];
          if ([[record objectForKey: @"b"] isEqual: [NSData data]] == NO)
            {
              NSLog(@"Retrieved empty data does not match saved data");
+             result++;
            }
          record = [records objectAtIndex: 2];
          if ([[record objectForKey: @"char1"] isEqual: nonLatin] == NO)
            {
              NSLog(@"Retrieved non-latin does not match saved string");
+             result++;
            }
          if ([[record objectForKey: @"k"] isEqual: oddChars] == NO)
            {
              NSLog(@"Retrieved odd chars does not match saved string");
+             result++;
            }
        }
 
@@ -333,6 +339,6 @@ main()
     }
 
   [pool release];
-  return 0;
+  return result;
 }
 
diff --git a/testMySQL.m b/testMySQL.m
index 8728e25..2a39590 100644
--- a/testMySQL.m
+++ b/testMySQL.m
@@ -37,6 +37,7 @@ main()
   unsigned char                dbuf[256];
   unsigned int         i;
   NSData               *data;
+  int                  result = 0;
 
   defs = [NSUserDefaults standardUserDefaults];
   [defs registerDefaults:
@@ -110,6 +111,7 @@ main()
   if ([records count] != 2)
     {
       NSLog(@"Expected 2 records but got %" PRIuPTR "", [records count]);
+      result++;
     }
   else
     {
@@ -118,17 +120,19 @@ main()
        {
          NSLog(@"Retrieved data does not match saved data %@ %@",
            data, [record objectForKey: @"b"]);
+         result++;
        }
       record = [records objectAtIndex: 1];
       if ([[record objectForKey: @"b"] isEqual: [NSData data]] == NO)
        {
          NSLog(@"Retrieved empty data does not match saved data");
+         result++;
        }
     }
 
   NSLog(@"Records - %@", records);
 
   [pool release];
-  return 0;
+  return result;
 }
 
diff --git a/testPostgres.m b/testPostgres.m
index 0345f04..88df16d 100644
--- a/testPostgres.m
+++ b/testPostgres.m
@@ -52,6 +52,7 @@ main()
   NSData               *data;
   NSString             *name;
   Logger               *l;
+  int                  result = 0;
 
   defs = [NSUserDefaults standardUserDefaults];
   [defs registerDefaults:
@@ -398,6 +399,7 @@ main()
       if ([records count] != 3)
        {
          NSLog(@"Expected 3 records but got %lu", [records count]);
+         result++;
        }
       else
        {
@@ -408,22 +410,26 @@ main()
            {
              NSLog(@"Retrieved data does not match saved data %@ %@",
                data, [record objectForKey: @"b"]);
+             result++;
            }
          record = [records objectAtIndex: 1];
          if ([[record objectForKey: @"b"] isEqual: [NSData data]] == NO)
            {
              NSLog(@"Retrieved empty data does not match saved data");
+             result++;
            }
          record = [records objectAtIndex: 2];
          if ([[record objectForKey: @"char1"] isEqual: nonLatin] == NO)
            {
              NSLog(@"Retrieved non-latin does not match saved string");
+             result++;
            }
           id o = [[record objectForKey: @"k"] stringByTrimmingSpaces];
          if ([o isEqual: oddNoNul] == NO)
            {
              NSLog(@"Retrieved odd chars (%@) does not match oddNoNul (%@)",
                 o, oddNoNul);
+             result++;
            }
           else
             {
@@ -434,21 +440,25 @@ main()
          if ([o isEqual: e1] == NO)
            {
              NSLog(@"Retrieved extra1 (%@) does not match saved (%@)", o, e1);
+             result++;
            }
           o = [record objectForKey: @"extra2"];
          if ([o isEqual: e2] == NO)
            {
              NSLog(@"Retrieved extra2 (%@) does not match saved (%@)", o, e2);
+             result++;
            }
           o = [record objectForKey: @"extra3"];
          if ([o isEqual: e3] == NO)
            {
              NSLog(@"Retrieved extra3 (%@) does not match saved (%@)", o, e3);
+             result++;
            }
           o = [record objectForKey: @"extra4"];
          if ([o count] != [e4 count])
            {
              NSLog(@"Retrieved extra4 (%@) does not match saved (%@)", o, e4);
+             result++;
            }
          for (i = 0; i < [o count]; i++)
            {
@@ -457,12 +467,14 @@ main()
                {
                  NSLog(@"Retrieved extra4 (%@) does not match saved (%@)",
                    o, e4);
+                 result++;
                }
            }
           o = [record objectForKey: @"extra5"];
          if ([o count] != [e5 count])
            {
              NSLog(@"Retrieved extra5 (%@) does not match saved (%@)", o, e5);
+             result++;
            }
          for (i = 0; i < [o count]; i++)
            {
@@ -471,6 +483,7 @@ main()
                {
                  NSLog(@"Retrieved extra5 (%@) does not match saved (%@)",
                    o, e5);
+                 result++;
                }
            }
        }
@@ -513,6 +526,6 @@ main()
   NSLog(@"Pool stats:\n%@", [sp statistics]);
 
   [pool release];
-  return 0;
+  return result;
 }
 
diff --git a/testSQLite.m b/testSQLite.m
index 389b0cd..13496f3 100644
--- a/testSQLite.m
+++ b/testSQLite.m
@@ -37,6 +37,7 @@ main()
   unsigned char                dbuf[256];
   unsigned int         i;
   NSData               *data;
+  int                  result = 0;
 
   defs = [NSUserDefaults standardUserDefaults];
   [defs registerDefaults:
@@ -105,6 +106,7 @@ main()
   if ([records count] != 2)
     {
       NSLog(@"Expected 2 records but got %" PRIuPTR "", [records count]);
+      result++;
     }
   else
     {
@@ -113,17 +115,19 @@ main()
        {
          NSLog(@"Retrieved data does not match saved data %@ %@",
            data, [record objectForKey: @"b"]);
+         result++;
        }
       record = [records objectAtIndex: 1];
       if ([[record objectForKey: @"b"] isEqual: [NSData data]] == NO)
        {
          NSLog(@"Retrieved empty data does not match saved data");
+         result++;
        }
     }
 
   NSLog(@"Records - %@", records);
 
   [pool release];
-  return 0;
+  return result;
 }
 
-- 
2.49.0

Reply via email to