/** Delete rows * Like query(), except the selected rows are deleted. */ void del(void *db, char **argv, int argc) { int qargc; void *rec = NULL; wg_query *q; wg_query_arg *arglist; gint lock_id; arglist = make_arglist(db, argv, argc, &qargc); if(!arglist) return; /* Use maximum isolation */ if(!(lock_id = wg_start_write(db))) { fprintf(stderr, "failed to get lock on database\n"); goto abrt1; } q = wg_make_query(db, NULL, 0, arglist, qargc); if(!q) goto abrt2; if(q->res_count > 0) { printf("Deleting %d rows...", (int) q->res_count); rec = wg_fetch(db, q); while(rec) { wg_delete_record(db, (gint *) rec); rec = wg_fetch(db, q); } printf(" done\n"); } wg_free_query(db, q); abrt2: wg_end_write(db, lock_id); abrt1: free_arglist(db, arglist, qargc); }
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"); }
void run_workers(void *db, int rcnt, int wcnt) { pt_data *pt_table; int i, tcnt; #ifdef HAVE_PTHREAD int err; void *status; pthread_attr_t attr; #endif #if !defined(HAVE_PTHREAD) && !defined(_WIN32) fprintf(stderr, "No thread support: skipping tests.\n"); return; #endif #ifdef HAVE_PTHREAD pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); #endif #if defined(BENCHMARK) && defined(HAVE_PTHREAD) pthread_rwlock_init(&rwlock, NULL); #endif #ifdef SYNC_THREADS #if defined(HAVE_PTHREAD) pthread_mutex_init(&twait_mutex, NULL); pthread_cond_init(&twait_cv, NULL); #elif defined(_WIN32) /* Manual reset event, initial state nonsignaled. */ twait_ev = CreateEvent(NULL, TRUE, FALSE, NULL); #endif #endif tcnt = rcnt + wcnt; pt_table = (pt_data *) malloc(tcnt * sizeof(pt_data)); if(!pt_table) { fprintf(stderr, "Failed to allocate thread table: skipping tests.\n"); return; } /* Spawn the threads */ #ifdef SYNC_THREADS twait_cnt = 0; #endif for(i=0; i<tcnt; i++) { pt_table[i].db = db; pt_table[i].threadid = i; /* First wcnt threads are writers, the remainder readers */ if(i<wcnt) { #if defined(HAVE_PTHREAD) err = pthread_create(&pt_table[i].pth, &attr, writer_thread, \ (void *) &pt_table[i]); #elif defined(_WIN32) pt_table[i].hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) writer_thread, (LPVOID) &pt_table[i], 0, NULL); #endif } else { #if defined(HAVE_PTHREAD) err = pthread_create(&pt_table[i].pth, &attr, reader_thread, \ (void *) &pt_table[i]); #elif defined(_WIN32) pt_table[i].hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) reader_thread, (LPVOID) &pt_table[i], 0, NULL); #endif } #if defined(HAVE_PTHREAD) if(err) { fprintf(stderr, "Error code from pthread_create: %d.\n", err); #elif defined(_WIN32) if(!pt_table[i].hThread) { /* XXX: GetLastError() gives the error code if needed */ fprintf(stderr, "CreateThread failed.\n"); #endif goto workers_done; } } #ifdef SYNC_THREADS /* Check that all workers have entered wait state */ for(;;) { /* While reading a word from memory is atomic, we * still use the mutex because we want to guarantee * that the last thread has called pthread_cond_wait(). * With Win32 API, condition variables with similar * functionality are available starting from Windows Vista, * so this implementation uses a simple synchronization * event instead. This causes small, probably non-relevant * loss in sync accuracy. */ #ifdef HAVE_PTHREAD pthread_mutex_lock(&twait_mutex); #endif if(twait_cnt >= tcnt) break; #ifdef HAVE_PTHREAD pthread_mutex_unlock(&twait_mutex); #endif } /* Now wake up all threads */ #if defined(HAVE_PTHREAD) pthread_cond_broadcast(&twait_cv); pthread_mutex_unlock(&twait_mutex); #elif defined(_WIN32) SetEvent(twait_ev); #endif #endif /* SYNC_THREADS */ /* Join the workers (wait for them to complete) */ for(i=0; i<tcnt; i++) { #if defined(HAVE_PTHREAD) err = pthread_join(pt_table[i].pth, &status); if(err) { fprintf(stderr, "Error code from pthread_join: %d.\n", err); break; } #elif defined(_WIN32) WaitForSingleObject(pt_table[i].hThread, INFINITE); CloseHandle(pt_table[i].hThread); #endif } workers_done: #ifdef HAVE_PTHREAD pthread_attr_destroy(&attr); #endif #if defined(BENCHMARK) && defined(HAVE_PTHREAD) pthread_rwlock_destroy(&rwlock); #endif #ifdef SYNC_THREADS #if defined(HAVE_PTHREAD) pthread_mutex_destroy(&twait_mutex); pthread_cond_destroy(&twait_cv); #elif defined(_WIN32) CloseHandle(twait_ev); #endif #endif free(pt_table); } /** Writer thread * Runs preconfigured number of basic write transactions */ worker_t writer_thread(void * threadarg) { void * db; int threadid, i, j, cksum; void *rec = NULL, *frec = NULL; db = ((pt_data *) threadarg)->db; threadid = ((pt_data *) threadarg)->threadid; #ifdef CHATTY_THREADS fprintf(stdout, "Writer thread %d started.\n", threadid); #endif #ifdef SYNC_THREADS /* Increment the thread counter to inform the caller * that we are entering 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 */ frec = wg_get_first_record(db); for(i=0; i<WORKLOAD; i++) { wg_int c=-1, lock_id; #if defined(BENCHMARK) && defined(HAVE_PTHREAD) pthread_rwlock_wrlock(&rwlock); #else /* Start transaction */ if(!(lock_id = wg_start_write(db))) { fprintf(stderr, "Writer thread %d: wg_start_write failed.\n", threadid); goto writer_done; } #endif /* Fetch checksum */ cksum = wg_decode_int(db, wg_get_field(db, frec, 0)); /* Fetch record from database */ if(i) rec = wg_get_next_record(db, rec); else rec = frec; if(!rec) { fprintf(stderr, "Writer thread %d: wg_get_next_record failed.\n", threadid); #if defined(BENCHMARK) && defined(HAVE_PTHREAD) pthread_rwlock_unlock(&rwlock); #else wg_end_write(db, lock_id); #endif goto writer_done; } /* Modify record */ if(i) j = 0; else j = 1; for(; j<REC_SIZE; j++) { if (wg_set_int_field(db, rec, j, c--) != 0) { fprintf(stderr, "Writer thread %d: int storage error.\n", threadid); #if defined(BENCHMARK) && defined(HAVE_PTHREAD) pthread_rwlock_unlock(&rwlock); #else wg_end_write(db, lock_id); #endif goto writer_done; } } /* Update checksum */ wg_set_int_field(db, frec, 0, ++cksum); #if defined(BENCHMARK) && defined(HAVE_PTHREAD) pthread_rwlock_unlock(&rwlock); #else /* End transaction */ if(!wg_end_write(db, lock_id)) { fprintf(stderr, "Writer thread %d: wg_end_write failed.\n", threadid); goto writer_done; } #endif } #ifdef CHATTY_THREADS fprintf(stdout, "Writer thread %d ended.\n", threadid); #endif writer_done: #if defined(HAVE_PTHREAD) pthread_exit(NULL); #elif defined(_WIN32) return 0; #endif }
static char* insert(thread_data_p tdata, char* inparams[], char* invalues[], int incount) { char* database=tdata->database; char *token=NULL; int i,tmp; //int j,x; char* json=NULL; //int count=MAXCOUNT; void* db=NULL; // actual database pointer //void* rec; char* res; wg_int lock_id=0; // non-0 iff lock set char errbuf[ERRBUF_LEN]; // used for building variable-content input param error strings only // yajl /* yajl_handle hand; yajl_gen g; yajl_status stat; size_t rd; int retval = 0; int a = 1; */ // -------check and parse cgi parameters, attach database ------------ // 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 ids and display format parameters for(i=0;i<incount;i++) { if (strncmp(inparams[i],"rec",MAXQUERYLEN)==0) { json=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 } } if (json==NULL || strlen(json)==0) { return errhalt(MISSING_JSON_ERR,tdata); } // authorization if (!authorize(WRITE_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 "" } // attach to database db=op_attach_database(tdata,database,READ_LEVEL); if (!db) { if (!authorize(ADMIN_LEVEL,tdata,database,token)) { return errhalt(NOT_AUTHORIZED_INSERT_CREATE_ERR,tdata); } else { if (tdata->realthread && tdata->common->shutdown) return NULL; // for multithreading only db=op_create_database(tdata,database,0); if (!db) return err_clear_detach_halt(DB_CREATE_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; op_print_data_start(tdata,1); // initialize yajl /* g = yajl_gen_alloc(NULL); yajl_gen_config(g, yajl_gen_beautify, 0); yajl_gen_config(g, yajl_gen_validate_utf8, 1); hand = yajl_alloc(&callbacks, NULL, (void *) g); yajl_config(hand, yajl_allow_comments, 1); */ // take a write lock if (tdata->realthread && tdata->common->shutdown) return NULL; // for multithreading only lock_id = wg_start_write(db); // get write lock tdata->lock_id=lock_id; tdata->lock_type=WRITE_LOCK_TYPE; if (!lock_id) return err_clear_detach_halt(LOCK_ERR,tdata); // start parsing /* rd=strlen(json); stat = yajl_parse(hand, json, rd); if (stat==yajl_status_ok) stat=yajl_complete_parse(hand); if (stat != yajl_status_ok) { unsigned char * str = yajl_get_error(hand, 1, json, rd); fprintf(stderr, "%s", (const char *) str); yajl_free_error(hand, str); retval = 1; } else { // parse succeeded const unsigned char * buf; size_t len; yajl_gen_get_buf(g, &buf, &len); fwrite(buf, 1, len, stdout); yajl_gen_clear(g); } yajl_gen_free(g); yajl_free(hand); */ // parsing ended // parse json and insert tmp=wg_parse_json_document(db,json); if(tmp==-1) { return err_clear_detach_halt(JSON_ERR,tdata); } else if(tmp==-2) { return err_clear_detach_halt(INCONSISTENT_ERR,tdata); } if(!str_guarantee_space(tdata,MIN_STRLEN)) return err_clear_detach_halt(MALLOC_ERR,tdata); strcpy(tdata->bufptr,"1"); tdata->bufptr+=strlen("1"); // end activity if (!wg_end_write(db, lock_id)) { // release write 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,1)) return err_clear_detach_halt(MALLOC_ERR,tdata); return tdata->buf; }
static char* drop(thread_data_p tdata, char* inparams[], char* invalues[], int incount) { char* database=tdata->database; char *token=NULL; int i,tmp; void* db=NULL; // actual database pointer char *res; int lock_id=0; int found=0; char errbuf[ERRBUF_LEN]; // find and check parameters for(i=0;i<incount;i++) { if (strncmp(inparams[i],"db",MAXQUERYLEN)==0) { database=invalues[i]; if(database==NULL || strlen(database)<1 || atoi(database)<=0) { return errhalt(DB_NAME_ERR,tdata); } } 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 (!authorize(ADMIN_LEVEL,tdata,database,token)) { return errhalt(NOT_AUTHORIZED_ERR,tdata); } // all parameters and values were understood // 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; op_print_data_start(tdata,0); // check if access allowed in the conf file if (database!=NULL && (tdata->global)->conf->dbases.used>0) { for(i=0;i<(tdata->global)->conf->dbases.used;i++) { if (!strcmp(database,(tdata->global)->conf->dbases.vals[i])) { found=1; break; } } if (!found) return errhalt(DB_AUTHORIZE_ERR,tdata); } // first try to attach to an existing database db=op_attach_database(tdata,database,ADMIN_LEVEL); if (db==NULL) { return errhalt(DB_NOT_EXISTS_ERR,tdata); } else { // database exists, take lock if (tdata->realthread && tdata->common->shutdown) return NULL; // for multithreading only tdata->db=db; lock_id = wg_start_write(db); // get write lock tdata->lock_id=lock_id; tdata->lock_type=WRITE_LOCK_TYPE; if (!lock_id) return err_clear_detach_halt(LOCK_ERR,tdata); tmp=wg_detach_database(db); // detaches a database: returns 0 if OK if (tmp) return err_clear_detach_halt(DB_DROP_ERR,tdata); tmp=wg_delete_database(database); if (tmp) return errhalt(DB_DROP_ERR,tdata); } // deleted successfully tdata->db=NULL; tdata->lock_id=0; if(!str_guarantee_space(tdata,MIN_STRLEN)) return err_clear_detach_halt(MALLOC_ERR,tdata); strcpy(tdata->bufptr,"1"); tdata->bufptr+=strlen("1"); // end activity if(!op_print_data_end(tdata,0)) return errhalt(MALLOC_ERR,tdata); return tdata->buf; }