/** Free the argument list created by make_arglist() * */ void free_arglist(void *db, wg_query_arg *arglist, int sz) { if(arglist) { int i; for(i=0; i<sz; i++) { if(arglist[i].value != WG_ILLEGAL) { wg_free_query_param(db, arglist[i].value); } } free(arglist); } }
char* search(char* database, char* inparams[], char* invalues[], int incount, int* hformat) { int i,rcount,gcount,itmp; wg_int type=0; char* fields[MAXPARAMS]; char* values[MAXPARAMS]; char* compares[MAXPARAMS]; char* types[MAXPARAMS]; int fcount=0, vcount=0, ccount=0, tcount=0; int from=0; int count=MAXCOUNT; void* db=NULL; // actual database pointer void* rec; char* res; int res_size=INITIAL_MALLOC; wg_query *wgquery; // query datastructure built later wg_query_arg wgargs[MAXPARAMS]; wg_int lock_id=0; // non-0 iff lock set int nosearch=0; // 1 iff no search parameters given, use scan int maxdepth=MAX_DEPTH_DEFAULT; // rec depth limit for printer int showid=0; // add record id as first extra elem: 0: no, 1: yes int format=1; // 0: csv, 1:json int escape=2; // string special chars escaping: 0: just ", 1: urlencode, 2: json, 3: csv char errbuf[200]; // used for building variable-content input param error strings only char* strbuffer; // main result string buffer start (malloced later) int strbufferlen; // main result string buffer length char* strbufferptr; // current output location ptr in the main result string buffer // -------check and parse cgi parameters, attach database ------------ // set params to defaults for(i=0;i<MAXPARAMS;i++) { fields[i]=NULL; values[i]=NULL; compares[i]=NULL; types[i]=NULL; } // find search parameters for(i=0;i<incount;i++) { if (strncmp(inparams[i],"field",MAXQUERYLEN)==0) { fields[fcount++]=invalues[i]; } else if (strncmp(inparams[i],"value",MAXQUERYLEN)==0) { values[vcount++]=invalues[i]; } else if (strncmp(inparams[i],"compare",MAXQUERYLEN)==0) { compares[ccount++]=invalues[i]; } else if (strncmp(inparams[i],"type",MAXQUERYLEN)==0) { types[tcount++]=invalues[i]; } else if (strncmp(inparams[i],"from",MAXQUERYLEN)==0) { from=atoi(invalues[i]); } else if (strncmp(inparams[i],"count",MAXQUERYLEN)==0) { count=atoi(invalues[i]); } else if (strncmp(inparams[i],"depth",MAXQUERYLEN)==0) { maxdepth=atoi(invalues[i]); } else if (strncmp(inparams[i],"showid",MAXQUERYLEN)==0) { if (strncmp(invalues[i],"yes",MAXQUERYLEN)==0) showid=1; else if (strncmp(invalues[i],"no",MAXQUERYLEN)==0) showid=0; else { snprintf(errbuf,100,UNKNOWN_PARAM_VALUE_ERR,invalues[i],inparams[i]); errhalt(errbuf); } } else if (strncmp(inparams[i],"format",MAXQUERYLEN)==0) { if (strncmp(invalues[i],"csv",MAXQUERYLEN)==0) format=0; else if (strncmp(invalues[i],"json",MAXQUERYLEN)==0) format=1; else { snprintf(errbuf,100,UNKNOWN_PARAM_VALUE_ERR,invalues[i],inparams[i]); errhalt(errbuf); } } else if (strncmp(inparams[i],"escape",MAXQUERYLEN)==0) { if (strncmp(invalues[i],"no",MAXQUERYLEN)==0) escape=0; else if (strncmp(invalues[i],"url",MAXQUERYLEN)==0) escape=1; else if (strncmp(invalues[i],"json",MAXQUERYLEN)==0) escape=2; else { snprintf(errbuf,100,UNKNOWN_PARAM_VALUE_ERR,invalues[i],inparams[i]); errhalt(errbuf); } } else if (strncmp(inparams[i],"db",MAXQUERYLEN)==0) { // correct parameter, no action here } else if (strncmp(inparams[i],"op",MAXQUERYLEN)==0) { // correct parameter, no action here } else { // incorrect/unrecognized parameter snprintf(errbuf,100,UNKNOWN_PARAM_ERR,inparams[i]); errhalt(errbuf); } } // all parameters and values were understood if (format==0) { // csv maxdepth=0; // record structure not printed for csv escape=3; // only " replaced with "" *hformat=0; // store to caller for content-type header global_format=0; // for error handler only } // check search parameters if (!fcount) { if (vcount || ccount || tcount) errhalt(NO_FIELD_ERR); else nosearch=1; } // attach to database db = wg_attach_existing_database(database); global_db=db; if (!db) errhalt(DB_ATTACH_ERR); res=malloc(res_size); if (!res) { err_clear_detach_halt(db,0,MALLOC_ERR); } // database attached OK // create output string buffer (may be reallocated later) strbuffer=str_new(INITIAL_MALLOC); strbufferlen=INITIAL_MALLOC; strbufferptr=strbuffer; if (nosearch) { // ------- special case without real search: just output records --- gcount=0; rcount=0; lock_id = wg_start_read(db); // get read lock global_lock_id=lock_id; // only for handling errors if (!lock_id) err_clear_detach_halt(db,0,LOCK_ERR); str_guarantee_space(&strbuffer,&strbufferlen,&strbufferptr,MIN_STRLEN); if (format!=0) { // json snprintf(strbufferptr,MIN_STRLEN,"[\n"); strbufferptr+=2; } if (maxdepth>MAX_DEPTH_HARD) maxdepth=MAX_DEPTH_HARD; rec=wg_get_first_record(db); do { if (rcount>=from) { gcount++; if (gcount>count) break; str_guarantee_space(&strbuffer,&strbufferlen,&strbufferptr,MIN_STRLEN); if (gcount>1 && format!=0) { // json and not first row snprintf(strbufferptr,MIN_STRLEN,",\n"); strbufferptr+=2; } sprint_record(db,rec,&strbuffer,&strbufferlen,&strbufferptr,format,showid,0,maxdepth,escape); if (format==0) { // csv str_guarantee_space(&strbuffer,&strbufferlen,&strbufferptr,MIN_STRLEN); snprintf(strbufferptr,MIN_STRLEN,"\r\n"); strbufferptr+=2; } } rec=wg_get_next_record(db,rec); rcount++; } while(rec!=NULL); if (!wg_end_read(db, lock_id)) { // release read lock err_clear_detach_halt(db,lock_id,LOCK_RELEASE_ERR); } global_lock_id=0; // only for handling errors wg_detach_database(db); global_db=NULL; // only for handling errors str_guarantee_space(&strbuffer,&strbufferlen,&strbufferptr,MIN_STRLEN); if (format!=0) { // json snprintf(strbufferptr,MIN_STRLEN,"\n]"); strbufferptr+=3; } return strbuffer; } // ------------ normal search case: --------- // create a query list datastructure for(i=0;i<fcount;i++) { // field num if (!isint(fields[i])) err_clear_detach_halt(db,0,NO_FIELD_ERR); itmp=atoi(fields[i]); // column to compare wgargs[i].column = itmp; // comparison op: default equal wgargs[i].cond = encode_incomp(db,compares[i]); // valuetype: default guess from value later type=encode_intype(db,types[i]); // encode value to compare with wgargs[i].value = encode_invalue(db,values[i],type); } // make the query structure and read-lock the database before! lock_id = wg_start_read(db); // get read lock global_lock_id=lock_id; // only for handling errors if (!lock_id) err_clear_detach_halt(db,lock_id,LOCK_ERR); wgquery = wg_make_query(db, NULL, 0, wgargs, i); if (!wgquery) err_clear_detach_halt(db,lock_id,QUERY_ERR); // actually perform the query rcount=0; gcount=0; str_guarantee_space(&strbuffer,&strbufferlen,&strbufferptr,MIN_STRLEN); if (format!=0) { // json snprintf(strbufferptr,MIN_STRLEN,"[\n"); strbufferptr+=2; } if (maxdepth>MAX_DEPTH_HARD) maxdepth=MAX_DEPTH_HARD; while((rec = wg_fetch(db, wgquery))) { if (rcount>=from) { gcount++; str_guarantee_space(&strbuffer,&strbufferlen,&strbufferptr,MIN_STRLEN); if (gcount>1 && format!=0) { // json and not first row snprintf(strbufferptr,MIN_STRLEN,",\n"); strbufferptr+=2; } sprint_record(db,rec,&strbuffer,&strbufferlen,&strbufferptr,format,showid,0,maxdepth,escape); if (format==0) { // csv str_guarantee_space(&strbuffer,&strbufferlen,&strbufferptr,MIN_STRLEN); snprintf(strbufferptr,MIN_STRLEN,"\r\n"); strbufferptr+=2; } } rcount++; if (gcount>=count) break; } // free query datastructure, release lock, detach for(i=0;i<fcount;i++) wg_free_query_param(db, wgargs[i].value); wg_free_query(db,wgquery); if (!wg_end_read(db, lock_id)) { // release read lock err_clear_detach_halt(db,lock_id,LOCK_RELEASE_ERR); } global_lock_id=0; // only for handling errors wg_detach_database(db); global_db=NULL; // only for handling errors str_guarantee_space(&strbuffer,&strbufferlen,&strbufferptr,MIN_STRLEN); if (format!=0) { // json snprintf(strbufferptr,MIN_STRLEN,"\n]"); strbufferptr+=3; } return strbuffer; }
static char* search(thread_data_p tdata, char* inparams[], char* invalues[], int incount, int opcode) { char* database=tdata->database; char *token=NULL; int i,j,x,itmp; wg_int type=0; char* fields[MAXPARAMS]; // search fields char* values[MAXPARAMS]; // search values char* compares[MAXPARAMS]; // search comparisons char* types[MAXPARAMS]; // search value types char* cids=NULL; wg_int ids[MAXIDS]; // select these ids only int fcount=0, vcount=0, ccount=0, tcount=0; // array el counters for above char* sfields[MAXPARAMS]; // set / selected fields char* svalues[MAXPARAMS]; // set field values char* stypes[MAXPARAMS]; // set field types int sfcount; // array el counters for above int from=0; unsigned long count,rcount,gcount,handlecount; void* db=NULL; // actual database pointer void *rec, *oldrec; char* res; wg_query *wgquery; // query datastructure built later wg_query_arg wgargs[MAXPARAMS]; wg_int lock_id=0; // non-0 iff lock set int searchtype=0; // 0: full scan, 1: record ids, 2: by fields char errbuf[ERRBUF_LEN]; // used for building variable-content input param error strings only // default max nr of rows shown/handled if (opcode==COUNT_CODE) count=LONG_MAX; else count=MAXCOUNT; // -------check and parse cgi parameters, attach database ------------ // set params to defaults for(i=0;i<MAXPARAMS;i++) { fields[i]=NULL; values[i]=NULL; compares[i]=NULL; types[i]=NULL; sfields[i]=NULL; svalues[i]=NULL; stypes[i]=NULL; } // set printing params to defaults tdata->format=1; // 1: json tdata->maxdepth=MAX_DEPTH_DEFAULT; // rec depth limit for printer tdata->showid=0; // add record id as first extra elem: 0: no, 1: yes tdata->strenc=2; // string special chars escaping: 0: just ", 1: urlencode, 2: json, 3: csv // find search parameters for(i=0;i<incount;i++) { if (strncmp(inparams[i],"recids",MAXQUERYLEN)==0) { cids=invalues[i]; x=0; // set ids to defaults for(j=0;j<MAXIDS;j++) ids[j]=0; // split csv int list to ids int array for(j=0;j<strlen(cids);j++) { if (atoi(cids+j) && atoi(cids+j)>0) ids[x++]=atoi(cids+j); if (x>=MAXIDS) break; for(;j<strlen(cids) && cids[j]!=','; j++) {}; } } else if (strncmp(inparams[i],"fld",MAXQUERYLEN)==0) { res=handle_fld_param(tdata,inparams[i],invalues[i], &sfields[sfcount],&svalues[sfcount],&stypes[sfcount],sfcount,errbuf); if (res!=NULL) return res; // return error string sfcount++; } else if (strncmp(inparams[i],"field",MAXQUERYLEN)==0) { fields[fcount++]=invalues[i]; } else if (strncmp(inparams[i],"value",MAXQUERYLEN)==0) { values[vcount++]=invalues[i]; } else if (strncmp(inparams[i],"compare",MAXQUERYLEN)==0) { compares[ccount++]=invalues[i]; } else if (strncmp(inparams[i],"type",MAXQUERYLEN)==0) { types[tcount++]=invalues[i]; } else if (strncmp(inparams[i],"from",MAXQUERYLEN)==0) { from=atoi(invalues[i]); } else if (strncmp(inparams[i],"count",MAXQUERYLEN)==0) { count=atoi(invalues[i]); } else { // handle generic parameters for all queries: at end of param check res=handle_generic_param(tdata,inparams[i],invalues[i],&token,errbuf); if (res!=NULL) return res; // return error string } } // authorization if (opcode==DELETE_CODE || opcode==UPDATE_CODE) { if (!authorize(WRITE_LEVEL,tdata,database,token)) return errhalt(NOT_AUTHORIZED_ERR,tdata); } else { if (!authorize(READ_LEVEL,tdata,database,token)) return errhalt(NOT_AUTHORIZED_ERR,tdata); } // all parameters and values were understood if (tdata->format==0) { // csv tdata->maxdepth=0; // record structure not printed for csv tdata->strenc=3; // only " replaced with "" } // check search parameters if (cids!=NULL) { // query by record ids if (fcount) return errhalt(RECIDS_COMBINED_ERR,tdata); searchtype=1; } else if (!fcount) { // no search fields given if (vcount || ccount || tcount) return errhalt(NO_FIELD_ERR,tdata); else searchtype=0; // scan everything } else { // search by fields searchtype=2; } // attach to database db=op_attach_database(tdata,database,READ_LEVEL); if (!db) return errhalt(DB_ATTACH_ERR,tdata); // database attached OK // create output string buffer (may be reallocated later) tdata->buf=str_new(INITIAL_MALLOC); if (tdata->buf==NULL) return errhalt(MALLOC_ERR,tdata); tdata->bufsize=INITIAL_MALLOC; tdata->bufptr=tdata->buf; // check printing depth if (tdata->maxdepth>MAX_DEPTH_HARD) tdata->maxdepth=MAX_DEPTH_HARD; // initial print if(!op_print_data_start(tdata,opcode==SEARCH_CODE)) return err_clear_detach_halt(MALLOC_ERR,tdata); // zero counters rcount=0; gcount=0; handlecount=0; // actual nr of records handled // get lock if (tdata->realthread && tdata->common->shutdown) return NULL; // for multithreading only lock_id = wg_start_read(db); // get read lock tdata->lock_id=lock_id; tdata->lock_type=READ_LOCK_TYPE; if (!lock_id) return err_clear_detach_halt(LOCK_ERR,tdata); // handle one of the cases if (searchtype==0) { // ------- full scan case --- rec=wg_get_first_record(db); while (rec!=NULL) { if (rcount>=from) { gcount++; if (gcount>count) break; if (opcode==COUNT_CODE) { handlecount++; } else if (opcode==SEARCH_CODE) { itmp=op_print_record(tdata,rec,gcount); if (!itmp) return err_clear_detach_halt(MALLOC_ERR,tdata); } else if (opcode==UPDATE_CODE) { itmp=op_update_record(tdata,db,rec,0,0); if (!itmp) handlecount++; } } oldrec=rec; rec=wg_get_next_record(db,rec); if (opcode==DELETE_CODE) { x=wg_get_record_len(db,oldrec); if (x>0) { itmp=op_delete_record(tdata,oldrec); if (!itmp) handlecount++; //else err_clear_detach_halt(DELETE_ERR,tdata); } } rcount++; } } else if (searchtype==1) { // ------------ search by record ids: ------------ for(j=0; ids[j]!=0 && j<MAXIDS; j++) { x=wg_get_encoded_type(db,ids[j]); if (x!=WG_RECORDTYPE) continue; rec=wg_decode_record(db,ids[j]); if (rec==NULL) continue; x=wg_get_record_len(db,rec); if (x<=0) continue; gcount++; if (gcount>count) break; if (opcode==COUNT_CODE) handlecount++; else if (opcode==SEARCH_CODE) { itmp=op_print_record(tdata,rec,gcount); if (!itmp) return err_clear_detach_halt(MALLOC_ERR,tdata); } else if (opcode==UPDATE_CODE) { itmp=op_update_record(tdata,db,rec,0,0); if (!itmp) handlecount++; } else if (opcode==DELETE_CODE) { // test that db is not null, otherwise we may corrupt the database oldrec=wg_get_first_record(db); if (oldrec!=NULL) { //wg_int objecthead=dbfetch((void*)db,(void*)rec); //printf("isfreeobject %d\n",isfreeobject((int)objecthead)); wg_print_record(db,rec); itmp=op_delete_record(tdata,rec); printf("deletion result %d\n",itmp); if (!itmp) handlecount++; //else return err_clear_detach_halt(DELETE_ERR,tdata); } } } } else if (searchtype==2) { // ------------by field search case: --------- // create a query list datastructure for(i=0;i<fcount;i++) { // field num if (!isint(fields[i])) return err_clear_detach_halt(NO_FIELD_ERR,tdata); itmp=atoi(fields[i]); if(itmp<0) return err_clear_detach_halt(NO_FIELD_ERR,tdata); // column to compare wgargs[i].column = itmp; // comparison op: default equal wgargs[i].cond = encode_incomp(db,compares[i]); if (wgargs[i].cond==BAD_WG_VALUE) return err_clear_detach_halt(COND_ERR,tdata); // valuetype: default guess from value later type=encode_intype(db,types[i]); if (type==BAD_WG_VALUE) return err_clear_detach_halt(INTYPE_ERR,tdata); // encode value to compare with wgargs[i].value = encode_invalue(db,values[i],type); if (wgargs[i].value==WG_ILLEGAL) return err_clear_detach_halt(INTYPE_ERR,tdata); } // make the query structure wgquery = wg_make_query(db, NULL, 0, wgargs, i); if (!wgquery) return err_clear_detach_halt(QUERY_ERR,tdata); // actually perform the query if (tdata->maxdepth>MAX_DEPTH_HARD) tdata->maxdepth=MAX_DEPTH_HARD; while((rec = wg_fetch(db, wgquery))) { if (rcount>=from) { gcount++; if (opcode==COUNT_CODE) handlecount++; else if (opcode==SEARCH_CODE) { itmp=op_print_record(tdata,rec,gcount); if (!itmp) return err_clear_detach_halt(MALLOC_ERR,tdata); } else if (opcode==UPDATE_CODE) { itmp=op_update_record(tdata,db,rec,0,0); if (!itmp) handlecount++; } else if (opcode==DELETE_CODE) { itmp=op_delete_record(tdata,rec); if (!itmp) handlecount++; //else return err_clear_detach_halt(DELETE_ERR,tdata); } } rcount++; if (gcount>=count) break; } // free query datastructure, for(i=0;i<fcount;i++) wg_free_query_param(db, wgargs[i].value); wg_free_query(db,wgquery); } // ----- cases handled ------ // print a single number for count and delete if (opcode==COUNT_CODE || opcode==DELETE_CODE) { if(!str_guarantee_space(tdata,MIN_STRLEN)) return err_clear_detach_halt(MALLOC_ERR,tdata); itmp=snprintf(tdata->bufptr,MIN_STRLEN,"%lu",handlecount); tdata->bufptr+=itmp; } // release locks and detach if (!wg_end_read(db, lock_id)) { // release read lock return err_clear_detach_halt(LOCK_RELEASE_ERR,tdata); } tdata->lock_id=0; op_detach_database(tdata,db); if(!op_print_data_end(tdata,opcode==SEARCH_CODE)) return err_clear_detach_halt(MALLOC_ERR,tdata); return tdata->buf; }