/** * Convert rows from mysql to db API representation */ static inline int db_mysql_convert_rows(const db_con_t* _h, db_res_t* _r) { int row; if ((!_h) || (!_r)) { LM_ERR("invalid parameter\n"); return -1; } if (CON_HAS_PS(_h)) { RES_ROW_N(_r) = mysql_stmt_num_rows(CON_PS_STMT(_h)); } else { RES_ROW_N(_r) = mysql_num_rows(CON_RESULT(_h)); } if (!RES_ROW_N(_r)) { LM_DBG("no rows returned from the query\n"); RES_ROWS(_r) = 0; return 0; } if (db_allocate_rows( _r, RES_ROW_N(_r))!=0) { LM_ERR("no private memory left\n"); return -2; } for(row = 0; row < RES_ROW_N(_r); row++) { if (CON_HAS_PS(_h)) { mysql_stmt_fetch(CON_PS_STMT(_h)); //if(mysql_stmt_fetch(CON_PS_STMT(_h))!=1) // LM_ERR("STMT ERR=%s\n",mysql_stmt_error(CON_PS_STMT(_h))); } else { CON_ROW(_h) = mysql_fetch_row(CON_RESULT(_h)); if (!CON_ROW(_h)) { LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h))); RES_ROW_N(_r) = row; db_free_rows(_r); return -3; } } if (db_mysql_convert_row(_h, _r, &(RES_ROWS(_r)[row])) < 0) { LM_ERR("error while converting row #%d\n", row); RES_ROW_N(_r) = row; db_free_rows(_r); return -4; } } return 0; }
/* * Print values of SQL statement */ int db_print_values(const db_con_t* _c, char* _b, const int _l, const db_val_t* _v, const int _n, int (*val2str)(const db_con_t*, const db_val_t*, char*, int*)) { int i, l, len = 0; if (!_c || !_b || !_l || !_v || !_n) { LM_ERR("Invalid parameter value\n"); return -1; } for(i = 0; i < _n; i++) { if (CON_HAS_PS(_c)) { *(_b+len++) = '?'; } else { l = _l - len; if ( (*val2str)(_c, _v + i, _b + len, &l) < 0) { LM_ERR("Error while converting value to string\n"); return -1; } len += l; } if (i != (_n - 1)) { *(_b + len) = ','; len++; } } return len; }
/* * Print where clause of SQL statement */ int db_print_where(const db_con_t* _c, char* _b, const int _l, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*)) { int i, l, ret, len = 0; if (!_c || !_b || !_l || !_k || !_v || !_n) { LM_ERR("Invalid parameter value\n"); return -1; } for(i = 0; i < _n; i++) { if (_o) { ret = snprintf(_b + len, _l - len, "%.*s%s", _k[i]->len, _k[i]->s, _o[i]); if (ret < 0 || ret >= (_l - len)) goto error; len += ret; } else { ret = snprintf(_b + len, _l - len, "%.*s=", _k[i]->len, _k[i]->s); if (ret < 0 || ret >= (_l - len)) goto error; len += ret; } if (CON_HAS_PS(_c)) { *(_b+len++) = '?'; } else { l = _l - len; if ( (*val2str)(_c, &(_v[i]), _b + len, &l) < 0) { LM_ERR("Error while converting value to string\n"); return -1; } len += l; } if (i != (_n - 1)) { ret = snprintf(_b + len, _l - len, " AND "); if (ret < 0 || ret >= (_l - len)) goto error; len += ret; } } return len; error: LM_ERR("Error in snprintf\n"); return -1; }
/** * Convert a row from result into db API representation */ int db_mysql_convert_row(const db_con_t* _h, db_res_t* _res, db_row_t* _r) { unsigned long* lengths; int i; if ((!_h) || (!_res) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } /* Save the number of columns in the ROW structure */ ROW_N(_r) = RES_COL_N(_res); if (CON_HAS_PS(_h)) { for(i=0; i < CON_MYSQL_PS(_h)->cols_out; i++) { if (db_mysql_str2val(RES_TYPES(_res)[i], &(ROW_VALUES(_r)[i]), CON_PS_OUTCOL(_h, i).null?NULL:CON_PS_OUTCOL(_h, i).buf, CON_PS_OUTCOL(_h,i).len) < 0) { LM_ERR("failed to convert value from stmt\n"); db_free_row(_r); return -3; } } } else { lengths = mysql_fetch_lengths(CON_RESULT(_h)); for(i = 0; i < RES_COL_N(_res); i++) { if (db_mysql_str2val(RES_TYPES(_res)[i], &(ROW_VALUES(_r)[i]), ((MYSQL_ROW)CON_ROW(_h))[i], lengths[i]) < 0) { LM_ERR("failed to convert value\n"); LM_DBG("free row at %p\n", _r); db_free_row(_r); return -3; } } } return 0; }
/** * Get and convert columns from a result */ int db_mysql_get_columns(const db_con_t* _h, db_res_t* _r) { int col; MYSQL_FIELD* fields; if ((!_h) || (!_r)) { LM_ERR("invalid parameter\n"); return -1; } if (CON_HAS_PS(_h)) { RES_COL_N(_r) = CON_MYSQL_PS(_h)->cols_out; } else { RES_COL_N(_r) = mysql_field_count(CON_CONNECTION(_h)); } if (!RES_COL_N(_r)) { LM_ERR("no columns returned from the query\n"); return -2; } else { LM_DBG("%d columns returned from the query\n", RES_COL_N(_r)); } if (db_allocate_columns(_r, RES_COL_N(_r)) != 0) { LM_ERR("could not allocate columns\n"); return -3; } fields = mysql_fetch_fields(CON_RESULT(_h)); for(col = 0; col < RES_COL_N(_r); col++) { /* The pointer that is here returned is part of the result structure */ RES_NAMES(_r)[col]->s = fields[col].name; RES_NAMES(_r)[col]->len = strlen(fields[col].name); LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_r)[col], col, RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s); switch(fields[col].type) { case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: case MYSQL_TYPE_INT24: case MYSQL_TYPE_DECIMAL: #if MYSQL_VERSION_ID > 49999 case MYSQL_TYPE_NEWDECIMAL: #endif case MYSQL_TYPE_TIMESTAMP: LM_DBG("use DB_INT result type\n"); RES_TYPES(_r)[col] = DB_INT; break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: LM_DBG("use DB_DOUBLE result type\n"); RES_TYPES(_r)[col] = DB_DOUBLE; break; case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_DATE: LM_DBG("use DB_DATETIME result type\n"); RES_TYPES(_r)[col] = DB_DATETIME; break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: LM_DBG("use DB_BLOB result type\n"); RES_TYPES(_r)[col] = DB_BLOB; break; case FIELD_TYPE_SET: LM_DBG("use DB_BITMAP result type\n"); RES_TYPES(_r)[col] = DB_BITMAP; break; case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: LM_DBG("use DB_STRING result type\n"); RES_TYPES(_r)[col] = DB_STRING; break; case MYSQL_TYPE_LONGLONG: LM_DBG("use DB_BIGINT result type\n"); RES_TYPES(_r)[col] = DB_BIGINT; break; default: LM_WARN("unhandled data type column (%.*s) type id (%d), " "use DB_STRING as default\n", RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s, fields[col].type); RES_TYPES(_r)[col] = DB_STRING; break; } } return 0; }
int db_do_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*), int (*submit_query)(const db_con_t* _h, const str* _c)) { int off, ret,i,no_rows=0; db_val_t **buffered_rows = NULL; if (!_h || !_k || !_v || !_n || !val2str || !submit_query) { LM_ERR("invalid parameter value\n"); return -1; } /* insert buffering is enabled ? */ if (CON_HAS_INSLIST(_h) && !CON_HAS_PS(_h)) { LM_DBG("inlist %p\n",CON_HAS_INSLIST(_h)); if (IS_INSTANT_FLUSH(_h)) { LM_DBG("timer wishing to flush \n"); /* if caller signals it's flush time ( timer, etc ), * detach rows in queue * the caller is holding the lock at this point */ no_rows = ql_detach_rows_unsafe(_h->ins_list,&buffered_rows); CON_FLUSH_RESET(_h,_h->ins_list); if (no_rows == -1) { LM_ERR("failed to detach rows for insertion\n"); goto error; } if (no_rows > 0) goto build_query; else { /* caller wanted to make sure that everything if flushed * but queue is empty */ return 0; } } /* if connection has prepared statement, leave the row insertion to the proper module func, as the submit_query func provided is a dummy one*/ if ( (no_rows = ql_row_add(_h->ins_list,_v,&buffered_rows)) < 0) { LM_ERR("failed to insert row to buffered list \n"); goto error; } LM_DBG("no rows = %d\n",no_rows); if (no_rows == 0) { /* wait for queries to pile up */ return 0; } } build_query: ret = snprintf(sql_buf, SQL_BUF_LEN, "insert into %.*s (", CON_TABLE(_h)->len, CON_TABLE(_h)->s); if (ret < 0 || ret >= SQL_BUF_LEN) goto error; off = ret; ret = db_print_columns(sql_buf + off, SQL_BUF_LEN - off, _k, _n); if (ret < 0) goto error; off += ret; if (CON_HAS_INSLIST(_h)) { if (buffered_rows != NULL || CON_HAS_PS(_h)) { /* if we have to insert now, build the query * * if a prep stmt is provided, * build a prep stmt with query_buffer_size elements */ if (CON_HAS_PS(_h)) no_rows = query_buffer_size; ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values"); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; for (i=0;i<no_rows;i++) { sql_buf[off++]='('; ret = db_print_values(_h, sql_buf + off, SQL_BUF_LEN - off, CON_HAS_PS(_h)?_v:buffered_rows[i], _n, val2str); if (ret < 0) goto error; off += ret; sql_buf[off++]=')'; if (i != (no_rows -1)) sql_buf[off++]=','; /* if we have a PS, leave the function handling prep stmts in the module to free the rows once it's done */ if (!CON_HAS_PS(_h)) { shm_free(buffered_rows[i]); buffered_rows[i] = NULL; } } if (off + 1 > SQL_BUF_LEN) goto error0; sql_buf[off] = '\0'; sql_str.s = sql_buf; sql_str.len = off; goto submit; } else { /* wait for queries to pile up */ return 0; } } else { ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values ("); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error0; off += ret; ret = db_print_values(_h, sql_buf + off, SQL_BUF_LEN - off, _v, _n, val2str); if (ret < 0) goto error0; off += ret; if (off + 2 > SQL_BUF_LEN) goto error0; } sql_buf[off++] = ')'; sql_buf[off] = '\0'; sql_str.s = sql_buf; sql_str.len = off; submit: if (submit_query(_h, &sql_str) < 0) { LM_ERR("error while submitting query\n"); return -2; } return 0; error: cleanup_rows(buffered_rows); error0: LM_ERR("error while preparing insert operation\n"); return -1; }