int sprint_record(void *db, wg_int *rec, char **buf, int *bufsize, char **bptr, int format, int showid, int depth, int maxdepth, int strenc) { int i,limit; wg_int enc, len; #ifdef USE_CHILD_DB void *parent; #endif limit=MIN_STRLEN; str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); if (rec==NULL) { snprintf(*bptr, limit, JS_NULL); (*bptr)+=strlen(JS_NULL); return 1; } if (format!=0) { // json **bptr= '['; (*bptr)++; } #ifdef USE_CHILD_DB parent = wg_get_rec_owner(db, rec); #endif if (1) { len = wg_get_record_len(db, rec); if (showid) { // add record id (offset) as the first extra elem of record snprintf(*bptr, limit-1, "%d",wg_encode_record(db,rec)); *bptr=*bptr+strlen(*bptr); } for(i=0; i<len; i++) { enc = wg_get_field(db, rec, i); #ifdef USE_CHILD_DB if(parent != db) enc = wg_translate_hdroffset(db, parent, enc); #endif str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); if (i || showid) { if (format!=0) **bptr = ','; else **bptr = CSV_SEPARATOR; (*bptr)++; } *bptr=sprint_value(db, enc, buf, bufsize, bptr, format, showid, depth, maxdepth, strenc); } } if (format!=0) { // json str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); **bptr = ']'; (*bptr)++; } return 1; }
/** Parse a JSON argument from command line. * *sz contains the arglist size * *doc contains the parsed JSON document, to be deleted later */ wg_json_query_arg *make_json_arglist(void *db, char *json, int *sz, void **doc) { wg_json_query_arg *arglist; void *document; gint i, reclen; if(wg_check_json(db, json) || wg_parse_json_param(db, json, &document)) return NULL; if(!is_schema_object(document)) { fprintf(stderr, "Invalid input JSON (must be an object)\n"); wg_delete_document(db, document); return NULL; } reclen = wg_get_record_len(db, document); arglist = malloc(sizeof(wg_json_query_arg) * reclen); if(!arglist) { fprintf(stderr, "Failed to allocate memory\n"); wg_delete_document(db, document); return NULL; } for(i=0; i<reclen; i++) { void *rec = wg_decode_record(db, wg_get_field(db, document, i)); gint key = wg_get_field(db, rec, WG_SCHEMA_KEY_OFFSET); gint value = wg_get_field(db, rec, WG_SCHEMA_VALUE_OFFSET); if(key == WG_ILLEGAL || value == WG_ILLEGAL) { free(arglist); wg_delete_document(db, document); return NULL; } arglist[i].key = key; arglist[i].value = value; } *sz = reclen; *doc = document; return arglist; }
/** Compare two encoded values * a, b - encoded values * returns WG_GREATER, WG_EQUAL or WG_LESSTHAN * assumes that a and b themselves are not equal and so * their decoded values need to be examined (which could still * be equal for some data types). * depth - recursion depth for records */ gint wg_compare(void *db, gint a, gint b, int depth) { /* a very simplistic version of the function: * - we get the types of the variables * - if the types match, compare the decoded values * - otherwise compare the type codes (not really scientific, * but will provide a means of ordering values). * * One important point that should be observed here is * that the returned values should be consistent when * comparing A to B and then B to A. This applies to cases * where we have no reason to think one is greater than * the other from the *user's* point of view, but for use * in T-tree index and similar, values need to be consistently * ordered. Examples include unknown types and record pointers * (once recursion depth runs out). */ /* XXX: might be able to save time here to mask and compare * the type bits instead */ gint typea = wg_get_encoded_type(db, a); gint typeb = wg_get_encoded_type(db, b); /* assume types are >2 (NULLs are always equal) and * <13 (not implemented as of now) * XXX: all of this will fall apart if type codes * are somehow rearranged :-) */ if(typeb==typea) { if(typea>WG_CHARTYPE) { /* > 9, not a string */ if(typea>WG_FIXPOINTTYPE) { /* date or time. Compare decoded gints */ gint deca, decb; if(typea==WG_DATETYPE) { deca = wg_decode_date(db, a); decb = wg_decode_date(db, b); } else if(typea==WG_TIMETYPE) { deca = wg_decode_time(db, a); decb = wg_decode_time(db, b); } else if(typea==WG_VARTYPE) { deca = wg_decode_var(db, a); decb = wg_decode_var(db, b); } else { /* anon const or other new type, no idea how to compare */ return (a>b ? WG_GREATER : WG_LESSTHAN); } return (deca>decb ? WG_GREATER : WG_LESSTHAN); } else { /* fixpoint, need to compare doubles */ double deca, decb; deca = wg_decode_fixpoint(db, a); decb = wg_decode_fixpoint(db, b); return (deca>decb ? WG_GREATER : WG_LESSTHAN); } } else if(typea<WG_STRTYPE) { /* < 5, still not a string */ if(typea==WG_RECORDTYPE) { void *deca, *decb; deca = wg_decode_record(db, a); decb = wg_decode_record(db, b); if(!depth) { /* No more recursion allowed and pointers aren't equal. * So while we're technically comparing the addresses here, * the main point is that the returned value != WG_EQUAL */ return ((gint) deca> (gint) decb ? WG_GREATER : WG_LESSTHAN); } else { int i; #ifdef USE_CHILD_DB void *parenta, *parentb; #endif int lena = wg_get_record_len(db, deca); int lenb = wg_get_record_len(db, decb); #ifdef USE_CHILD_DB /* Determine, if the records are inside the memory area beloning * to our current base address. If it is outside, the encoded * values inside the record contain offsets in relation to * a different base address and need to be translated. */ parenta = wg_get_rec_owner(db, deca); parentb = wg_get_rec_owner(db, decb); #endif /* XXX: Currently we're considering records of differing lengths * non-equal without comparing the elements */ if(lena!=lenb) return (lena>lenb ? WG_GREATER : WG_LESSTHAN); /* Recursively check each element in the record. If they * are not equal, break and return with the obtained value */ for(i=0; i<lena; i++) { gint elema = wg_get_field(db, deca, i); gint elemb = wg_get_field(db, decb, i); #ifdef USE_CHILD_DB if(parenta != dbmemseg(db)) { elema = wg_translate_hdroffset(db, parenta, elema); } if(parentb != dbmemseg(db)) { elemb = wg_translate_hdroffset(db, parentb, elemb); } #endif if(elema != elemb) { gint cr = wg_compare(db, elema, elemb, depth - 1); if(cr != WG_EQUAL) return cr; } } return WG_EQUAL; /* all elements matched */ } } else if(typea==WG_INTTYPE) { gint deca, decb; deca = wg_decode_int(db, a); decb = wg_decode_int(db, b); if(deca==decb) return WG_EQUAL; /* large ints can be equal */ return (deca>decb ? WG_GREATER : WG_LESSTHAN); } else { /* WG_DOUBLETYPE */ double deca, decb; deca = wg_decode_double(db, a); decb = wg_decode_double(db, b); if(deca==decb) return WG_EQUAL; /* decoded doubles can be equal */ return (deca>decb ? WG_GREATER : WG_LESSTHAN); } } else { /* string */ /* Need to compare the characters. In case of 0-terminated * strings we use strcmp() directly, which in glibc is heavily * optimised. In case of blob type we need to query the length * and use memcmp(). */ char *deca, *decb, *exa=NULL, *exb=NULL; char buf[4]; gint res; if(typea==WG_STRTYPE) { /* lang is ignored */ deca = wg_decode_str(db, a); decb = wg_decode_str(db, b); } else if(typea==WG_URITYPE) { exa = wg_decode_uri_prefix(db, a); exb = wg_decode_uri_prefix(db, b); deca = wg_decode_uri(db, a); decb = wg_decode_uri(db, b); } else if(typea==WG_XMLLITERALTYPE) { exa = wg_decode_xmlliteral_xsdtype(db, a); exb = wg_decode_xmlliteral_xsdtype(db, b); deca = wg_decode_xmlliteral(db, a); decb = wg_decode_xmlliteral(db, b); } else if(typea==WG_CHARTYPE) { buf[0] = wg_decode_char(db, a); buf[1] = '\0'; buf[2] = wg_decode_char(db, b); buf[3] = '\0'; deca = buf; decb = &buf[2]; } else { /* WG_BLOBTYPE */ deca = wg_decode_blob(db, a); decb = wg_decode_blob(db, b); } if(exa || exb) { /* String type where extra information is significant * (we're ignoring this for plain strings and blobs). * If extra part is equal, normal comparison continues. If * one string is missing altogether, it is considered to be * smaller than the other string. */ if(!exb) { if(exa[0]) return WG_GREATER; } else if(!exa) { if(exb[0]) return WG_LESSTHAN; } else { res = strcmp(exa, exb); if(res > 0) return WG_GREATER; else if(res < 0) return WG_LESSTHAN; } } #if 0 /* paranoia check */ if(!deca || !decb) { if(decb) if(decb[0]) return WG_LESSTHAN; } else if(deca) { if(deca[0]) return WG_GREATER; } return WG_EQUAL; } #endif if(typea==WG_BLOBTYPE) { /* Blobs are not 0-terminated */ int lena = wg_decode_blob_len(db, a); int lenb = wg_decode_blob_len(db, b); res = memcmp(deca, decb, (lena < lenb ? lena : lenb)); if(!res) res = lena - lenb; } else { res = strcmp(deca, decb); } if(res > 0) return WG_GREATER; else if(res < 0) return WG_LESSTHAN; else return WG_EQUAL; } }
void run_demo(void* db) { void *rec = NULL, *firstrec = NULL, *nextrec = NULL; /* Pointers to a database record */ wg_int enc; /* Encoded data */ wg_int lock_id; /* Id of an acquired lock (for releasing it later) */ wg_int len; int i; int intdata, datedata, timedata; char strbuf[80]; printf("********* Starting demo ************\n"); /* Begin by creating a simple record of 3 fields and fill it * with integer data. */ printf("Creating first record.\n"); rec=wg_create_record(db, 3); if (rec==NULL) { printf("rec creation error.\n"); return; } /* Encode a field, checking for errors */ enc = wg_encode_int(db, 44); if(enc==WG_ILLEGAL) { printf("failed to encode an integer.\n"); return; } /* Negative return value shows that an error occurred */ if(wg_set_field(db, rec, 0, enc) < 0) { printf("failed to store a field.\n"); return; } /* Skip error checking for the sake of brevity for the rest of fields */ enc = wg_encode_int(db, -199999); wg_set_field(db, rec, 1, enc); wg_set_field(db, rec, 2, wg_encode_int(db, 0)); /* Now examine the record we have created. Get record length, * encoded value of each field, data type and decoded value. */ /* Negative return value shows an error. */ len = wg_get_record_len(db, rec); if(len < 0) { printf("failed to get record length.\n"); return; } printf("Size of created record at %p was: %d\n", rec, (int) len); for(i=0; i<len; i++) { printf("Reading field %d:", i); enc = wg_get_field(db, rec, i); if(wg_get_encoded_type(db, enc) != WG_INTTYPE) { printf("data was of unexpected type.\n"); return; } intdata = wg_decode_int(db, enc); /* No error checking here. All integers are valid. */ printf(" %d\n", intdata); } /* Fields can be erased by setting their value to 0 which always stands for NULL value. */ printf("Clearing field 1.\n"); wg_set_field(db, rec, 1, 0); if(wg_get_field(db, rec, 1)==0) { printf("Re-reading field 1 returned a 0 (NULL) field.\n"); } else { printf("unexpected value \n"); return; } /* Fields can be updated with data of any type (the type is not fixed). */ printf("Updating field 0 to a floating-point number.\n"); enc = wg_encode_double(db, 56.9988); wg_set_field(db, rec, 0, enc); enc = wg_get_field(db, rec, 0); if(wg_get_encoded_type(db, enc) == WG_DOUBLETYPE) { printf("Re-reading field 0 returned %f.\n", wg_decode_double(db, enc)); } else { printf("data was of unexpected type.\n"); return; } /* Create a next record. Let's assume we're in an environment where * the database is used concurrently, so there's a need to use locking. */ printf("Creating second record.\n"); /* Lock id of 0 means that the operation failed */ lock_id = wg_start_write(db); if(!lock_id) { printf("failed to acquire lock.\n"); return; } /* Do the write operation we acquired the lock for. */ rec=wg_create_record(db, 6); /* Failing to release the lock would be fatal to database operation. */ if(!wg_end_write(db, lock_id)) { printf("failed to release lock.\n"); return; } if (!rec) { printf("rec creation error.\n"); return; } /* Reading also requires locking./ */ lock_id = wg_start_read(db); if(!lock_id) { printf("failed to acquire lock.\n"); return; } /* Do our read operation... */ len = wg_get_record_len(db, rec); /* ... and unlock immediately */ if(!wg_end_read(db, lock_id)) { printf("failed to release lock.\n"); return; } if(len < 0) { printf("failed to get record length.\n"); return; } printf("Size of created record at %p was: %d\n", rec, (int) len); /* Let's find the first record in the database */ lock_id = wg_start_read(db); firstrec = wg_get_first_record(db); wg_end_read(db, lock_id); if(!firstrec) { printf("Failed to find first record.\n"); return; } printf("First record of database had address %p.\n", firstrec); /* Let's check what the next record is to demonstrate scanning records. */ nextrec = firstrec; lock_id = wg_start_read(db); do { nextrec = wg_get_next_record(db, nextrec); if(nextrec) printf("Next record had address %p.\n", nextrec); } while(nextrec); printf("Finished scanning database records.\n"); wg_end_read(db, lock_id); /* Set fields to various values. Field 0 is not touched at all (un- * initialized). Field 1 is set to point to another record. */ printf("Populating second record with data.\n"); /* Let's use the first record we found to demonstrate storing * a link to a record in a field inside another record. */ lock_id = wg_start_write(db); enc = wg_encode_record(db, firstrec); wg_set_field(db, rec, 1, enc); wg_end_write(db, lock_id); /* Now set other fields to various data types. To keep the example shorter, * the locking and unlocking operations are omitted (in real applications, * this would be incorrect usage if concurrent access is expected). */ wg_set_field(db, rec, 2, wg_encode_str(db, "This is a char array", NULL)); wg_set_field(db, rec, 3, wg_encode_char(db, 'a')); /* For time and date, we use current time in local timezone */ enc = wg_encode_date(db, wg_current_localdate(db)); if(enc==WG_ILLEGAL) { printf("failed to encode date.\n"); return; } wg_set_field(db, rec, 4, enc); enc = wg_encode_time(db, wg_current_localtime(db)); if(enc==WG_ILLEGAL) { printf("failed to encode time.\n"); return; } wg_set_field(db, rec, 5, enc); /* Now read and print all the fields. */ wg_print_record(db, (wg_int *) rec); printf("\n"); /* Date and time can be handled together as a datetime object. */ datedata = wg_decode_date(db, wg_get_field(db, rec, 4)); timedata = wg_decode_time(db, wg_get_field(db, rec, 5)); wg_strf_iso_datetime(db, datedata, timedata, strbuf); printf("Reading datetime: %s.\n", strbuf); printf("Setting date and time to 2010-03-31, 12:59\n"); /* Update date and time to arbitrary values using wg_strp_iso_date/time */ wg_set_field(db, rec, 4, wg_encode_date(db, wg_strp_iso_date(db, "2010-03-31"))); wg_set_field(db, rec, 5, wg_encode_time(db, wg_strp_iso_time(db, "12:59:00.33"))); printf("Dumping the contents of the database:\n"); wg_print_db(db); printf("********* Demo ended ************\n"); }
char* recids(char* database, char* inparams[], char* invalues[], int incount, int* hformat) { int i,j,x,gcount; char* cids; wg_int ids[MAXIDS]; int count=MAXCOUNT; void* db=NULL; // actual database pointer void* rec; char* res; int res_size=INITIAL_MALLOC; wg_int lock_id=0; // non-0 iff lock set 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 ids to defaults for(i=0;i<MAXIDS;i++) { ids[i]=0; } // find ids and display format parameters for(i=0;i<incount;i++) { if (strncmp(inparams[i],"recids",MAXQUERYLEN)==0) { cids=invalues[i]; x=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],"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 } // 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; // take a read lock and loop over ids gcount=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; 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; 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); } 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; }
worker_t reader_thread(void * threadarg) { void * db; int threadid, i, j; void *rec = NULL; db = ((pt_data *) threadarg)->db; threadid = ((pt_data *) threadarg)->threadid; #ifdef CHATTY_THREADS fprintf(stdout, "Reader thread %d started.\n", threadid); #endif #ifdef SYNC_THREADS /* Enter wait state */ #ifdef HAVE_PTHREAD pthread_mutex_lock(&twait_mutex); #endif twait_cnt++; #if defined(HAVE_PTHREAD) pthread_cond_wait(&twait_cv, &twait_mutex); pthread_mutex_unlock(&twait_mutex); #elif defined(_WIN32) WaitForSingleObject(twait_ev, INFINITE); #endif #endif /* SYNC_THREADS */ for(i=0; i<WORKLOAD; i++) { wg_int reclen, lock_id; #if defined(BENCHMARK) && defined(HAVE_PTHREAD) pthread_rwlock_rdlock(&rwlock); #else /* Start transaction */ if(!(lock_id = wg_start_read(db))) { fprintf(stderr, "Reader thread %d: wg_start_read failed.\n", threadid); goto reader_done; } #endif /* Fetch record from database */ if(i) rec = wg_get_next_record(db, rec); else rec = wg_get_first_record(db); if(!rec) { fprintf(stderr, "Reader thread %d: wg_get_next_record failed.\n", threadid); #if defined(BENCHMARK) && defined(HAVE_PTHREAD) pthread_rwlock_unlock(&rwlock); #else wg_end_read(db, lock_id); #endif goto reader_done; } /* Parse the record. We can safely ignore errors here, * except for the record length, which is supposed to be REC_SIZE */ reclen = wg_get_record_len(db, rec); if (reclen < 0) { fprintf(stderr, "Reader thread %d: invalid record length.\n", threadid); #if defined(BENCHMARK) && defined(HAVE_PTHREAD) pthread_rwlock_unlock(&rwlock); #else wg_end_read(db, lock_id); #endif goto reader_done; } for(j=0; j<reclen; j++) { wg_get_field(db, rec, j); wg_get_field_type(db, rec, j); } #if defined(BENCHMARK) && defined(HAVE_PTHREAD) pthread_rwlock_unlock(&rwlock); #else /* End transaction */ if(!wg_end_read(db, lock_id)) { fprintf(stderr, "Reader thread %d: wg_end_read failed.\n", threadid); goto reader_done; } #endif } #ifdef CHATTY_THREADS fprintf(stdout, "Reader thread %d ended.\n", threadid); #endif reader_done: #if defined(HAVE_PTHREAD) pthread_exit(NULL); #elif defined(_WIN32) return 0; #endif }
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; }