Beispiel #1
0
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;
}
Beispiel #2
0
/** 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;
}
Beispiel #3
0
/** 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;
    }
  }
Beispiel #4
0
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");
}
Beispiel #5
0
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;
}
Beispiel #6
0
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
}
Beispiel #7
0
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;
}