IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 7644


Ignore:
Timestamp:
Jun 22, 2006, 3:51:48 PM (20 years ago)
Author:
jhoblitt
Message:
  • rework the way MYSQL_BIND arrays are handled so that they are wrapped in a structure that keeps track of how many elements are in the array. This allows a custom free function to iterate through the array and free allocated buffers.
  • preliminary psTime support
  • add p_psDBRunQueryPrepared() (not yet used)
Location:
trunk/psLib/src/db
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/psLib/src/db/psDB.c

    r7626 r7644  
    1212 *  @author Joshua Hoblitt
    1313 *
    14  *  @version $Revision: 1.60 $ $Name: not supported by cvs2svn $
    15  *  @date $Date: 2006-06-22 00:36:22 $
     14 *  @version $Revision: 1.61 $ $Name: not supported by cvs2svn $
     15 *  @date $Date: 2006-06-23 01:51:48 $
    1616 *
    1717 *  Copyright 2005 Joshua Hoblitt, University of Hawaii
     
    5353mysqlType;
    5454
     55typedef struct
     56{
     57    MYSQL_BIND      *bind;
     58    psU32           n;
     59}
     60psDBMysqlRow;
     61
    5562static pthread_mutex_t lookupTableMutex = PTHREAD_MUTEX_INITIALIZER;
    5663static psHash   *pTypeToSQLlookupTable = NULL;
     
    6067
    6168// database utility functions
    62 static inline bool psDBPackRow(MYSQL_BIND *bind, const psMetadata *values, psU32 paramCount);
     69static inline bool psDBPackMySQLRow(psDBMysqlRow *mysqlRow, const psMetadata *values);
     70static psDBMysqlRow *psDBMysqlRowAlloc(psU32 paramCount);
     71static void psDBMysqlRowFree(psDBMysqlRow *row);
    6372
    6473// SQL generation functions
     
    564573                    const psArray *rowSet)
    565574{
    566     psMetadata      *row;               // row of data
    567575    char            *query;             // SQL query
    568576    MYSQL_STMT      *stmt;              // prepared db statement
    569     MYSQL_BIND      *bind;              // field values to insert
    570577    unsigned long   paramCount;         // number of placeholders in query
    571578    psU64           j;                  // row index
     
    585592    // we are assuming that all rows in the set have an identical with reguard
    586593    // to field count and type
    587     row = rowSet->data[0];
     594    psMetadata *row = rowSet->data[0];
    588595
    589596    // Generate SQL query string
     
    611618    paramCount = mysql_stmt_param_count(stmt);
    612619
    613     // structure larger enough to hold one field of data per place holder
    614     bind = psAlloc(sizeof(MYSQL_BIND) * paramCount);
     620    // structure large enough to hold one field of data per place holder
     621    psDBMysqlRow *mysqlRow = psDBMysqlRowAlloc(paramCount);
    615622
    616623    // loop over rows
    617624    for (j = 0; j < rowSet->n; j++) {
    618         row = rowSet->data[j];
    619 
    620         // reset bind for each row
    621         memset(bind, 0, sizeof(MYSQL_BIND) * paramCount);
    622 
    623         if (!psDBPackRow(bind, row, paramCount)) {
     625        psMetadata *rowData = rowSet->data[j];
     626
     627        if (!psDBPackMySQLRow(mysqlRow, rowData)) {
    624628            psError(PS_ERR_UNKNOWN, false, "Failed to pack params into bind structure.");
    625629
    626630            mysql_rollback(dbh->mysql);
    627631
    628             psFree(bind);
     632            psFree(mysqlRow);
    629633            mysql_stmt_close(stmt);
    630         }
    631 
    632         if (mysql_stmt_bind_param(stmt, bind)) {
     634
     635            return false;
     636        }
     637
     638        if (mysql_stmt_bind_param(stmt, mysqlRow->bind)) {
    633639            psError(PS_ERR_UNKNOWN, true, "Failed to bind params.  Error: %s", mysql_stmt_error(stmt));
    634640
    635641            mysql_rollback(dbh->mysql);
    636642
    637             psFree(bind);
     643            psFree(mysqlRow);
    638644            mysql_stmt_close(stmt);
    639645
     
    647653            mysql_rollback(dbh->mysql);
    648654
    649             psFree(bind);
     655            psFree(mysqlRow);
    650656            mysql_stmt_close(stmt);
    651657
     
    654660    } // end loop over rows
    655661
    656     psFree(bind);
     662    psFree(mysqlRow);
    657663    mysql_stmt_close(stmt);
    658664
     
    768774
    769775    // structure large enough to hold one field of data per place holder
    770     bind = psAlloc(sizeof(MYSQL_BIND) * paramCount);
    771 
    772     // init bind
    773     memset(bind, 0, sizeof(MYSQL_BIND) * paramCount);
    774 
    775     if (!psDBPackRow(bind, values, paramCount)) {
    776         psFree(bind);
     776    psDBMysqlRow *mysqlRow = psDBMysqlRowAlloc(paramCount);
     777
     778    if (!psDBPackMySQLRow(mysqlRow, values)) {
     779        psFree(mysqlRow);
    777780        mysql_stmt_close(stmt);
    778781        return -1;
     
    782785        psError(PS_ERR_UNKNOWN, true, "Failed to bind params.  Error: %s", mysql_stmt_error(stmt));
    783786        mysql_rollback(dbh->mysql);
    784         psFree(bind);
     787        psFree(mysqlRow);
    785788        mysql_stmt_close(stmt);
    786789        return -1;
     
    791794                mysql_stmt_error(stmt));
    792795        mysql_rollback(dbh->mysql);
    793         psFree(bind);
     796        psFree(mysqlRow);
    794797        mysql_stmt_close(stmt);
    795798        return -1;
    796799    }
    797800
    798     psFree(bind);
     801    psFree(mysqlRow);
    799802
    800803    // mysql_stmt_affected_rows() must be called before a commit
     
    948951    psFree(dest);
    949952    return true;
     953}
     954
     955long p_psDBRunQueryPrepared(psDB *dbh,
     956                            const psArray *rowSet,
     957                            const char *format,
     958                            ...)
     959{
     960    va_list argPtr;
     961    char *query = NULL;
     962
     963    PS_ASSERT_PTR_NON_NULL(dbh, -1);
     964    PS_ASSERT_PTR_NON_NULL(rowSet, -1);
     965    PS_ASSERT_PTR_NON_NULL(format, -1);
     966
     967    // generate query string
     968    va_start(argPtr, format);
     969    if(argPtr == NULL) {
     970        query = psStringCopy(format);
     971    } else {
     972        int destSize = vsnprintf(query, 0, format, argPtr);
     973        query = psAlloc(destSize + 1);
     974        vsnprintf(query, destSize + 1, format, argPtr);
     975    }
     976    va_end(argPtr);
     977
     978    // Prepare SQL statement
     979    MYSQL_STMT *stmt = mysql_stmt_init(dbh->mysql);
     980    if (!stmt) {
     981        psAbort(__func__, "mysql_stmt_init(), out of memory.");
     982    }
     983    if (mysql_stmt_prepare(stmt, query, (unsigned long)strlen(query))) {
     984        psError(PS_ERR_UNKNOWN, true, "Failed to prepare query.  Error: %s", mysql_stmt_error(stmt));
     985        mysql_stmt_close(stmt);
     986        psFree(query);
     987        return -1;
     988    }
     989
     990    psFree(query);
     991
     992    // how many place holders are in our query
     993    psS32 paramCount = mysql_stmt_param_count(stmt);
     994
     995    // structure large enough to hold one field of data per place holder
     996    psDBMysqlRow *mysqlRow = psDBMysqlRowAlloc(paramCount);
     997
     998    // loop over rows
     999    for (long j = 0; j < rowSet->n; j++) {
     1000        psMetadata *rowData = rowSet->data[j];
     1001
     1002        if (!psDBPackMySQLRow(mysqlRow, rowData)) {
     1003            psError(PS_ERR_UNKNOWN, false, "Failed to pack params into bind structure.");
     1004
     1005            psFree(mysqlRow);
     1006            mysql_stmt_close(stmt);
     1007
     1008            return -1;
     1009        }
     1010
     1011        if (mysql_stmt_bind_param(stmt, mysqlRow->bind)) {
     1012            psError(PS_ERR_UNKNOWN, true, "Failed to bind params.  Error: %s", mysql_stmt_error(stmt));
     1013
     1014            psFree(mysqlRow);
     1015            mysql_stmt_close(stmt);
     1016
     1017            return -1;
     1018        }
     1019
     1020        if (mysql_stmt_execute(stmt)) {
     1021            psError(PS_ERR_UNKNOWN, true, "Failed to execute prepared statement.  Error: %s", mysql_stmt_error(stmt));
     1022
     1023            psFree(mysqlRow);
     1024            mysql_stmt_close(stmt);
     1025
     1026            return -1;
     1027        }
     1028    } // end loop over rows
     1029
     1030    psFree(mysqlRow);
     1031
     1032    // FYI mysql_stmt_affected_rows() must be called before a commit
     1033    long rowsAffected = mysql_stmt_affected_rows(stmt);
     1034
     1035    mysql_stmt_close(stmt);
     1036
     1037    return rowsAffected;
    9501038}
    9511039
     
    10261114            } else if (pType == PS_DATA_BOOL) {
    10271115                psMetadataAdd(md, PS_LIST_TAIL, field[i].name, pType, "", atoi(data));
     1116            } else if (pType == PS_DATA_TIME) {
     1117                psMetadataAddTime(md, PS_LIST_TAIL, field[i].name, 0, "",
     1118                                  psTimeFromISO(data, PS_TIME_UTC));
    10281119            } else {
    10291120                // XXX: assume binary string ...
     
    10441135}
    10451136
    1046 static inline bool psDBPackRow(MYSQL_BIND *bind,
    1047                                const psMetadata *values,
    1048                                psU32 paramCount)
    1049 {
    1050     psListIterator  *cursor;            // row iterator
    1051     psMetadataItem  *item;              // field in row
     1137static inline bool psDBPackMySQLRow(psDBMysqlRow *mysqlRow,
     1138                                    const psMetadata *values)
     1139{
    10521140    mysqlType       *mType;             // type tmp variable
    10531141    static bool     isNull = true;      // used in a MYSQL_BIND to indicate NULL,
    1054     // this will be used outside of this func
    1055     psU32           i;                  // field index
    1056 
    1057     // Verify values or bind are not null
    1058     if((values == NULL) || (bind == NULL)) {
    1059         return false;
    1060     }
    1061 
     1142
     1143    PS_ASSERT_PTR_NON_NULL(mysqlRow, false);
     1144    PS_ASSERT_PTR_NON_NULL(values, false);
     1145
     1146    MYSQL_BIND *bind = mysqlRow->bind;
     1147
     1148    // row iterator
    10621149    // check size of values == paramCount ?
    1063     cursor = psListIteratorAlloc(values->list, 0, false);
     1150    psListIterator *cursor = psListIteratorAlloc(values->list, 0, false);
    10641151
    10651152    // loop over fields
    1066     i = 0;
     1153    psU32 i = 0;
     1154    psMetadataItem *item = NULL;              // field in row
    10671155    while ((item = psListGetAndIncrement(cursor))) {
    10681156        // lookup pType -> mysql type
     
    11121200                bind[i].buffer_length = (unsigned long)strlen((char *)item->data.V);
    11131201                bind[i].length  = &bind[i].buffer_length;
    1114                 bind[i].buffer  = item->data.V;
     1202                bind[i].buffer  = psStringCopy(item->data.V);
    11151203                bind[i].is_null = *(char *)item->data.V == '\0'
    11161204                                  ? (my_bool *)&isNull
    11171205                                  : NULL;
     1206            } else {
     1207                // handles the case of NULL as a NULL database value
     1208                bind[i].buffer_length = 0;
     1209                bind[i].length  = &bind[i].buffer_length;
     1210                bind[i].buffer  = NULL;
     1211                bind[i].is_null = (my_bool *)&isNull;
     1212            }
     1213        } else if (item->type == PS_DATA_TIME) {
     1214            // XXX we're abusing the comment field of the metadata item here as
     1215            // we need to have a buffer that exists outside this functions
     1216            // scope without leaking memory
     1217            // make a copy of the psTime so we don't modify user data when we
     1218            // try to do the conversion
     1219            if ((char *)item->data.V) {
     1220                psTime *time = p_psTimeCopy((psTime *)item->data.V);
     1221                psTimeConvert(time, PS_TIME_UTC);
     1222                bind[i].buffer  = psTimeToISO(time);
     1223                psFree(time);
     1224                bind[i].buffer_length = (unsigned long)strlen((char *)bind[i].buffer);
     1225                bind[i].length  = &bind[i].buffer_length;
     1226                bind[i].is_null = NULL;
    11181227            } else {
    11191228                // handles the case of NULL as a NULL database value
     
    11271236                    "FIXME: Only type of PS_TYPE_S32 (PS_DATA_S32), "
    11281237                    "PS_TYPE_F32 (PS_DATA_F32), PS_TYPE_F64 (PS_DATA_F64), "
    1129                     "PS_TYPE_BOOL (PS_DATA_BOOL), "
    1130                     "and PS_DATA_STRING are supported.");
     1238                    "PS_TYPE_BOOL (PS_DATA_BOOL), PS_DATA_STRING"
     1239                    "and PS_DATA_TIME are supported.");
    11311240
    11321241            psFree(cursor);
     
    11421251
    11431252    return true;
     1253}
     1254
     1255static psDBMysqlRow *psDBMysqlRowAlloc(psU32 paramCount)
     1256{
     1257    psDBMysqlRow *row = psAlloc(sizeof(psDBMysqlRow));
     1258    psMemSetDeallocator(row, (psFreeFunc)psDBMysqlRowFree);
     1259
     1260    row->bind = psAlloc(sizeof(MYSQL_BIND) * paramCount);
     1261    memset(row->bind, 0, sizeof(MYSQL_BIND) * paramCount);
     1262    row->n = paramCount;
     1263
     1264    return row;
     1265}
     1266
     1267static void psDBMysqlRowFree(psDBMysqlRow *mysqlRow)
     1268{
     1269    MYSQL_BIND *bind = mysqlRow->bind;
     1270
     1271    for (psU32 i = 0; i < mysqlRow->n; i++) {
     1272        // buffer_length is only defined for pointers to character buffers
     1273        // primitive types will have this value set to zero.
     1274        if (bind[i].buffer_length) {
     1275            psFree(bind[i].buffer);
     1276        }
     1277    }
     1278
     1279    psFree(bind);
    11441280}
    11451281
     
    16951831        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_TYPE_BOOL,"TINYINT");
    16961832        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_STRING, "VARCHAR");
     1833        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_TIME, "DATETIME");
    16971834    } else {
    16981835        // increment the ref count by one for every psDB
     
    18431980
    18441981        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_STRING,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
     1982        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_TIME,      psDBMySQLTypeAlloc(MYSQL_TYPE_DATETIME, false));
    18451983        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_VECTOR,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
    18461984        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_IMAGE,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
  • trunk/psLib/src/db/psDB.h

    r7386 r7644  
    1010 *  @author Joshua Hoblitt
    1111 *
    12  *  @version $Revision: 1.18 $ $Name: not supported by cvs2svn $
    13  *  @date $Date: 2006-06-07 03:40:43 $
     12 *  @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
     13 *  @date $Date: 2006-06-23 01:51:48 $
    1414 *
    1515 *  Copyright 2005 Joshua Hoblitt, University of Hawaii
     
    9494    const char *format,                ///< SQL string to execute
    9595    ...                                ///< Arguments for name formatting and metadata item data.
     96);
     97
     98/** Executes a SQL query as a prepared statement
     99 *
     100 * This function will execute a string as a raw SQL query.  No additional
     101 * processing of the string or abstraction of the underlying database's SQL
     102 * dialect is provided.  Caveat emptor.
     103 *
     104 * @return long:    the number of database rows affected
     105 */
     106long p_psDBRunQueryPrepared(
     107    psDB *dbh,                          ///< Database handle
     108    const psArray *rowSet,              ///< row data as psArray of psMetadata
     109    const char *format,                 ///< SQL string to execute
     110    ...
    96111);
    97112
Note: See TracChangeset for help on using the changeset viewer.