int next_obj(void *_cursor) { size_t sz; void *ptr = NULL; sky_cursor *cursor = (sky_cursor*)_cursor; if(cursor->context == NULL) { ptr = DATA3; sz = DATA3_LENGTH; } else if(cursor->context == DATA3) { ptr = DATA4; sz = DATA4_LENGTH; } if(ptr != NULL) { cursor->context = ptr; sky_cursor_set_ptr(cursor, ptr, sz); return 1; } else { return 0; } }
int test_sky_cursor_set_data() { // Setup data object & cursor. sky_cursor *cursor = sky_cursor_new(-4, 4); sky_cursor_set_timestamp_offset(cursor, offsetof(test_t, timestamp)); sky_cursor_set_ts_offset(cursor, offsetof(test_t, ts)); sky_cursor_set_property(cursor, -5, offsetof(test_t, action_boolean), sizeof(bool), "boolean"); sky_cursor_set_property(cursor, -4, offsetof(test_t, action_double), sizeof(double), "float"); sky_cursor_set_property(cursor, -3, offsetof(test_t, action_int), sizeof(int32_t), "integer"); sky_cursor_set_property(cursor, -2, offsetof(test_t, action_string), sizeof(sky_string), "string"); sky_cursor_set_property(cursor, -1, offsetof(test_t, action), sizeof(sky_string), "string"); sky_cursor_set_property(cursor, 1, offsetof(test_t, object_string), sizeof(sky_string), "string"); sky_cursor_set_property(cursor, 2, offsetof(test_t, object_int), sizeof(int32_t), "integer"); sky_cursor_set_property(cursor, 3, offsetof(test_t, object_double), sizeof(double), "float"); sky_cursor_set_property(cursor, 4, offsetof(test_t, object_boolean), sizeof(bool), "boolean"); sky_cursor_set_data_sz(cursor, sizeof(test_t)); sky_cursor_set_ptr(cursor, DATA0, DATA0_LENGTH); ASSERT_OBJ_STATE(cursor->data, 0LL, 0, "", "", 0LL, 0, false, "", 0LL, 0, false); // Event 1 (State-Only) mu_assert_bool(sky_lua_cursor_next_event(cursor)); ASSERT_OBJ_STATE(cursor->data, 0LL, 0, "", "john doe", 1000LL, 100.2, true, "", 0LL, 0, false); // Event 2 (Action + Action Data) mu_assert_bool(sky_lua_cursor_next_event(cursor)); ASSERT_OBJ_STATE(cursor->data, sky_timestamp_shift(1000000LL), 1, "A1", "john doe", 1000LL, 100.2, true, "super", 21LL, 100, true); // Event 3 (Action-Only) mu_assert_bool(sky_lua_cursor_next_event(cursor)); ASSERT_OBJ_STATE(cursor->data, sky_timestamp_shift(2000000LL), 2, "A2", "john doe", 1000LL, 100.2, true, "", 0LL, 0, false); // Event 4 (Data-Only) mu_assert_bool(sky_lua_cursor_next_event(cursor)); ASSERT_OBJ_STATE(cursor->data, sky_timestamp_shift(3000000LL), 3, "", "frank sinatra", 20LL, -100, false, "", 0LL, 0, false); // EOF mu_assert_bool(!sky_lua_cursor_next_event(cursor)); sky_cursor_free(cursor); return 0; }
// Adds an event to the tablet. // // tablet - The tablet. // event - The event to add. // // Returns 0 if successful, otherwise returns -1. int sky_tablet_add_event(sky_tablet *tablet, sky_event *event) { int rc; char *errptr = NULL; void *new_data = NULL; void *data = NULL; sky_data_object *data_object = NULL; sky_data_descriptor *descriptor = NULL; sky_cursor cursor; memset(&cursor, 0, sizeof(cursor)); assert(tablet != NULL); assert(event != NULL); // Make sure that this event is being added to the correct tablet. sky_tablet *target_tablet = NULL; rc = sky_table_get_target_tablet(tablet->table, event->object_id, &target_tablet); check(rc == 0, "Unable to determine target tablet"); check(tablet == target_tablet, "Event added to invalid tablet; IDX:%d of %d, OID:%d", tablet->index, tablet->table->tablet_count, event->object_id); // Retrieve the existing value. size_t data_length; data = (void*)leveldb_get(tablet->leveldb_db, tablet->readoptions, (const char*)&event->object_id, sizeof(event->object_id), &data_length, &errptr); check(errptr == NULL, "LevelDB get error: %s", errptr); // Find the insertion point on the path. size_t insert_offset = 0; size_t event_length; // If the object doesn't exist yet then just set the single event. Easy peasy. if(data == NULL) { event_length = sky_event_sizeof(event); new_data = calloc(1, event_length); check_mem(new_data); insert_offset = 0; } // If the object does exist, we need to find where to insert the event data. // Also, we need to strip off any state which is redundant at the point of // insertion. else { void *insert_ptr = NULL; // Initialize data descriptor. descriptor = sky_data_descriptor_create(); check_mem(descriptor); rc = sky_data_descriptor_init_with_event(descriptor, event); check(rc == 0, "Unable to initialize data descriptor for event insert"); // Initialize data object. data_object = calloc(1, descriptor->data_sz); check_mem(data); // Attach data & descriptor to the cursor. cursor.data_descriptor = descriptor; cursor.data = (void*)data_object; // Initialize the cursor. rc = sky_cursor_set_ptr(&cursor, data, data_length); check(rc == 0, "Unable to set pointer on cursor"); // Loop over cursor until we reach the event insertion point. while(!cursor.eof) { // Retrieve event insertion pointer once the timestamp is reached. if(data_object->ts >= event->timestamp) { insert_ptr = cursor.ptr; break; } // Move to next event. check(sky_cursor_next_event(&cursor) == 0, "Unable to move to next event"); } // If no insertion point was found then append the event to the // end of the path. if(insert_ptr == NULL) { insert_ptr = data + data_length; } insert_offset = insert_ptr - data; // Clear off any object data on the event that matches // what is the current state of the event in the database. uint32_t i; for(i=0; i<event->data_count; i++) { // Ignore any action properties. if(event->data[i]->key > 0) { sky_data_property_descriptor *property_descriptor = &descriptor->property_zero_descriptor[event->data[i]->key]; // If the values match then splice this from the array. // Compare strings. void *a = &event->data[i]->value; void *b = ((void*)data_object)+property_descriptor->offset; size_t n = sky_data_type_sizeof(event->data[i]->data_type); bool is_equal = false; if(event->data[i]->data_type == SKY_DATA_TYPE_STRING) { is_equal = sky_string_bequals((sky_string*)b, event->data[i]->string_value); } // Compare other types. else if(memcmp(a, b, n) == 0) { is_equal = true; } // If the data is equal then remove it. if(is_equal) { sky_event_data_free(event->data[i]); if(i < event->data_count - 1) { memmove(&event->data[i], &event->data[i+1], (event->data_count-i-1) * sizeof(*event->data)); } i--; event->data_count--; } } } // Determine the serialized size of the event. If the event is // completely redundant (e.g. it is a data-only event and the event // matches the current object state) then don't allocate space for a // new path value. event_length = sky_event_sizeof(event); if(event_length > 0) { // Allocate space for the existing data plus the new data. new_data = calloc(1, data_length + event_length); check_mem(new_data); // Copy in data before event. if(insert_offset > 0) { memmove(new_data, data, insert_ptr-data); } // Copy in data after event. if(insert_offset < data_length) { event_length = sky_event_sizeof(event); memmove(new_data+insert_offset+event_length, data+insert_offset, data_length-insert_offset); } } } // If no space was allocated then it means the event is redundant and // should be ignored. if(new_data != NULL) { // If the object doesn't exist then just set the event as the data. size_t event_sz; rc = sky_event_pack(event, new_data + insert_offset, &event_sz); check(rc == 0, "Unable to pack event"); check(event_sz == event_length, "Expected event size (%ld) does not match actual event size (%ld)", event_length, event_sz); leveldb_put(tablet->leveldb_db, tablet->writeoptions, (const char*)&event->object_id, sizeof(event->object_id), new_data, data_length + event_length, &errptr); check(errptr == NULL, "LevelDB put error: %s", errptr); } free(data_object); sky_data_descriptor_free(descriptor); free(data); free(new_data); return 0; error: if(errptr) leveldb_free(errptr); sky_data_descriptor_free(descriptor); if(data) free(data); if(new_data) free(new_data); if(data_object) free(data_object); return -1; }
int test_sky_cursor_sessionize() { // Setup data object. sky_cursor *cursor = sky_cursor_new(-2, 1); sky_cursor_set_timestamp_offset(cursor, offsetof(test_t, timestamp)); sky_cursor_set_ts_offset(cursor, offsetof(test_t, ts)); sky_cursor_set_property(cursor, -2, offsetof(test_t, action_int), sizeof(int32_t), "integer"); sky_cursor_set_property(cursor, -1, offsetof(test_t, action), sizeof(sky_string), "string"); sky_cursor_set_property(cursor, 1, offsetof(test_t, object_int), sizeof(int32_t), "integer"); sky_cursor_set_data_sz(cursor, sizeof(test_t)); // Initialize data and set a 10 second idle time. sky_cursor_set_ptr(cursor, DATA1, DATA1_LENGTH); sky_cursor_set_session_idle(cursor, 10); mu_assert_int_equals(cursor->session_event_index, -1); ASSERT_OBJ_STATE2(cursor->data, 0, "", 0LL, 0LL); // Pre-session mu_assert_bool(sky_lua_cursor_next_event(cursor) == false); mu_assert_int_equals(cursor->session_event_index, -1); ASSERT_OBJ_STATE2(cursor->data, 0, "", 0LL, 0LL); // Session 1 mu_assert_bool(sky_lua_cursor_next_session(cursor)); mu_assert_int_equals(cursor->session_event_index, -1); ASSERT_OBJ_STATE2(cursor->data, 0, "", 0LL, 0LL); // Session 1, Event 1 mu_assert_bool(sky_lua_cursor_next_event(cursor)); mu_assert_int_equals(cursor->session_event_index, 0); ASSERT_OBJ_STATE2(cursor->data, 0, "A1", 1000LL, 0LL); // Session 1, Event 2 mu_assert_bool(sky_lua_cursor_next_event(cursor)); mu_assert_int_equals(cursor->session_event_index, 1); ASSERT_OBJ_STATE2(cursor->data, 1, "A2", 1000LL, 100LL); // Session 1, Event 3 mu_assert_bool(sky_lua_cursor_next_event(cursor)); mu_assert_int_equals(cursor->session_event_index, 2); ASSERT_OBJ_STATE2(cursor->data, 10, "A3", 1000LL, 200LL); // Prevent next session! mu_assert_bool(sky_lua_cursor_next_event(cursor) == false); mu_assert_int_equals(cursor->session_event_index, 2); ASSERT_OBJ_STATE2(cursor->data, 10, "A3", 1000LL, 200LL); // Session 2 (Single Event) mu_assert_bool(sky_lua_cursor_next_session(cursor)); mu_assert_bool(sky_lua_cursor_next_event(cursor)); mu_assert_int_equals(cursor->session_event_index, 0); ASSERT_OBJ_STATE2(cursor->data, 20, "A1", 1000LL, 300LL); mu_assert_bool(sky_lua_cursor_next_event(cursor) == false); // Session 3 (with same data) mu_assert_bool(sky_lua_cursor_next_session(cursor)); mu_assert_int_equals(cursor->session_event_index, -1); ASSERT_OBJ_STATE2(cursor->data, 20, "A1", 1000LL, 300LL); // Session 3, Event 1 mu_assert_bool(sky_lua_cursor_next_event(cursor)); mu_assert_int_equals(cursor->session_event_index, 0); ASSERT_OBJ_STATE2(cursor->data, 60, "A1", 2000LL, 0LL); // Session 3, Event 2 mu_assert_bool(sky_lua_cursor_next_event(cursor)); mu_assert_int_equals(cursor->session_event_index, 1); ASSERT_OBJ_STATE2(cursor->data, 63, "A2", 2000LL, 400LL); // Prevent next session! mu_assert_bool(sky_lua_cursor_next_event(cursor) == false); mu_assert_bool(sky_lua_cursor_next_session(cursor) == false); // EOF! mu_assert_bool(cursor->eof == true); mu_assert_bool(cursor->in_session == false); // Reuse cursor. sky_cursor_set_ptr(cursor, DATA1, DATA1_LENGTH); mu_assert_int_equals(cursor->session_event_index, -1); mu_assert_bool(sky_lua_cursor_next_event(cursor)); mu_assert_int_equals(cursor->session_event_index, 0); ASSERT_OBJ_STATE2(cursor->data, 0, "A1", 1000LL, 0LL); sky_cursor_free(cursor); return 0; }