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; }
/* safely adds a new row to the insert list * also checks if the queue is full and returns all the rows that need to * be flushed to DB to the caller * * returns the number of rows detached * * Important : it is the caller's job to shm_free the rows * after flushing to DB * */ int ql_row_add(query_list_t *entry,const db_val_t *row,db_val_t ***ins_rows) { int val_size,i,len,no_rows = 0; char *pos; db_val_t *shm_row; val_size = entry->col_no * sizeof(db_val_t); for (i=0;i<entry->col_no;i++) { if (VAL_TYPE(row+i) == DB_STR && VAL_NULL(row+i) == 0) { val_size += VAL_STR(row+i).len; continue; } if (VAL_TYPE(row+i) == DB_STRING && VAL_NULL(row+i) == 0) { val_size += strlen(VAL_STRING(row+i))+1; continue; } if (VAL_TYPE(row+i) == DB_BLOB && VAL_NULL(row+i) == 0) val_size += VAL_BLOB(row+i).len; } shm_row = shm_malloc(val_size); if (shm_row == NULL) { LM_ERR("no more shm\n"); return -1; } LM_DBG("adding row to table [%.*s] & entry %p\n",entry->table.len,entry->table.s,entry); /* save row info to shm */ pos = (char *)(shm_row + entry->col_no); memcpy(shm_row,row,entry->col_no * sizeof(db_val_t)); for (i=0;i<entry->col_no;i++) { if (VAL_TYPE(row+i) == DB_STR && VAL_NULL(row+i) == 0) { len = VAL_STR(row+i).len; VAL_STR(shm_row+i).len = len; VAL_STR(shm_row+i).s = pos; memcpy(VAL_STR(shm_row+i).s,VAL_STR(row+i).s,len); pos += len; continue; } if (VAL_TYPE(row+i) == DB_STRING && VAL_NULL(row+i) == 0) { len = strlen(VAL_STRING(row+i)) + 1; VAL_STRING(shm_row+i) = pos; memcpy((void *)VAL_STRING(shm_row+i),VAL_STRING(row+i),len); pos += len; continue; } if (VAL_TYPE(row+i) == DB_BLOB && VAL_NULL(row+i) == 0) { len = VAL_BLOB(row+i).len; VAL_BLOB(shm_row+i).len = len; VAL_BLOB(shm_row+i).s = pos; memcpy(VAL_BLOB(shm_row+i).s,VAL_BLOB(row+i).s,len); pos += len; } } LM_DBG("before locking query entry\n"); lock_get(entry->lock); /* store oldest query for timer to know */ if (entry->no_rows == 0) entry->oldest_query = time(0); entry->rows[entry->no_rows++] = shm_row; LM_DBG("query for table [%.*s] has %d rows\n",entry->table.len,entry->table.s,entry->no_rows); /* is it time to flush to DB ? */ if (entry->no_rows == query_buffer_size) { if ((no_rows = ql_detach_rows_unsafe(entry,ins_rows)) < 0) { LM_ERR("failed to detach rows for insertion\n"); lock_release(entry->lock); return -1; } } lock_release(entry->lock); return no_rows; }