/* * cursor_open () - * return: true on all ok, false otherwise * cursor_id(out): Cursor identifier * list_id: List file identifier * updatable: Flag which indicates if cursor is updatable * is_oid_included: Flag which indicates if first column of the list file * contains hidden object identifiers * * Note: A cursor is opened to scan through the tuples of the given * list file. The cursor identifier is initialized and memory * buffer for the cursor identifier is allocated. If is_oid_included * flag is set to true, this indicates that the first column * of list file tuples contains the object identifier to be used * for cursor update/delete operations. */ bool cursor_open (CURSOR_ID * cursor_id_p, QFILE_LIST_ID * list_id_p, bool updatable, bool is_oid_included) { static QFILE_LIST_ID empty_list_id; /* TODO: remove static empty_list_id */ QFILE_CLEAR_LIST_ID (&empty_list_id); cursor_id_p->is_updatable = updatable; cursor_id_p->is_oid_included = is_oid_included; cursor_id_p->oid_ent_count = 0; cursor_id_p->oid_set = NULL; cursor_id_p->mop_set = NULL; cursor_id_p->position = C_BEFORE; cursor_id_p->tuple_no = -1; VPID_SET_NULL (&cursor_id_p->current_vpid); VPID_SET_NULL (&cursor_id_p->next_vpid); VPID_SET_NULL (&cursor_id_p->header_vpid); cursor_id_p->tuple_record.size = 0; cursor_id_p->tuple_record.tpl = NULL; cursor_id_p->on_overflow = false; cursor_id_p->buffer_tuple_count = 0; cursor_id_p->current_tuple_no = -1; cursor_id_p->current_tuple_offset = -1; cursor_id_p->current_tuple_p = NULL; cursor_id_p->current_tuple_length = -1; cursor_id_p->oid_col_no = NULL; cursor_id_p->oid_col_no_cnt = 0; cursor_id_p->buffer = NULL; cursor_id_p->buffer_area = NULL; cursor_id_p->buffer_filled_size = 0; cursor_id_p->list_id = empty_list_id; cursor_id_p->prefetch_lock_mode = DB_FETCH_READ; cursor_id_p->is_copy_tuple_value = true; /* copy */ cursor_initialize_current_tuple_value_position (cursor_id_p); if (cursor_copy_list_id (&cursor_id_p->list_id, list_id_p) != NO_ERROR) { return false; } if (cursor_id_p->list_id.type_list.type_cnt) { cursor_id_p->buffer_area = (char *) malloc (CURSOR_BUFFER_AREA_SIZE); cursor_id_p->buffer = cursor_id_p->buffer_area; if (cursor_id_p->buffer == NULL) { return false; } if (is_oid_included) { cursor_allocate_oid_buffer (cursor_id_p); } } return true; }
/* * overflow_rv_newpage_link_undo () - Undo allocation of new overflow page and the * reference to it * return: 0 if no error, or error code * rcv(in): Recovery structure */ int overflow_rv_newpage_link_undo (THREAD_ENTRY * thread_p, LOG_RCV * rcv) { OVERFLOW_REST_PART *rest_parts; rest_parts = (OVERFLOW_REST_PART *) rcv->pgptr; VPID_SET_NULL (&rest_parts->next_vpid); pgbuf_set_dirty (thread_p, rcv->pgptr, DONT_FREE); return NO_ERROR; }
/* * overflow_update () - Update the content of a multipage data * return: ovf_vpid on success or NULL on failure * ovf_vfid(in): File where the overflow data is stored * WARNING: MUST BE THE SAME AS IT WAS GIVEN DURING INSERT * ovf_vpid(in): Overflow address * recdes(in): Record descriptor * * Note: The function may allocate or deallocate several overflow pages if * the multipage data increase/decrease in length. * * Overflow pages are not locked in any mode since they are not shared * by other data and its address is know by accessing the relocation * overflow record which has been appropriately locked. */ const VPID * overflow_update (THREAD_ENTRY * thread_p, const VFID * ovf_vfid, const VPID * ovf_vpid, RECDES * recdes) { OVERFLOW_FIRST_PART *first_part = NULL; OVERFLOW_REST_PART *rest_parts = NULL; char *copyto; OVERFLOW_RECV_LINKS recv; VPID tmp_vpid; int hdr_length; int copy_length; int old_length = 0; int length; char *data; VPID next_vpid; VPID *addr_vpid_ptr; LOG_DATA_ADDR addr; LOG_DATA_ADDR logical_undoaddr; bool isnewpage = false; /* * We don't need to lock the overflow pages since these pages are not * shared among several pieces of overflow data. The overflow pages are * know by accessing the relocation-overflow record with the appropiate lock */ addr.vfid = ovf_vfid; addr.offset = 0; logical_undoaddr.vfid = ovf_vfid; logical_undoaddr.offset = 0; logical_undoaddr.pgptr = NULL; recv.ovf_vfid = *ovf_vfid; next_vpid = *ovf_vpid; data = recdes->data; length = recdes->length; while (length > 0) { addr.pgptr = pgbuf_fix (thread_p, &next_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH); if (addr.pgptr == NULL) { goto exit_on_error; } addr_vpid_ptr = pgbuf_get_vpid_ptr (addr.pgptr); /* Log before and after images */ /* Is this the first page ? */ if (VPID_EQ (addr_vpid_ptr, ovf_vpid)) { /* This is the first part */ first_part = (OVERFLOW_FIRST_PART *) addr.pgptr; old_length = first_part->length; copyto = (char *) first_part->data; next_vpid = first_part->next_vpid; hdr_length = offsetof (OVERFLOW_FIRST_PART, data); if ((length + hdr_length) > DB_PAGESIZE) { copy_length = DB_PAGESIZE - hdr_length; } else { copy_length = length; } /* Log before image */ if (hdr_length + old_length > DB_PAGESIZE) { log_append_undo_data (thread_p, RVOVF_PAGE_UPDATE, &addr, DB_PAGESIZE, addr.pgptr); old_length -= DB_PAGESIZE - hdr_length; } else { log_append_undo_data (thread_p, RVOVF_PAGE_UPDATE, &addr, hdr_length + old_length, addr.pgptr); old_length = 0; } /* Modify the new length */ first_part->length = length; /* notify the first part of overflow recdes */ log_append_empty_record (thread_p, LOG_DUMMY_OVF_RECORD); } else { rest_parts = (OVERFLOW_REST_PART *) addr.pgptr; copyto = (char *) rest_parts->data; if (isnewpage == true) { VPID_SET_NULL (&next_vpid); rest_parts->next_vpid = next_vpid; } else { next_vpid = rest_parts->next_vpid; } hdr_length = offsetof (OVERFLOW_REST_PART, data); if ((length + hdr_length) > DB_PAGESIZE) { copy_length = DB_PAGESIZE - hdr_length; } else { copy_length = length; } if (old_length > 0) { if (hdr_length + old_length > DB_PAGESIZE) { log_append_undo_data (thread_p, RVOVF_PAGE_UPDATE, &addr, DB_PAGESIZE, addr.pgptr); old_length -= DB_PAGESIZE - hdr_length; } else { log_append_undo_data (thread_p, RVOVF_PAGE_UPDATE, &addr, hdr_length + old_length, addr.pgptr); old_length = 0; } } } memcpy (copyto, data, copy_length); data += copy_length; length -= copy_length; log_append_redo_data (thread_p, RVOVF_PAGE_UPDATE, &addr, copy_length + hdr_length, (char *) addr.pgptr); if (length > 0) { /* Need more pages... Get next page */ if (VPID_ISNULL (&next_vpid)) { /* We need to allocate a new page */ if (file_alloc_pages (thread_p, ovf_vfid, &next_vpid, 1, addr_vpid_ptr, NULL, NULL) == NULL) { pgbuf_set_dirty (thread_p, addr.pgptr, FREE); addr.pgptr = NULL; goto exit_on_error; } recv.new_vpid = next_vpid; log_append_undoredo_data (thread_p, RVOVF_NEWPAGE_LINK, &addr, sizeof (recv), sizeof (next_vpid), &recv, &next_vpid); isnewpage = true; /* So that its link can be set to NULL */ /* This logical undo is to remove the page in case of rollback */ if (file_is_new_file (thread_p, ovf_vfid) == FILE_OLD_FILE) { /* we don't do undo logging for new files */ log_append_undo_data (thread_p, RVOVF_NEWPAGE_LOGICAL_UNDO, &logical_undoaddr, sizeof (recv), &recv); } if (rest_parts == NULL) { /* This is the first part */ first_part->next_vpid = next_vpid; } else { /* This is part of rest part */ rest_parts->next_vpid = next_vpid; } } pgbuf_set_dirty (thread_p, addr.pgptr, FREE); addr.pgptr = NULL; } else { /* The content of the data has been copied. We don't need more pages. Deallocate any additional pages */ VPID_SET_NULL (&tmp_vpid); log_append_undoredo_data (thread_p, RVOVF_CHANGE_LINK, &addr, sizeof (next_vpid), sizeof (next_vpid), &next_vpid, &tmp_vpid); if (rest_parts == NULL) { /* This is the first part */ VPID_SET_NULL (&first_part->next_vpid); } else { /* This is part of rest part */ VPID_SET_NULL (&rest_parts->next_vpid); } pgbuf_set_dirty (thread_p, addr.pgptr, FREE); addr.pgptr = NULL; while (!(VPID_ISNULL (&next_vpid))) { addr.pgptr = pgbuf_fix (thread_p, &next_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH); if (addr.pgptr == NULL) { goto exit_on_error; } tmp_vpid = next_vpid; rest_parts = (OVERFLOW_REST_PART *) addr.pgptr; next_vpid = rest_parts->next_vpid; if (pgbuf_invalidate (thread_p, addr.pgptr) != NO_ERROR) { goto exit_on_error; } addr.pgptr = NULL; if (file_dealloc_page (thread_p, ovf_vfid, &tmp_vpid) != NO_ERROR) { goto exit_on_error; } } break; } } return ovf_vpid; exit_on_error: return NULL; }
/* * overflow_insert () - Insert a multipage data in overflow * return: ovf_vpid on success or NULL on failure * ovf_vfid(in): File where the overflow data is going to be stored * ovf_vpid(out): Overflow address * recdes(in): Record descriptor * * Note: Data in overflow is composed of several pages. Pages in the overflow * area are not shared among other pieces of overflow data. * * -------------------------------- ------------------------ * |Next_vpid |Length|... data ...| ... --> |Next_vpid|... data ...| * -------------------------------- ------------------------ * * Single link list of pages. * The length of the multipage data is stored on its first overflow page * * Overflow pages are not locked in any mode since they are not shared * by other pieces of data and its address is only know by accessing the * relocation overflow record data which has been appropriately locked. */ VPID * overflow_insert (THREAD_ENTRY * thread_p, const VFID * ovf_vfid, VPID * ovf_vpid, RECDES * recdes) { PAGE_PTR vfid_fhdr_pgptr = NULL; OVERFLOW_FIRST_PART *first_part; OVERFLOW_REST_PART *rest_parts; OVERFLOW_RECV_LINKS undo_recv; char *copyto; int length, copy_length; INT32 npages = 0; char *data; int alloc_nth; LOG_DATA_ADDR addr; LOG_DATA_ADDR logical_undoaddr; int i; VPID *vpids, fhdr_vpid; VPID vpids_buffer[OVERFLOW_ALLOCVPID_ARRAY_SIZE + 1]; FILE_ALLOC_VPIDS alloc_vpids; /* * We don't need to lock the overflow pages since these pages are not * shared among several pieces of overflow data. The overflow pages are * know by accessing the relocation-overflow record with the appropiate lock */ addr.vfid = ovf_vfid; addr.offset = 0; logical_undoaddr.vfid = ovf_vfid; logical_undoaddr.offset = 0; logical_undoaddr.pgptr = NULL; undo_recv.ovf_vfid = *ovf_vfid; /* * Temporary: * Lock the file header, so I am the only one changing the file table * of allocated pages. This is needed since this function is using * file_find_nthpages, which could give me not the expected page, if someone * else remove pages, after the initial allocation. */ fhdr_vpid.volid = ovf_vfid->volid; fhdr_vpid.pageid = ovf_vfid->fileid; vfid_fhdr_pgptr = pgbuf_fix (thread_p, &fhdr_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH); if (vfid_fhdr_pgptr == NULL) { return NULL; } /* * Guess the number of pages. The total number of pages is found by * dividing length by pagesize - the smallest header. Then, we make sure * that this estimate is correct. */ length = recdes->length - (DB_PAGESIZE - (int) offsetof (OVERFLOW_FIRST_PART, data)); if (length > 0) { i = DB_PAGESIZE - offsetof (OVERFLOW_REST_PART, data); npages = 1 + CEIL_PTVDIV (length, i); } else { npages = 1; } if (npages > OVERFLOW_ALLOCVPID_ARRAY_SIZE) { vpids = (VPID *) malloc ((npages + 1) * sizeof (VPID)); if (vpids == NULL) { er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (npages + 1) * sizeof (VPID)); pgbuf_unfix (thread_p, vfid_fhdr_pgptr); return NULL; } } else { vpids = vpids_buffer; } #if !defined(NDEBUG) for (i = 0; i < npages; i++) { VPID_SET_NULL (&vpids[i]); } #endif VPID_SET_NULL (&vpids[npages]); alloc_vpids.vpids = vpids; alloc_vpids.index = 0; /* * We do not initialize the pages during allocation since they are not * pointed by anyone until we return from this function, at that point * they are initialized. */ if (file_alloc_pages_as_noncontiguous (thread_p, ovf_vfid, vpids, &alloc_nth, npages, NULL, NULL, NULL, &alloc_vpids) == NULL) { if (vpids != vpids_buffer) { free_and_init (vpids); } pgbuf_unfix (thread_p, vfid_fhdr_pgptr); return NULL; } assert (alloc_vpids.index == npages); #if !defined(NDEBUG) for (i = 0; i < npages; i++) { assert (!VPID_ISNULL (&vpids[i])); } #endif *ovf_vpid = vpids[0]; /* Copy the content of the data */ data = recdes->data; length = recdes->length; for (i = 0; i < npages; i++) { addr.pgptr = pgbuf_fix (thread_p, &vpids[i], NEW_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH); if (addr.pgptr == NULL) { goto exit_on_error; } /* Is this the first page ? */ if (i == 0) { /* This is the first part */ first_part = (OVERFLOW_FIRST_PART *) addr.pgptr; first_part->next_vpid = vpids[i + 1]; first_part->length = length; copyto = (char *) first_part->data; copy_length = DB_PAGESIZE - offsetof (OVERFLOW_FIRST_PART, data); if (length < copy_length) { copy_length = length; } /* notify the first part of overflow recdes */ log_append_empty_record (thread_p, LOG_DUMMY_OVF_RECORD); } else { rest_parts = (OVERFLOW_REST_PART *) addr.pgptr; rest_parts->next_vpid = vpids[i + 1]; copyto = (char *) rest_parts->data; copy_length = DB_PAGESIZE - offsetof (OVERFLOW_REST_PART, data); if (length < copy_length) { copy_length = length; } } memcpy (copyto, data, copy_length); data += copy_length; length -= copy_length; pgbuf_get_vpid (addr.pgptr, &undo_recv.new_vpid); if (file_is_new_file (thread_p, ovf_vfid) == FILE_OLD_FILE) { /* we don't do undo logging for new files */ log_append_undo_data (thread_p, RVOVF_NEWPAGE_LOGICAL_UNDO, &logical_undoaddr, sizeof (undo_recv), &undo_recv); } log_append_redo_data (thread_p, RVOVF_NEWPAGE_INSERT, &addr, copy_length + CAST_BUFLEN (copyto - (char *) addr.pgptr), (char *) addr.pgptr); pgbuf_set_dirty (thread_p, addr.pgptr, FREE); addr.pgptr = NULL; } assert (length == 0); #if defined (CUBRID_DEBUG) if (length > 0) { er_log_debug (ARG_FILE_LINE, "ovf_insert: ** SYSTEM ERROR calculation" " of number of pages needed to store overflow data seems" " incorrect. Need no more than %d pages", npages); goto exit_on_error; } #endif /* * Temporary: * Unlock the file header, so I am the only one changing the file table * of allocated pages. This is needed since curently, I am using * file_find_nthpages, which could give me not the expected page, if someone * else remove pages. */ if (vpids != vpids_buffer) { free_and_init (vpids); } pgbuf_unfix (thread_p, vfid_fhdr_pgptr); return ovf_vpid; exit_on_error: for (i = 0; i < npages; i++) { (void) file_dealloc_page (thread_p, ovf_vfid, &vpids[i]); } if (vpids != vpids_buffer) { free_and_init (vpids); } pgbuf_unfix (thread_p, vfid_fhdr_pgptr); return NULL; }