Index: /trunk/archive/psdb/psDB.c
===================================================================
--- /trunk/archive/psdb/psDB.c	(revision 3209)
+++ /trunk/archive/psdb/psDB.c	(revision 3210)
@@ -8,6 +8,6 @@
  *  @author Joshua Hoblitt
  *
- *  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-02-13 22:23:07 $
+ *  @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-13 23:18:26 $
  *
  *  Copyright 2005 Joshua Hoblitt, University of Hawaii
@@ -30,39 +30,48 @@
 } mysqlType;
 
+// database utility functions
+static bool     psDBRunQuery(psDB *dbh, const char *query);
+static inline bool psDBPackRow(MYSQL_BIND *bind, psMetadata *values, psU32 paramCount);
+
+// SQL generation functions
+static char    *psDBGenerateCreateTableSQL(const char *tableName, psMetadata *where);
+static char    *psDBGenerateSelectRowSQL(const char *tableName, const char *col, psMetadata *where, const psU64 limit);
+static char    *psDBGenerateInsertRowSQL(const char *tableName, psMetadata *row);
+static char    *psDBGenerateUpdateRowSQL(const char *tableName, psMetadata *where, psMetadata *values);
+static char    *psDBGenerateWhereSQL(psMetadata *where);
+static char    *psDBGenerateSetSQL(psMetadata *set);
+
 // lookup table functions
+static psU32    psDBMySQLToPType(psU32 type, psU32 flags);
+
+static psHash  *psDBGetPSToSQLTable(void);
+static void     psDBPSToSQLTableCleanup(void);
+
+static psHash  *psDBGetSQLToPSTable(void);
+static void     psDBSQLToPSTableCleanup(void);
+
+static psHash  *psDBGetMySQLToSQLTable(void);
+static void     psDBMySQLToSQLTableCleanup(void);
+
+static psHash  *psDBGetPSToMySQLTable(void);
+static void     psDBPSToMySQLTableCleanup(void);
+
+static psPtr    psDBMySQLTypeAlloc(enum enum_field_types type, bool isUnsigned);
 static void     psDBAddToLookupTable(psHash *lookupTable, const psU32 type, const char *string);
 static void     psDBAddVoidToLookupTable(psHash *lookupTable, const psU32 type, psPtr value);
-static psHash  *psDBGetMySQLToSQLTable(void);
-static void     psDBMySQLToSQLTableCleanup(void);
-static psHash  *psDBGetPSToSQLTable(void);
-static void     psDBPSToSQLTableCleanup(void);
-static psHash  *psDBGetSQLToPSTable(void);
-static void     psDBSQLToPSTableCleanup(void);
-static psHash  *psDBGetPSToMySQLTable(void);
-static void     psDBPSToMySQLTableCleanup(void);
-static psPtr    psDBMySQLTypeAlloc(enum enum_field_types type, bool isUnsigned);
-static psU32 psDBMySQLToPType(psU32 type, psU32 flags);
 
 // pType utility functions
 static psPtr    psDBGetPTypeNaN(psElemType pType);
+static bool     psDBIsPTypeNaN(psElemType pType, psPtr data);
 static char    *psDBPTypeAsString(psPtr data, psElemType pType);
-static bool     psDBIsPTypeNaN(psElemType pType, psPtr data);
-
-// general utility functions
+
+// string utility functions
 static char    *psDBIntToString(psU64 n);
 static ssize_t  psStringAppend(char **dest, const char *format, ...);
 static ssize_t  psStringPrepend(char **dest, const char *format, ...);
 
-// database utility functions
-static bool     psDBRunQuery(psDB *dbh, const char *query);
-static inline bool psDBPackRow(MYSQL_BIND *bind, psMetadata *values, psU32 paramCount);
-
-// SQL generation functions
-static char    *psDBGenerateWhereSQL(psMetadata *where);
-static char    *psDBGenerateInsertRowSQL(const char *tableName, psMetadata *row);
-static char    *psDBGenerateCreateTableSQL(const char *tableName, psMetadata *where);
-static char    *psDBGenerateSelectRowSQL(const char *tableName, const char *col, psMetadata *where, const psU64 limit);
-static char    *psDBGenerateUpdateRowSQL(const char *tableName, psMetadata *where, psMetadata *values);
-static char    *psDBGenerateSetSQL(psMetadata *set);
+
+// public functions
+/*****************************************************************************/
 
 psDB *psDBInit(const char *host, const char *user, const char *passwd, const char *dbname)
@@ -705,4 +714,19 @@
 }
 
+
+// database utility functions
+/*****************************************************************************/
+
+static bool psDBRunQuery(psDB *dbh, const char *query)
+{
+    if (mysql_real_query(dbh->mysql, query, (unsigned long)strlen(query)) !=0) {
+        fprintf(stderr, "Failed to execute query.  Error: %s\n", mysql_error(dbh->mysql));
+           
+        return false;
+    }
+
+    return true;
+}
+
 static inline bool psDBPackRow(MYSQL_BIND *bind, psMetadata *values, psU32 paramCount)
 {
@@ -767,4 +791,154 @@
 }
 
+
+// SQL generation functions
+/*****************************************************************************/
+
+static char *psDBGenerateCreateTableSQL(const char *tableName, psMetadata *table)
+{
+    char            *query = NULL;      // complete query
+    psMetadataItem  *item;              // column description
+    psListIterator  *cursor;            // column iterator
+    psHash          *colType;           // type lookup table
+    char            *key;               // hash tmp variable
+    char            *value;             // hash tmp variable
+
+    if (!table) {
+        return NULL;
+    }
+
+    // + _ + tableName + _ + (
+    psStringAppend(&query, "CREATE TABLE %s (", tableName);
+
+    colType = psDBGetPSToSQLTable();
+
+    cursor = psListIteratorAlloc(table->list, 0);
+
+    // find column name and type
+    while ((item = psListGetNext(cursor))) {
+        if (item->type == PS_META_PRIMITIVE) {
+            // lookup SQL column type
+            key = psDBIntToString((psU64)item->pType);
+            value = psHashLookup(colType, key);
+            psFree(key);
+
+            // + column name + _ + column type
+            psStringAppend(&query, "%s %s", item->name, value);
+        } else if ((item->type == PS_META_STR) && (item->pType == PS_TYPE_PTR)) {
+            // + column name + _ + varchar( + length + )
+            psStringAppend(&query, "%s VARCHAR(%s)", item->name, item->data.V);
+        } else {
+            // error, invalid type
+            psFree(query);
+            psFree(cursor);
+
+            return NULL;
+        }
+
+        // add a , after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    psFree(colType);
+
+    psListIteratorSet(cursor, 0);
+
+    // find database indexes
+    while ((item = psListGetNext(cursor))) {
+        if ((strncmp(item->comment, "Primary Key", strlen("Primary Key"))) == 0) {
+            psStringAppend(&query, ", PRIMARY KEY(%s)", item->name);
+        } else if ((strncmp(item->comment, "Key", strlen("Key"))) == 0) {
+            psStringAppend(&query, ", KEY(%s)", item->name);
+        }
+    }
+
+    psFree(cursor);
+
+    // end column types + table type
+    psStringAppend(&query, ") ENGINE=innodb");
+
+    return query;
+}
+
+static char *psDBGenerateSelectRowSQL(const char *tableName, const char *col, psMetadata *where, const psU64 limit)
+{
+    char            *query = NULL;
+    char            *whereSQL;
+    char            *limitString;
+
+    // select all columns if col is NULL
+    if (col) {
+        psStringAppend(&query, "SELECT %s FROM %s", col, tableName);
+    } else {
+        psStringAppend(&query, "SELECT * FROM %s", tableName);
+    }
+
+    // select all rows if where is NULL
+    if (where) {
+        whereSQL = psDBGenerateWhereSQL(where);
+        if (!whereSQL) {
+            psFree(query);
+
+            return NULL;
+        }
+        psStringAppend(&query, " %s", whereSQL);
+        psFree(whereSQL);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        limitString = psDBIntToString(limit);
+        psStringAppend(&query, " LIMIT %s", limitString);
+        psFree(limitString);
+    }
+
+    return query;
+}
+
+static char *psDBGenerateInsertRowSQL(const char *tableName, psMetadata *row)
+{
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    psStringAppend(&query, "INSERT INTO %s (", tableName);
+
+    cursor = psListIteratorAlloc(row->list, 0);
+
+    // get field names
+    while ((item = psListGetNext(cursor))) {
+        psStringAppend(&query, item->name);
+
+        // + , + _ between every field name
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    // end of field names
+    psStringAppend(&query, ") VALUES (");
+
+    psListIteratorSet(cursor, 0);
+
+    // create value place holders
+    while ((item = psListGetNext(cursor))) {
+        psStringAppend(&query, "?");
+
+        // + ", " between every place holder
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    psFree(cursor);
+
+    // end of values
+    psStringAppend(&query, ")");
+
+    return query;
+}
+
 static char *psDBGenerateUpdateRowSQL(const char *tableName, psMetadata *where, psMetadata *values)
 {
@@ -790,47 +964,4 @@
     psFree(setSQL);
     psFree(whereSQL);
-
-    return query;
-}
-
-static char *psDBGenerateInsertRowSQL(const char *tableName, psMetadata *row)
-{
-    char            *query = NULL;
-    psListIterator  *cursor;
-    psMetadataItem  *item;
-
-    psStringAppend(&query, "INSERT INTO %s (", tableName);
-
-    cursor = psListIteratorAlloc(row->list, 0);
-
-    // get field names
-    while ((item = psListGetNext(cursor))) {
-        psStringAppend(&query, item->name);
-
-        // + , + _ between every field name
-        if (!cursor->offEnd) {
-            psStringAppend(&query, ", ");
-        }
-    }
-
-    // end of field names
-    psStringAppend(&query, ") VALUES (");
-
-    psListIteratorSet(cursor, 0);
-
-    // create value place holders
-    while ((item = psListGetNext(cursor))) {
-        psStringAppend(&query, "?");
-
-        // + ", " between every place holder
-        if (!cursor->offEnd) {
-            psStringAppend(&query, ", ");
-        }
-    }
-
-    psFree(cursor);
-
-    // end of values
-    psStringAppend(&query, ")");
 
     return query;
@@ -910,381 +1041,7 @@
 }
 
-static char *psDBGenerateCreateTableSQL(const char *tableName, psMetadata *table)
-{
-    char            *query = NULL;      // complete query
-    psMetadataItem  *item;              // column description
-    psListIterator  *cursor;            // column iterator
-    psHash          *colType;           // type lookup table
-    char            *key;               // hash tmp variable
-    char            *value;             // hash tmp variable
-
-    if (!table) {
-        return NULL;
-    }
-
-    // + _ + tableName + _ + (
-    psStringAppend(&query, "CREATE TABLE %s (", tableName);
-
-    colType = psDBGetPSToSQLTable();
-
-    cursor = psListIteratorAlloc(table->list, 0);
-
-    // find column name and type
-    while ((item = psListGetNext(cursor))) {
-        if (item->type == PS_META_PRIMITIVE) {
-            // lookup SQL column type
-            key = psDBIntToString((psU64)item->pType);
-            value = psHashLookup(colType, key);
-            psFree(key);
-
-            // + column name + _ + column type
-            psStringAppend(&query, "%s %s", item->name, value);
-        } else if ((item->type == PS_META_STR) && (item->pType == PS_TYPE_PTR)) {
-            // + column name + _ + varchar( + length + )
-            psStringAppend(&query, "%s VARCHAR(%s)", item->name, item->data.V);
-        } else {
-            // error, invalid type
-            psFree(query);
-            psFree(cursor);
-
-            return NULL;
-        }
-
-        // add a , after every column declaration except the last one
-        if (!cursor->offEnd) {
-            psStringAppend(&query, ", ");
-        }
-    }
-
-    psFree(colType);
-
-    psListIteratorSet(cursor, 0);
-
-    // find database indexes
-    while ((item = psListGetNext(cursor))) {
-        if ((strncmp(item->comment, "Primary Key", strlen("Primary Key"))) == 0) {
-            psStringAppend(&query, ", PRIMARY KEY(%s)", item->name);
-        } else if ((strncmp(item->comment, "Key", strlen("Key"))) == 0) {
-            psStringAppend(&query, ", KEY(%s)", item->name);
-        }
-    }
-
-    psFree(cursor);
-
-    // end column types + table type
-    psStringAppend(&query, ") ENGINE=innodb");
-
-    return query;
-}
-
-static char *psDBGenerateSelectRowSQL(const char *tableName, const char *col, psMetadata *where, const psU64 limit)
-{
-    char            *query = NULL;
-    char            *whereSQL;
-    char            *limitString;
-
-    // select all columns if col is NULL
-    if (col) {
-        psStringAppend(&query, "SELECT %s FROM %s", col, tableName);
-    } else {
-        psStringAppend(&query, "SELECT * FROM %s", tableName);
-    }
-
-    // select all rows if where is NULL
-    if (where) {
-        whereSQL = psDBGenerateWhereSQL(where);
-        if (!whereSQL) {
-            psFree(query);
-
-            return NULL;
-        }
-        psStringAppend(&query, " %s", whereSQL);
-        psFree(whereSQL);
-    }
-
-    // treat limit == 0 as "no limit"
-    if (limit) {
-        limitString = psDBIntToString(limit);
-        psStringAppend(&query, " LIMIT %s", limitString);
-        psFree(limitString);
-    }
-
-    return query;
-}
-
-#define PS_NAN_ALLOC(dest, type, nan) \
-    dest = psAlloc(sizeof(type)); \
-    *(type *)dest = nan;
-
-static psPtr psDBGetPTypeNaN(psElemType pType)
-{
-    psPtr           myNaN;
-
-    switch (pType) {
-        case PS_TYPE_S8:
-            PS_NAN_ALLOC(myNaN, psS8, PS_MAX_S8);
-            break;
-        case PS_TYPE_S16:
-            PS_NAN_ALLOC(myNaN, psS16, PS_MAX_S16);
-            break;
-        case PS_TYPE_S32:
-            PS_NAN_ALLOC(myNaN, psS32, PS_MAX_S32);
-            break;
-        case PS_TYPE_S64:
-            PS_NAN_ALLOC(myNaN, psS64, PS_MAX_S64);
-            break;
-        case PS_TYPE_U8:
-            PS_NAN_ALLOC(myNaN, psU8, PS_MAX_U8);
-            break;
-        case PS_TYPE_U16:
-            PS_NAN_ALLOC(myNaN, psU16, PS_MAX_U16);
-            break;
-        case PS_TYPE_U32:
-            PS_NAN_ALLOC(myNaN, psU32, PS_MAX_U32);
-            break;
-        case PS_TYPE_U64:
-            PS_NAN_ALLOC(myNaN, psU64, PS_MAX_U64);
-            break;
-        case PS_TYPE_F32:
-            PS_NAN_ALLOC(myNaN, psF32, NAN);
-            break;
-        case PS_TYPE_F64:
-            PS_NAN_ALLOC(myNaN, psF64, NAN);
-            break;
-        case PS_TYPE_C32:
-            // this is a bogus SQL type
-            PS_NAN_ALLOC(myNaN, psC32, NAN);
-            break;
-        case PS_TYPE_C64:
-            // this is a bogus SQL type
-            PS_NAN_ALLOC(myNaN, psC64, NAN);
-            break;
-        case PS_TYPE_BOOL:
-            // what is NaN for a bool?
-            break;
-        case PS_TYPE_PTR:
-            PS_NAN_ALLOC(myNaN, char, '\0');
-            break;
-    }
-
-    return myNaN;
-}
-
-#define PS_IS_NAN(type, data, nan) *(type *)data == nan
-
-static bool psDBIsPTypeNaN(psElemType pType, psPtr data)
-{
-    bool    isNaN;
-
-    switch (pType) {
-        case PS_TYPE_S8:
-            isNaN = PS_IS_NAN(psS8, data, PS_MAX_S8);
-            break;
-        case PS_TYPE_S16:
-            isNaN = PS_IS_NAN(psS16, data, PS_MAX_S16);
-            break;
-        case PS_TYPE_S32:
-            isNaN = PS_IS_NAN(psS32, data, PS_MAX_S32);
-            break;
-        case PS_TYPE_S64:
-            isNaN = PS_IS_NAN(psS64, data, PS_MAX_S64);
-            break;
-        case PS_TYPE_U8:
-            isNaN = PS_IS_NAN(psU8, data, PS_MAX_U8);
-            break;
-        case PS_TYPE_U16:
-            isNaN = PS_IS_NAN(psU16, data, PS_MAX_U16);
-            break;
-        case PS_TYPE_U32:
-            isNaN = PS_IS_NAN(psU32, data, PS_MAX_U32);
-            break;
-        case PS_TYPE_U64:
-            isNaN = PS_IS_NAN(psU64, data, PS_MAX_U64);
-            break;
-        case PS_TYPE_F32:
-            isNaN = PS_IS_NAN(psF32, data, NAN);
-            break;
-        case PS_TYPE_F64:
-            isNaN = PS_IS_NAN(psF64, data, NAN);
-            break;
-        case PS_TYPE_C32:
-            // this is a bogus SQL type
-            isNaN = PS_IS_NAN(psC32, data, NAN);
-            break;
-        case PS_TYPE_C64:
-            // this is a bogus SQL type
-            isNaN = PS_IS_NAN(psC64, data, NAN);
-            break;
-        case PS_TYPE_BOOL:
-            // what is NaN for a bool?
-            break;
-        case PS_TYPE_PTR:
-            isNaN = PS_IS_NAN(char, data, '\0');
-            break;
-    }
-
-    return isNaN;
-}
-
-#define PS_PTYPE_TO_STR(type, format, data) \
-    (size_t)log10((double)*(type *)data) + 1; \
-    string = psAlloc(length); \
-    snprintf(string, length, format, *(type *)data);
-
-static char *psDBPTypeAsString(psPtr data, psElemType pType)
-{
-    size_t          length;
-    char            *string;
-
-    // formats are a portability issue
-    switch (pType) {
-        case PS_TYPE_S8:
-            length = PS_PTYPE_TO_STR(psS8, "%i", data);
-            break;
-        case PS_TYPE_S16:
-            length = PS_PTYPE_TO_STR(psS16, "%i", data);
-            break;
-        case PS_TYPE_S32:
-            length = PS_PTYPE_TO_STR(psS32, "%i", data);
-            break;
-        case PS_TYPE_S64:
-            length = PS_PTYPE_TO_STR(psS64, "%li", data);
-            break;
-        case PS_TYPE_U8:
-            length = PS_PTYPE_TO_STR(psU8, "%u", data);
-            break;
-        case PS_TYPE_U16:
-            length = PS_PTYPE_TO_STR(psU16, "%u", data);
-            break;
-        case PS_TYPE_U32:
-            length = PS_PTYPE_TO_STR(psU32, "%u", data);
-            break;
-        case PS_TYPE_U64:
-            length = PS_PTYPE_TO_STR(psU64, "%lu", data);
-            break;
-        case PS_TYPE_F32:
-            length = PS_PTYPE_TO_STR(psF32, "%f", data);
-            break;
-        case PS_TYPE_F64:
-            length = PS_PTYPE_TO_STR(psF32, "%f", data);
-            break;
-        case PS_TYPE_C32:
-            // this is a bogus SQL type
-            break;
-        case PS_TYPE_C64:
-            // this is a bogus SQL type
-            break;
-        case PS_TYPE_BOOL:
-            length = PS_PTYPE_TO_STR(bool, "%u", data);
-            break;
-        case PS_TYPE_PTR:
-            // unsafe to convert unless it's a string
-            break;
-    }
-
-    return string;
-}
-
-static bool psDBRunQuery(psDB *dbh, const char *query)
-{
-    if (mysql_real_query(dbh->mysql, query, (unsigned long)strlen(query)) !=0) {
-        fprintf(stderr, "Failed to execute query.  Error: %s\n", mysql_error(dbh->mysql));
-           
-        return false;
-    }
-
-    return true;
-}
-
-static ssize_t psStringAppend(char **dest, const char *format, ...)
-{
-    va_list         args;
-    size_t          length;             // complete string length (sans \0)
-    size_t          oldLength;          // original string length (sans \0)
-    ssize_t         tailLength;         // length of string to append
-
-    if (!*dest) {
-        *dest = psStringCopy("");
-        oldLength = 0;
-    } else {
-        // size of existing string
-        oldLength = strlen(*dest);
-    }
-
-    // find the size of the string to append
-    va_start(args, format);
-    // C99 guarentees vsnprintf() to work as expected with size = 0
-    tailLength = vsnprintf(*dest, 0, format, args);
-    va_end(args);
-
-    // if the new tail is zero length, return the length of the old string.  if
-    // it's a format error, return the error code.
-    if (tailLength < 1) {
-        return tailLength == 0 ? oldLength : tailLength;
-    }
-
-    // new string length (sans \0)
-    length = oldLength + tailLength;
-
-    // realloc string to string + tail + \0
-    *dest = psRealloc(*dest, length + 1);
-
-    // append tail + \0
-    va_start(args, format);
-    vsnprintf(*dest + oldLength, tailLength + 1, format, args);
-    va_end(args);
-
-    return length;
-}
-
-static ssize_t psStringPrepend(char **dest, const char *format, ...)
-{
-    va_list         args;
-    size_t          length;             // complete string length (sans \0)
-    ssize_t         headLength;         // length of string to prepend
-    char            *oldDest;           // copy of original string
-
-    if (!*dest) {
-        // makes the string backup and concatination pointless 
-        *dest = psStringCopy("");
-        length = 0;
-    } else {
-        // size of existing string
-        length = strlen(*dest);
-    }
-
-    // find the size of the string to prepend
-    va_start(args, format);
-    // C99 guarentees vsnprintf() to work as expected with size = 0
-    headLength = vsnprintf(*dest, 0, format, args);
-    va_end(args);
-
-    // if the new head is zero length, return the length of the old string.  if
-    // it's a format error, return the error code.
-    if (headLength < 1) {
-        return headLength == 0 ? length : headLength;
-    }
-
-    // backup original string
-    oldDest = psStringCopy(*dest);
-
-    // new string length (sans \0)
-    length += headLength;
-
-    // realloc string to head + string + \0
-    *dest = psRealloc(*dest, length + 1);
-
-    // copy the new head to the beginning of string
-    va_start(args, format);
-    vsnprintf(*dest, length + 1, format, args);
-    va_end(args);
-
-    // append the original string
-    strncat(*dest, oldDest, length + 1);
-
-    psFree(oldDest);
-
-    return length;
-}
+
+// lookup table functions
+/*****************************************************************************/
 
 static psU32 psDBMySQLToPType(psU32 type, psU32 flags)
@@ -1530,4 +1287,185 @@
 }
 
+
+// pType utility functions
+/*****************************************************************************/
+
+#define PS_NAN_ALLOC(dest, type, nan) \
+    dest = psAlloc(sizeof(type)); \
+    *(type *)dest = nan;
+
+static psPtr psDBGetPTypeNaN(psElemType pType)
+{
+    psPtr           myNaN;
+
+    switch (pType) {
+        case PS_TYPE_S8:
+            PS_NAN_ALLOC(myNaN, psS8, PS_MAX_S8);
+            break;
+        case PS_TYPE_S16:
+            PS_NAN_ALLOC(myNaN, psS16, PS_MAX_S16);
+            break;
+        case PS_TYPE_S32:
+            PS_NAN_ALLOC(myNaN, psS32, PS_MAX_S32);
+            break;
+        case PS_TYPE_S64:
+            PS_NAN_ALLOC(myNaN, psS64, PS_MAX_S64);
+            break;
+        case PS_TYPE_U8:
+            PS_NAN_ALLOC(myNaN, psU8, PS_MAX_U8);
+            break;
+        case PS_TYPE_U16:
+            PS_NAN_ALLOC(myNaN, psU16, PS_MAX_U16);
+            break;
+        case PS_TYPE_U32:
+            PS_NAN_ALLOC(myNaN, psU32, PS_MAX_U32);
+            break;
+        case PS_TYPE_U64:
+            PS_NAN_ALLOC(myNaN, psU64, PS_MAX_U64);
+            break;
+        case PS_TYPE_F32:
+            PS_NAN_ALLOC(myNaN, psF32, NAN);
+            break;
+        case PS_TYPE_F64:
+            PS_NAN_ALLOC(myNaN, psF64, NAN);
+            break;
+        case PS_TYPE_C32:
+            // this is a bogus SQL type
+            PS_NAN_ALLOC(myNaN, psC32, NAN);
+            break;
+        case PS_TYPE_C64:
+            // this is a bogus SQL type
+            PS_NAN_ALLOC(myNaN, psC64, NAN);
+            break;
+        case PS_TYPE_BOOL:
+            // what is NaN for a bool?
+            break;
+        case PS_TYPE_PTR:
+            PS_NAN_ALLOC(myNaN, char, '\0');
+            break;
+    }
+
+    return myNaN;
+}
+
+#define PS_IS_NAN(type, data, nan) *(type *)data == nan
+
+static bool psDBIsPTypeNaN(psElemType pType, psPtr data)
+{
+    bool    isNaN;
+
+    switch (pType) {
+        case PS_TYPE_S8:
+            isNaN = PS_IS_NAN(psS8, data, PS_MAX_S8);
+            break;
+        case PS_TYPE_S16:
+            isNaN = PS_IS_NAN(psS16, data, PS_MAX_S16);
+            break;
+        case PS_TYPE_S32:
+            isNaN = PS_IS_NAN(psS32, data, PS_MAX_S32);
+            break;
+        case PS_TYPE_S64:
+            isNaN = PS_IS_NAN(psS64, data, PS_MAX_S64);
+            break;
+        case PS_TYPE_U8:
+            isNaN = PS_IS_NAN(psU8, data, PS_MAX_U8);
+            break;
+        case PS_TYPE_U16:
+            isNaN = PS_IS_NAN(psU16, data, PS_MAX_U16);
+            break;
+        case PS_TYPE_U32:
+            isNaN = PS_IS_NAN(psU32, data, PS_MAX_U32);
+            break;
+        case PS_TYPE_U64:
+            isNaN = PS_IS_NAN(psU64, data, PS_MAX_U64);
+            break;
+        case PS_TYPE_F32:
+            isNaN = PS_IS_NAN(psF32, data, NAN);
+            break;
+        case PS_TYPE_F64:
+            isNaN = PS_IS_NAN(psF64, data, NAN);
+            break;
+        case PS_TYPE_C32:
+            // this is a bogus SQL type
+            isNaN = PS_IS_NAN(psC32, data, NAN);
+            break;
+        case PS_TYPE_C64:
+            // this is a bogus SQL type
+            isNaN = PS_IS_NAN(psC64, data, NAN);
+            break;
+        case PS_TYPE_BOOL:
+            // what is NaN for a bool?
+            break;
+        case PS_TYPE_PTR:
+            isNaN = PS_IS_NAN(char, data, '\0');
+            break;
+    }
+
+    return isNaN;
+}
+
+#define PS_PTYPE_TO_STR(type, format, data) \
+    (size_t)log10((double)*(type *)data) + 1; \
+    string = psAlloc(length); \
+    snprintf(string, length, format, *(type *)data);
+
+static char *psDBPTypeAsString(psPtr data, psElemType pType)
+{
+    size_t          length;
+    char            *string;
+
+    // formats are a portability issue
+    switch (pType) {
+        case PS_TYPE_S8:
+            length = PS_PTYPE_TO_STR(psS8, "%i", data);
+            break;
+        case PS_TYPE_S16:
+            length = PS_PTYPE_TO_STR(psS16, "%i", data);
+            break;
+        case PS_TYPE_S32:
+            length = PS_PTYPE_TO_STR(psS32, "%i", data);
+            break;
+        case PS_TYPE_S64:
+            length = PS_PTYPE_TO_STR(psS64, "%li", data);
+            break;
+        case PS_TYPE_U8:
+            length = PS_PTYPE_TO_STR(psU8, "%u", data);
+            break;
+        case PS_TYPE_U16:
+            length = PS_PTYPE_TO_STR(psU16, "%u", data);
+            break;
+        case PS_TYPE_U32:
+            length = PS_PTYPE_TO_STR(psU32, "%u", data);
+            break;
+        case PS_TYPE_U64:
+            length = PS_PTYPE_TO_STR(psU64, "%lu", data);
+            break;
+        case PS_TYPE_F32:
+            length = PS_PTYPE_TO_STR(psF32, "%f", data);
+            break;
+        case PS_TYPE_F64:
+            length = PS_PTYPE_TO_STR(psF32, "%f", data);
+            break;
+        case PS_TYPE_C32:
+            // this is a bogus SQL type
+            break;
+        case PS_TYPE_C64:
+            // this is a bogus SQL type
+            break;
+        case PS_TYPE_BOOL:
+            length = PS_PTYPE_TO_STR(bool, "%u", data);
+            break;
+        case PS_TYPE_PTR:
+            // unsafe to convert unless it's a string
+            break;
+    }
+
+    return string;
+}
+
+
+// string utility functions
+/*****************************************************************************/
+
 static char *psDBIntToString(psU64 n)
 {
@@ -1544,2 +1482,93 @@
     return string;
 }
+
+static ssize_t psStringAppend(char **dest, const char *format, ...)
+{
+    va_list         args;
+    size_t          length;             // complete string length (sans \0)
+    size_t          oldLength;          // original string length (sans \0)
+    ssize_t         tailLength;         // length of string to append
+
+    if (!*dest) {
+        *dest = psStringCopy("");
+        oldLength = 0;
+    } else {
+        // size of existing string
+        oldLength = strlen(*dest);
+    }
+
+    // find the size of the string to append
+    va_start(args, format);
+    // C99 guarentees vsnprintf() to work as expected with size = 0
+    tailLength = vsnprintf(*dest, 0, format, args);
+    va_end(args);
+
+    // if the new tail is zero length, return the length of the old string.  if
+    // it's a format error, return the error code.
+    if (tailLength < 1) {
+        return tailLength == 0 ? oldLength : tailLength;
+    }
+
+    // new string length (sans \0)
+    length = oldLength + tailLength;
+
+    // realloc string to string + tail + \0
+    *dest = psRealloc(*dest, length + 1);
+
+    // append tail + \0
+    va_start(args, format);
+    vsnprintf(*dest + oldLength, tailLength + 1, format, args);
+    va_end(args);
+
+    return length;
+}
+
+static ssize_t psStringPrepend(char **dest, const char *format, ...)
+{
+    va_list         args;
+    size_t          length;             // complete string length (sans \0)
+    ssize_t         headLength;         // length of string to prepend
+    char            *oldDest;           // copy of original string
+
+    if (!*dest) {
+        // makes the string backup and concatination pointless 
+        *dest = psStringCopy("");
+        length = 0;
+    } else {
+        // size of existing string
+        length = strlen(*dest);
+    }
+
+    // find the size of the string to prepend
+    va_start(args, format);
+    // C99 guarentees vsnprintf() to work as expected with size = 0
+    headLength = vsnprintf(*dest, 0, format, args);
+    va_end(args);
+
+    // if the new head is zero length, return the length of the old string.  if
+    // it's a format error, return the error code.
+    if (headLength < 1) {
+        return headLength == 0 ? length : headLength;
+    }
+
+    // backup original string
+    oldDest = psStringCopy(*dest);
+
+    // new string length (sans \0)
+    length += headLength;
+
+    // realloc string to head + string + \0
+    *dest = psRealloc(*dest, length + 1);
+
+    // copy the new head to the beginning of string
+    va_start(args, format);
+    vsnprintf(*dest, length + 1, format, args);
+    va_end(args);
+
+    // append the original string
+    strncat(*dest, oldDest, length + 1);
+
+    psFree(oldDest);
+
+    return length;
+}
