/* * __curjoin_endpoint_init_key -- * Set the key in the reference endpoint. */ static int __curjoin_endpoint_init_key(WT_SESSION_IMPL *session, WT_CURSOR_JOIN_ENTRY *entry, WT_CURSOR_JOIN_ENDPOINT *endpoint) { WT_CURSOR *cursor; WT_CURSOR_INDEX *cindex; WT_ITEM *k; uint64_t r; if ((cursor = endpoint->cursor) != NULL) { if (entry->index != NULL) { /* Extract and save the index's logical key. */ cindex = (WT_CURSOR_INDEX *)endpoint->cursor; WT_RET(__wt_struct_repack(session, cindex->child->key_format, (entry->repack_format != NULL ? entry->repack_format : cindex->iface.key_format), &cindex->child->key, &endpoint->key)); } else { k = &((WT_CURSOR_TABLE *)cursor)->cg_cursors[0]->key; if (WT_CURSOR_RECNO(cursor)) { r = *(uint64_t *)k->data; WT_RET(__curjoin_pack_recno(session, r, endpoint->recno_buf, sizeof(endpoint->recno_buf), &endpoint->key)); } else endpoint->key = *k; } } return (0); }
/* * __curdump_get_key -- * WT_CURSOR->get_key for dump cursors. */ static int __curdump_get_key(WT_CURSOR *cursor, ...) { WT_CURSOR *child; WT_CURSOR_DUMP *cdump; WT_DECL_RET; WT_ITEM item, *itemp; WT_SESSION_IMPL *session; uint64_t recno; va_list ap; cdump = (WT_CURSOR_DUMP *)cursor; child = cdump->child; CURSOR_API_CALL(cursor, session, get_key, NULL); if (WT_CURSOR_RECNO(cursor) && !F_ISSET(cursor, WT_CURSTD_RAW)) { WT_ERR(child->get_key(child, &recno)); WT_ERR(__wt_buf_fmt(session, &cursor->key, "%" PRIu64, recno)); } else { WT_ERR(child->get_key(child, &item)); WT_ERR(__raw_to_dump(session, &item, &cursor->key, F_ISSET(cursor, WT_CURSTD_DUMP_HEX) ? 1 : 0)); } va_start(ap, cursor); if (F_ISSET(cursor, WT_CURSTD_RAW)) { itemp = va_arg(ap, WT_ITEM *); itemp->data = cursor->key.data; itemp->size = cursor->key.size; } else
/* * __curjoin_split_key -- * Copy the primary key from a cursor (either main table or index) * to another cursor. When copying from an index file, the index * key is also returned. * */ static int __curjoin_split_key(WT_SESSION_IMPL *session, WT_CURSOR_JOIN *cjoin, WT_ITEM *idxkey, WT_CURSOR *tocur, WT_CURSOR *fromcur, const char *repack_fmt, bool isindex) { WT_CURSOR *firstcg_cur; WT_CURSOR_INDEX *cindex; WT_ITEM *keyp; const uint8_t *p; if (isindex) { cindex = ((WT_CURSOR_INDEX *)fromcur); /* * Repack tells us where the index key ends; advance past * that to get where the raw primary key starts. */ WT_RET(__wt_struct_repack(session, cindex->child->key_format, repack_fmt != NULL ? repack_fmt : cindex->iface.key_format, &cindex->child->key, idxkey)); WT_ASSERT(session, cindex->child->key.size > idxkey->size); tocur->key.data = (uint8_t *)idxkey->data + idxkey->size; tocur->key.size = cindex->child->key.size - idxkey->size; if (WT_CURSOR_RECNO(tocur)) { p = (const uint8_t *)tocur->key.data; WT_RET(__wt_vunpack_uint(&p, tocur->key.size, &tocur->recno)); } else tocur->recno = 0; } else { firstcg_cur = ((WT_CURSOR_TABLE *)fromcur)->cg_cursors[0]; keyp = &firstcg_cur->key; if (WT_CURSOR_RECNO(tocur)) { WT_ASSERT(session, keyp->size == sizeof(uint64_t)); tocur->recno = *(uint64_t *)keyp->data; WT_RET(__curjoin_pack_recno(session, tocur->recno, cjoin->recno_buf, sizeof(cjoin->recno_buf), &tocur->key)); } else { WT_ITEM_SET(tocur->key, *keyp); tocur->recno = 0; } idxkey->data = NULL; idxkey->size = 0; } return (0); }
/* * __async_set_key -- * WT_ASYNC_OP->set_key implementation for op handles. */ static void __async_set_key(WT_ASYNC_OP *asyncop, ...) { WT_CURSOR *c; va_list ap; c = &asyncop->c; va_start(ap, asyncop); __wt_cursor_set_keyv(c, c->flags, ap); if (!WT_DATA_IN_ITEM(&c->key) && !WT_CURSOR_RECNO(c)) c->saved_err = __wt_buf_set( O2S((WT_ASYNC_OP_IMPL *)asyncop), &c->key, c->key.data, c->key.size); va_end(ap); }
/* * __curjoin_endpoint_init_key -- * Set the key in the reference endpoint. */ static int __curjoin_endpoint_init_key(WT_SESSION_IMPL *session, WT_CURSOR_JOIN_ENTRY *entry, WT_CURSOR_JOIN_ENDPOINT *endpoint) { WT_CURSOR *cursor; WT_CURSOR_INDEX *cindex; WT_DECL_RET; WT_ITEM *k; uint64_t r; void *allocbuf; allocbuf = NULL; if ((cursor = endpoint->cursor) != NULL) { if (entry->index != NULL) { cindex = (WT_CURSOR_INDEX *)endpoint->cursor; if (cindex->index->extractor == NULL) { WT_ERR(__wt_struct_repack(session, cindex->child->key_format, entry->main->value_format, &cindex->child->key, &endpoint->key, &allocbuf)); if (allocbuf != NULL) F_SET(endpoint, WT_CURJOIN_END_OWN_KEY); } else endpoint->key = cindex->child->key; } else { k = &((WT_CURSOR_TABLE *)cursor)->cg_cursors[0]->key; if (WT_CURSOR_RECNO(cursor)) { r = *(uint64_t *)k->data; WT_ERR(__curjoin_pack_recno(session, r, endpoint->recno_buf, sizeof(endpoint->recno_buf), &endpoint->key)); } else endpoint->key = *k; } } if (0) { err: __wt_free(session, allocbuf); } return (ret); }
/* * __curjoin_entry_iter_next -- * Get the next item in an iteration. * */ static int __curjoin_entry_iter_next(WT_CURSOR_JOIN_ITER *iter, WT_ITEM *primkey, uint64_t *rp) { WT_CURSOR *firstcg_cur; WT_CURSOR_JOIN *cjoin; WT_DECL_RET; WT_SESSION_IMPL *session; uint64_t r; if (iter->advance) WT_ERR(iter->cursor->next(iter->cursor)); else iter->advance = true; session = iter->session; cjoin = iter->cjoin; /* * Set our key to the primary key, we'll also need this * to check membership. */ if (iter->entry->index != NULL) firstcg_cur = ((WT_CURSOR_INDEX *)iter->cursor)->cg_cursors[0]; else firstcg_cur = ((WT_CURSOR_TABLE *)iter->cursor)->cg_cursors[0]; if (WT_CURSOR_RECNO(&cjoin->iface)) { r = *(uint64_t *)firstcg_cur->key.data; WT_ERR(__curjoin_pack_recno(session, r, cjoin->recno_buf, sizeof(cjoin->recno_buf), primkey)); *rp = r; } else { WT_ITEM_SET(*primkey, firstcg_cur->key); *rp = 0; } iter->curkey = primkey; iter->entry->stats.actual_count++; iter->entry->stats.accesses++; err: return (ret); }
/* * __curds_compare -- * WT_CURSOR.compare method for the data-source cursor type. */ static int __curds_compare(WT_CURSOR *a, WT_CURSOR *b, int *cmpp) { WT_COLLATOR *collator; WT_DECL_RET; WT_SESSION_IMPL *session; CURSOR_API_CALL(a, session, compare, NULL); /* * Confirm both cursors refer to the same source and have keys, then * compare them. */ if (strcmp(a->internal_uri, b->internal_uri) != 0) WT_ERR_MSG(session, EINVAL, "Cursors must reference the same object"); WT_ERR(__cursor_needkey(a)); WT_ERR(__cursor_needkey(b)); if (WT_CURSOR_RECNO(a)) { if (a->recno < b->recno) *cmpp = -1; else if (a->recno == b->recno) *cmpp = 0; else *cmpp = 1; } else { /* * The assumption is data-sources don't provide WiredTiger with * WT_CURSOR.compare methods, instead, we'll copy the key/value * out of the underlying data-source cursor and any comparison * to be done can be done at this level. */ collator = ((WT_CURSOR_DATA_SOURCE *)a)->collator; WT_ERR(__wt_compare( session, collator, &a->key, &b->key, cmpp)); } err: API_END_RET(session, ret); }
/* * __wt_schema_project_slice -- * Given list of cursors and a projection, read columns from the * a raw buffer. */ int __wt_schema_project_slice(WT_SESSION_IMPL *session, WT_CURSOR **cp, const char *proj_arg, int key_only, const char *vformat, WT_ITEM *value) { WT_CURSOR *c; WT_ITEM *buf; WT_PACK pack, vpack; WT_PACK_VALUE pv, vpv; char *proj; uint8_t *end, *p; const uint8_t *next, *vp, *vend; size_t len, offset, old_len; uint32_t arg; int skip; WT_CLEAR(pack); /* -Wuninitialized */ WT_CLEAR(vpv); /* -Wuninitialized */ buf = NULL; /* -Wuninitialized */ p = end = NULL; /* -Wuninitialized */ WT_RET(__pack_init(session, &vpack, vformat)); vp = (uint8_t *)value->data; vend = vp + value->size; /* Reset any of the buffers we will be setting. */ for (proj = (char *)proj_arg; *proj != '\0'; proj++) { arg = (uint32_t)strtoul(proj, &proj, 10); if (*proj == WT_PROJ_KEY) { c = cp[arg]; WT_RET(__wt_buf_init(session, &c->key, 0)); } else if (*proj == WT_PROJ_VALUE && !key_only) { c = cp[arg]; WT_RET(__wt_buf_init(session, &c->value, 0)); } } skip = key_only; for (proj = (char *)proj_arg; *proj != '\0'; proj++) { arg = (uint32_t)strtoul(proj, &proj, 10); switch (*proj) { case WT_PROJ_KEY: skip = 0; c = cp[arg]; if (WT_CURSOR_RECNO(c)) { c->key.data = &c->recno; c->key.size = sizeof(c->recno); WT_RET(__pack_init(session, &pack, "R")); } else WT_RET(__pack_init( session, &pack, c->key_format)); buf = &c->key; p = (uint8_t *)buf->data; end = p + buf->size; continue; case WT_PROJ_VALUE: if ((skip = key_only) != 0) continue; c = cp[arg]; WT_RET(__pack_init(session, &pack, c->value_format)); buf = &c->value; p = (uint8_t *)buf->data; end = p + buf->size; continue; } /* * Otherwise, the argument is a count, where a missing * count means a count of 1. */ for (arg = (arg == 0) ? 1 : arg; arg > 0; arg--) { switch (*proj) { case WT_PROJ_NEXT: case WT_PROJ_SKIP: if (!skip) { WT_RET(__pack_next(&pack, &pv)); /* * A nasty case: if we are inserting * out-of-order, append a zero value * to keep the buffer in the correct * format. */ if (*proj == WT_PROJ_SKIP && p == end) { /* Set up an empty value. */ WT_CLEAR(pv.u); if (pv.type == 'S' || pv.type == 's') pv.u.s = ""; len = __pack_size(session, &pv); WT_RET(__wt_buf_grow(session, buf, buf->size + len)); p = (uint8_t *)buf->data + buf->size; WT_RET(__pack_write( session, &pv, &p, len)); end = p; buf->size += WT_STORE_SIZE(len); } else if (*proj == WT_PROJ_SKIP) WT_RET(__unpack_read(session, &pv, (const uint8_t **)&p, (size_t)(end - p))); } if (*proj == WT_PROJ_SKIP) break; WT_RET(__pack_next(&vpack, &vpv)); WT_RET(__unpack_read(session, &vpv, &vp, (size_t)(vend - vp))); /* FALLTHROUGH */ case WT_PROJ_REUSE: if (skip) break; /* Read the item we're about to overwrite. */ next = p; if (p < end) WT_RET(__unpack_read(session, &pv, &next, (size_t)(end - p))); old_len = (size_t)(next - p); /* * There is subtlety here: the value format * may not exactly match the cursor's format. * In particular, we need lengths with raw * columns in the middle of a packed struct, * but not if they are at the end of a column. */ pv.u = vpv.u; len = __pack_size(session, &pv); offset = WT_PTRDIFF(p, buf->data); WT_RET(__wt_buf_grow(session, buf, buf->size + len - old_len)); p = (uint8_t *)buf->data + offset; /* Make room if we're inserting out-of-order. */ if (offset + old_len < buf->size) memmove(p + len, p + old_len, buf->size - (offset + old_len)); WT_RET(__pack_write(session, &pv, &p, len)); buf->size += WT_STORE_SIZE(len - old_len); end = (uint8_t *)buf->data + buf->size; break; default: WT_RET_MSG(session, EINVAL, "unexpected projection plan: %c", (int)*proj); } } } return (0); }
/* * __wt_curfile_create -- * Open a cursor for a given btree handle. */ int __wt_curfile_create(WT_SESSION_IMPL *session, WT_CURSOR *owner, const char *cfg[], bool bulk, bool bitmap, WT_CURSOR **cursorp) { WT_CURSOR_STATIC_INIT(iface, __wt_cursor_get_key, /* get-key */ __wt_cursor_get_value, /* get-value */ __wt_cursor_set_key, /* set-key */ __wt_cursor_set_value, /* set-value */ __curfile_compare, /* compare */ __curfile_equals, /* equals */ __curfile_next, /* next */ __curfile_prev, /* prev */ __curfile_reset, /* reset */ __curfile_search, /* search */ __curfile_search_near, /* search-near */ __curfile_insert, /* insert */ __curfile_update, /* update */ __curfile_remove, /* remove */ __wt_cursor_reconfigure, /* reconfigure */ __curfile_close); /* close */ WT_BTREE *btree; WT_CONFIG_ITEM cval; WT_CURSOR *cursor; WT_CURSOR_BTREE *cbt; WT_CURSOR_BULK *cbulk; WT_DECL_RET; size_t csize; WT_STATIC_ASSERT(offsetof(WT_CURSOR_BTREE, iface) == 0); cbt = NULL; btree = S2BT(session); WT_ASSERT(session, btree != NULL); csize = bulk ? sizeof(WT_CURSOR_BULK) : sizeof(WT_CURSOR_BTREE); WT_RET(__wt_calloc(session, 1, csize, &cbt)); cursor = &cbt->iface; *cursor = iface; cursor->session = &session->iface; cursor->internal_uri = btree->dhandle->name; cursor->key_format = btree->key_format; cursor->value_format = btree->value_format; cbt->btree = btree; if (session->dhandle->checkpoint != NULL) F_SET(cbt, WT_CBT_NO_TXN); if (bulk) { F_SET(cursor, WT_CURSTD_BULK); cbulk = (WT_CURSOR_BULK *)cbt; /* Optionally skip the validation of each bulk-loaded key. */ WT_ERR(__wt_config_gets_def( session, cfg, "skip_sort_check", 0, &cval)); WT_ERR(__wt_curbulk_init( session, cbulk, bitmap, cval.val == 0 ? 0 : 1)); } /* * Random retrieval, row-store only. * Random retrieval cursors support a limited set of methods. */ WT_ERR(__wt_config_gets_def(session, cfg, "next_random", 0, &cval)); if (cval.val != 0) { if (WT_CURSOR_RECNO(cursor)) WT_ERR_MSG(session, ENOTSUP, "next_random configuration not supported for " "column-store objects"); __wt_cursor_set_notsup(cursor); cursor->next = __curfile_next_random; cursor->reset = __curfile_reset; WT_ERR(__wt_config_gets_def( session, cfg, "next_random_sample_size", 0, &cval)); if (cval.val != 0) cbt->next_random_sample_size = (u_int)cval.val; } /* Underlying btree initialization. */ __wt_btcur_open(cbt); /* __wt_cursor_init is last so we don't have to clean up on error. */ WT_ERR(__wt_cursor_init( cursor, cursor->internal_uri, owner, cfg, cursorp)); WT_STAT_FAST_CONN_INCR(session, cursor_create); WT_STAT_FAST_DATA_INCR(session, cursor_create); if (0) { err: __wt_free(session, cbt); } return (ret); }
/* * __curdump_get_key -- * WT_CURSOR->get_key for dump cursors. */ static int __curdump_get_key(WT_CURSOR *cursor, ...) { WT_CURSOR *child; WT_CURSOR_DUMP *cdump; WT_CURSOR_JSON *json; WT_DECL_RET; WT_ITEM item, *itemp; WT_SESSION_IMPL *session; size_t size; uint64_t recno; const char *fmt; const void *buffer; va_list ap; cdump = (WT_CURSOR_DUMP *)cursor; child = cdump->child; va_start(ap, cursor); CURSOR_API_CALL(cursor, session, get_key, NULL); if (F_ISSET(cursor, WT_CURSTD_DUMP_JSON)) { json = (WT_CURSOR_JSON *)cursor->json_private; WT_ASSERT(session, json != NULL); if (WT_CURSOR_RECNO(cursor)) { WT_ERR(child->get_key(child, &recno)); buffer = &recno; size = sizeof(recno); fmt = "R"; } else { WT_ERR(__wt_cursor_get_raw_key(child, &item)); buffer = item.data; size = item.size; if (F_ISSET(cursor, WT_CURSTD_RAW)) fmt = "u"; else fmt = cursor->key_format; } ret = __wt_json_alloc_unpack( session, buffer, size, fmt, json, true, ap); } else { if (WT_CURSOR_RECNO(cursor) && !F_ISSET(cursor, WT_CURSTD_RAW)) { WT_ERR(child->get_key(child, &recno)); WT_ERR(__wt_buf_fmt(session, &cursor->key, "%" PRIu64, recno)); } else { WT_ERR(child->get_key(child, &item)); WT_ERR(__raw_to_dump(session, &item, &cursor->key, F_ISSET(cursor, WT_CURSTD_DUMP_HEX))); } if (F_ISSET(cursor, WT_CURSTD_RAW)) { itemp = va_arg(ap, WT_ITEM *); itemp->data = cursor->key.data; itemp->size = cursor->key.size; } else *va_arg(ap, const char **) = cursor->key.data; }
/* * __wt_schema_project_slice -- * Given list of cursors and a projection, read columns from the * a raw buffer. */ int __wt_schema_project_slice(WT_SESSION_IMPL *session, WT_CURSOR **cp, const char *proj_arg, bool key_only, const char *vformat, WT_ITEM *value) { WT_CURSOR *c; WT_DECL_ITEM(buf); WT_DECL_PACK(pack); WT_DECL_PACK_VALUE(pv); WT_DECL_PACK_VALUE(vpv); WT_PACK vpack; u_long arg; char *proj; uint8_t *end, *p; const uint8_t *next, *vp, *vend; size_t len, offset, old_len; bool skip; p = end = NULL; /* -Wuninitialized */ WT_RET(__pack_init(session, &vpack, vformat)); vp = value->data; vend = vp + value->size; /* Reset any of the buffers we will be setting. */ for (proj = (char *)proj_arg; *proj != '\0'; proj++) { arg = strtoul(proj, &proj, 10); if (*proj == WT_PROJ_KEY) { c = cp[arg]; WT_RET(__wt_buf_init(session, &c->key, 0)); } else if (*proj == WT_PROJ_VALUE && !key_only) { c = cp[arg]; WT_RET(__wt_buf_init(session, &c->value, 0)); } } skip = key_only; for (proj = (char *)proj_arg; *proj != '\0'; proj++) { arg = strtoul(proj, &proj, 10); switch (*proj) { case WT_PROJ_KEY: skip = false; c = cp[arg]; if (WT_CURSOR_RECNO(c)) { c->key.data = &c->recno; c->key.size = sizeof(c->recno); WT_RET(__pack_init(session, &pack, "R")); } else WT_RET(__pack_init( session, &pack, c->key_format)); buf = &c->key; p = (uint8_t *)buf->data; end = p + buf->size; continue; case WT_PROJ_VALUE: skip = key_only; if (skip) continue; c = cp[arg]; WT_RET(__pack_init(session, &pack, c->value_format)); buf = &c->value; p = (uint8_t *)buf->data; end = p + buf->size; continue; } /* We have to get a key or value before any operations. */ WT_ASSERT(session, skip || buf != NULL); /* * Otherwise, the argument is a count, where a missing * count means a count of 1. */ for (arg = (arg == 0) ? 1 : arg; arg > 0; arg--) { switch (*proj) { case WT_PROJ_SKIP: if (skip) break; WT_RET(__pack_next(&pack, &pv)); /* * A nasty case: if we are inserting * out-of-order, append a zero value to keep * the buffer in the correct format. */ if (p == end) { /* Set up an empty value. */ WT_CLEAR(pv.u); if (pv.type == 'S' || pv.type == 's') pv.u.s = ""; WT_RET(__pack_size(session, &pv, &len)); WT_RET(__wt_buf_grow(session, buf, buf->size + len)); p = (uint8_t *)buf->data + buf->size; WT_RET(__pack_write( session, &pv, &p, len)); end = p; buf->size += len; } else WT_RET(__unpack_read(session, &pv, (const uint8_t **)&p, (size_t)(end - p))); break; case WT_PROJ_NEXT: WT_RET(__pack_next(&vpack, &vpv)); WT_RET(__unpack_read(session, &vpv, &vp, (size_t)(vend - vp))); /* FALLTHROUGH */ case WT_PROJ_REUSE: if (skip) break; /* * Read the item we're about to overwrite. * * There is subtlety here: the value format * may not exactly match the cursor's format. * In particular, we need lengths with raw * columns in the middle of a packed struct, * but not if they are at the end of a struct. */ WT_RET(__pack_next(&pack, &pv)); next = p; if (p < end) WT_RET(__unpack_read(session, &pv, &next, (size_t)(end - p))); old_len = (size_t)(next - p); /* Make sure the types are compatible. */ WT_ASSERT(session, __wt_tolower((u_char)pv.type) == __wt_tolower((u_char)vpv.type)); pv.u = vpv.u; WT_RET(__pack_size(session, &pv, &len)); offset = WT_PTRDIFF(p, buf->data); /* * Avoid growing the buffer if the value fits. * This is not just a performance issue: it * covers the case of record number keys, which * have to be written to cursor->recno. */ if (len > old_len) WT_RET(__wt_buf_grow(session, buf, buf->size + len - old_len)); p = (uint8_t *)buf->data + offset; /* Make room if we're inserting out-of-order. */ if (offset + old_len < buf->size) memmove(p + len, p + old_len, buf->size - (offset + old_len)); WT_RET(__pack_write(session, &pv, &p, len)); buf->size += len - old_len; end = (uint8_t *)buf->data + buf->size; break; default: WT_RET_MSG(session, EINVAL, "unexpected projection plan: %c", (int)*proj); } } } return (0); }
/* * __wt_schema_project_merge -- * Given list of cursors and a projection, build a buffer containing the * column values read from the cursors. */ int __wt_schema_project_merge(WT_SESSION_IMPL *session, WT_CURSOR **cp, const char *proj_arg, const char *vformat, WT_ITEM *value) { WT_CURSOR *c; WT_DECL_PACK(pack); WT_DECL_PACK_VALUE(pv); WT_DECL_PACK_VALUE(vpv); WT_ITEM *buf; WT_PACK vpack; u_long arg; char *proj; const uint8_t *p, *end; uint8_t *vp; size_t len; p = end = NULL; /* -Wuninitialized */ WT_RET(__wt_buf_init(session, value, 0)); WT_RET(__pack_init(session, &vpack, vformat)); for (proj = (char *)proj_arg; *proj != '\0'; proj++) { arg = strtoul(proj, &proj, 10); switch (*proj) { case WT_PROJ_KEY: c = cp[arg]; if (WT_CURSOR_RECNO(c)) { c->key.data = &c->recno; c->key.size = sizeof(c->recno); WT_RET(__pack_init(session, &pack, "R")); } else WT_RET(__pack_init( session, &pack, c->key_format)); buf = &c->key; p = buf->data; end = p + buf->size; continue; case WT_PROJ_VALUE: c = cp[arg]; WT_RET(__pack_init(session, &pack, c->value_format)); buf = &c->value; p = buf->data; end = p + buf->size; continue; } /* * Otherwise, the argument is a count, where a missing * count means a count of 1. */ for (arg = (arg == 0) ? 1 : arg; arg > 0; arg--) { switch (*proj) { case WT_PROJ_NEXT: case WT_PROJ_SKIP: case WT_PROJ_REUSE: WT_RET(__pack_next(&pack, &pv)); WT_RET(__unpack_read(session, &pv, &p, (size_t)(end - p))); /* Only copy the value out once. */ if (*proj != WT_PROJ_NEXT) break; WT_RET(__pack_next(&vpack, &vpv)); /* Make sure the types are compatible. */ WT_ASSERT(session, __wt_tolower((u_char)pv.type) == __wt_tolower((u_char)vpv.type)); vpv.u = pv.u; WT_RET(__pack_size(session, &vpv, &len)); WT_RET(__wt_buf_grow(session, value, value->size + len)); vp = (uint8_t *)value->mem + value->size; WT_RET(__pack_write(session, &vpv, &vp, len)); value->size += len; break; } } } return (0); }
/* * __wt_schema_project_out -- * Given list of cursors and a projection, read columns from the * dependent cursors and return them to the application. */ int __wt_schema_project_out(WT_SESSION_IMPL *session, WT_CURSOR **cp, const char *proj_arg, va_list ap) { WT_CURSOR *c; WT_DECL_PACK(pack); WT_DECL_PACK_VALUE(pv); u_long arg; char *proj; uint8_t *p, *end; p = end = NULL; /* -Wuninitialized */ for (proj = (char *)proj_arg; *proj != '\0'; proj++) { arg = strtoul(proj, &proj, 10); switch (*proj) { case WT_PROJ_KEY: c = cp[arg]; if (WT_CURSOR_RECNO(c)) { c->key.data = &c->recno; c->key.size = sizeof(c->recno); WT_RET(__pack_init(session, &pack, "R")); } else WT_RET(__pack_init( session, &pack, c->key_format)); p = (uint8_t *)c->key.data; end = p + c->key.size; continue; case WT_PROJ_VALUE: c = cp[arg]; WT_RET(__pack_init(session, &pack, c->value_format)); p = (uint8_t *)c->value.data; end = p + c->value.size; continue; } /* * Otherwise, the argument is a count, where a missing * count means a count of 1. */ for (arg = (arg == 0) ? 1 : arg; arg > 0; arg--) { switch (*proj) { case WT_PROJ_NEXT: case WT_PROJ_SKIP: case WT_PROJ_REUSE: WT_RET(__pack_next(&pack, &pv)); WT_RET(__unpack_read(session, &pv, (const uint8_t **)&p, (size_t)(end - p))); /* Only copy the value out once. */ if (*proj != WT_PROJ_NEXT) break; WT_UNPACK_PUT(session, pv, ap); break; } } } return (0); }
/* * __wt_schema_project_in -- * Given list of cursors and a projection, read columns from the * application into the dependent cursors. */ int __wt_schema_project_in(WT_SESSION_IMPL *session, WT_CURSOR **cp, const char *proj_arg, va_list ap) { WT_CURSOR *c; WT_DECL_ITEM(buf); WT_DECL_PACK(pack); WT_DECL_PACK_VALUE(pv); WT_PACK_VALUE old_pv; size_t len, offset, old_len; u_long arg; char *proj; uint8_t *p, *end; const uint8_t *next; p = end = NULL; /* -Wuninitialized */ /* Reset any of the buffers we will be setting. */ for (proj = (char *)proj_arg; *proj != '\0'; proj++) { arg = strtoul(proj, &proj, 10); if (*proj == WT_PROJ_KEY) { c = cp[arg]; WT_RET(__wt_buf_init(session, &c->key, 0)); } else if (*proj == WT_PROJ_VALUE) { c = cp[arg]; WT_RET(__wt_buf_init(session, &c->value, 0)); } } for (proj = (char *)proj_arg; *proj != '\0'; proj++) { arg = strtoul(proj, &proj, 10); switch (*proj) { case WT_PROJ_KEY: c = cp[arg]; if (WT_CURSOR_RECNO(c)) { c->key.data = &c->recno; c->key.size = sizeof(c->recno); WT_RET(__pack_init(session, &pack, "R")); } else WT_RET(__pack_init( session, &pack, c->key_format)); buf = &c->key; p = (uint8_t *)buf->data; end = p + buf->size; continue; case WT_PROJ_VALUE: c = cp[arg]; WT_RET(__pack_init(session, &pack, c->value_format)); buf = &c->value; p = (uint8_t *)buf->data; end = p + buf->size; continue; } /* We have to get a key or value before any operations. */ WT_ASSERT(session, buf != NULL); /* * Otherwise, the argument is a count, where a missing * count means a count of 1. */ for (arg = (arg == 0) ? 1 : arg; arg > 0; arg--) { switch (*proj) { case WT_PROJ_SKIP: WT_RET(__pack_next(&pack, &pv)); /* * A nasty case: if we are inserting * out-of-order, we may reach the end of the * data. That's okay: we want to append in * that case, and we're positioned to do that. */ if (p == end) { /* Set up an empty value. */ WT_CLEAR(pv.u); if (pv.type == 'S' || pv.type == 's') pv.u.s = ""; WT_RET(__pack_size(session, &pv, &len)); WT_RET(__wt_buf_grow(session, buf, buf->size + len)); p = (uint8_t *)buf->mem + buf->size; WT_RET(__pack_write( session, &pv, &p, len)); buf->size += len; end = (uint8_t *)buf->mem + buf->size; } else if (*proj == WT_PROJ_SKIP) WT_RET(__unpack_read(session, &pv, (const uint8_t **)&p, (size_t)(end - p))); break; case WT_PROJ_NEXT: WT_RET(__pack_next(&pack, &pv)); WT_PACK_GET(session, pv, ap); /* FALLTHROUGH */ case WT_PROJ_REUSE: /* Read the item we're about to overwrite. */ next = p; if (p < end) { old_pv = pv; WT_RET(__unpack_read(session, &old_pv, &next, (size_t)(end - p))); } old_len = (size_t)(next - p); WT_RET(__pack_size(session, &pv, &len)); offset = WT_PTRDIFF(p, buf->mem); WT_RET(__wt_buf_grow(session, buf, buf->size + len)); p = (uint8_t *)buf->mem + offset; end = (uint8_t *)buf->mem + buf->size + len; /* Make room if we're inserting out-of-order. */ if (offset + old_len < buf->size) memmove(p + len, p + old_len, buf->size - (offset + old_len)); WT_RET(__pack_write(session, &pv, &p, len)); buf->size += len; break; default: WT_RET_MSG(session, EINVAL, "unexpected projection plan: %c", (int)*proj); } } } return (0); }
/* * __wt_curindex_open -- * WT_SESSION->open_cursor method for index cursors. */ int __wt_curindex_open(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *owner, const char *cfg[], WT_CURSOR **cursorp) { WT_CURSOR_STATIC_INIT(iface, __wt_cursor_get_key, /* get-key */ __curindex_get_value, /* get-value */ __wt_cursor_set_key, /* set-key */ __curindex_set_value, /* set-value */ __curindex_compare, /* compare */ __wt_cursor_equals, /* equals */ __curindex_next, /* next */ __curindex_prev, /* prev */ __curindex_reset, /* reset */ __curindex_search, /* search */ __curindex_search_near, /* search-near */ __wt_cursor_notsup, /* insert */ __wt_cursor_notsup, /* update */ __wt_cursor_notsup, /* remove */ __wt_cursor_reconfigure_notsup, /* reconfigure */ __curindex_close); /* close */ WT_CURSOR_INDEX *cindex; WT_CURSOR *cursor; WT_DECL_ITEM(tmp); WT_DECL_RET; WT_INDEX *idx; WT_TABLE *table; const char *columns, *idxname, *tablename; size_t namesize; tablename = uri; if (!WT_PREFIX_SKIP(tablename, "index:") || (idxname = strchr(tablename, ':')) == NULL) WT_RET_MSG(session, EINVAL, "Invalid cursor URI: '%s'", uri); namesize = (size_t)(idxname - tablename); ++idxname; if ((ret = __wt_schema_get_table(session, tablename, namesize, false, &table)) != 0) { if (ret == WT_NOTFOUND) WT_RET_MSG(session, EINVAL, "Cannot open cursor '%s' on unknown table", uri); return (ret); } columns = strchr(idxname, '('); if (columns == NULL) namesize = strlen(idxname); else namesize = (size_t)(columns - idxname); if ((ret = __wt_schema_open_index( session, table, idxname, namesize, &idx)) != 0) { __wt_schema_release_table(session, table); return (ret); } WT_RET(__wt_calloc_one(session, &cindex)); cursor = &cindex->iface; *cursor = iface; cursor->session = &session->iface; cindex->table = table; cindex->index = idx; cindex->key_plan = idx->key_plan; cindex->value_plan = idx->value_plan; cursor->internal_uri = idx->name; cursor->key_format = idx->idxkey_format; cursor->value_format = table->value_format; /* * XXX * A very odd corner case is an index with a recno key. * The only way to get here is by creating an index on a column store * using only the primary's recno as the index key. Disallow that for * now. */ if (WT_CURSOR_RECNO(cursor)) WT_ERR_MSG(session, WT_ERROR, "Column store indexes based on a record number primary " "key are not supported"); /* Handle projections. */ if (columns != NULL) { WT_ERR(__wt_scr_alloc(session, 0, &tmp)); WT_ERR(__wt_struct_reformat(session, table, columns, strlen(columns), NULL, false, tmp)); WT_ERR(__wt_strndup( session, tmp->data, tmp->size, &cursor->value_format)); WT_ERR(__wt_buf_init(session, tmp, 0)); WT_ERR(__wt_struct_plan(session, table, columns, strlen(columns), false, tmp)); WT_ERR(__wt_strndup( session, tmp->data, tmp->size, &cindex->value_plan)); } WT_ERR(__wt_cursor_init( cursor, cursor->internal_uri, owner, cfg, cursorp)); WT_ERR(__wt_open_cursor( session, idx->source, cursor, cfg, &cindex->child)); /* Open the column groups needed for this index cursor. */ WT_ERR(__curindex_open_colgroups(session, cindex, cfg)); if (F_ISSET(cursor, WT_CURSTD_DUMP_JSON)) __wt_json_column_init( cursor, table->key_format, &idx->colconf, &table->colconf); if (0) { err: WT_TRET(__curindex_close(cursor)); *cursorp = NULL; } __wt_scr_free(session, &tmp); return (ret); }
/* * __wt_schema_project_merge -- * Given list of cursors and a projection, build a buffer containing the * column values read from the cursors. */ int __wt_schema_project_merge(WT_SESSION_IMPL *session, WT_CURSOR **cp, const char *proj_arg, const char *vformat, WT_ITEM *value) { WT_CURSOR *c; WT_ITEM *buf; WT_PACK pack, vpack; WT_PACK_VALUE pv, vpv; char *proj; uint8_t *p, *end, *vp; size_t len; uint32_t arg; WT_CLEAR(pack); /* -Wuninitialized */ WT_CLEAR(pv); /* -Wuninitialized */ WT_CLEAR(vpv); /* -Wuninitialized */ p = end = NULL; /* -Wuninitialized */ WT_RET(__wt_buf_init(session, value, 0)); WT_RET(__pack_init(session, &vpack, vformat)); for (proj = (char *)proj_arg; *proj != '\0'; proj++) { arg = (uint32_t)strtoul(proj, &proj, 10); switch (*proj) { case WT_PROJ_KEY: c = cp[arg]; if (WT_CURSOR_RECNO(c)) { c->key.data = &c->recno; c->key.size = sizeof(c->recno); WT_RET(__pack_init(session, &pack, "R")); } else WT_RET(__pack_init( session, &pack, c->key_format)); buf = &c->key; p = (uint8_t *)buf->data; end = p + buf->size; continue; case WT_PROJ_VALUE: c = cp[arg]; WT_RET(__pack_init(session, &pack, c->value_format)); buf = &c->value; p = (uint8_t *)buf->data; end = p + buf->size; continue; } /* * Otherwise, the argument is a count, where a missing * count means a count of 1. */ for (arg = (arg == 0) ? 1 : arg; arg > 0; arg--) { switch (*proj) { case WT_PROJ_NEXT: case WT_PROJ_SKIP: WT_RET(__pack_next(&pack, &pv)); WT_RET(__unpack_read(session, &pv, (const uint8_t **)&p, (size_t)(end - p))); if (*proj == WT_PROJ_SKIP) break; WT_RET(__pack_next(&vpack, &vpv)); vpv.u = pv.u; len = __pack_size(session, &vpv); WT_RET(__wt_buf_grow(session, value, value->size + len)); vp = (uint8_t *)value->data + value->size; WT_RET(__pack_write(session, &vpv, &vp, len)); value->size += WT_STORE_SIZE(len); /* FALLTHROUGH */ case WT_PROJ_REUSE: /* Don't copy the same value twice. */ break; } } } return (0); }
/* * __curfile_create -- * Open a cursor for a given btree handle. */ static int __curfile_create(WT_SESSION_IMPL *session, WT_CURSOR *owner, const char *cfg[], bool bulk, bool bitmap, WT_CURSOR **cursorp) { WT_CURSOR_STATIC_INIT(iface, __wt_cursor_get_key, /* get-key */ __wt_cursor_get_value, /* get-value */ __wt_cursor_set_key, /* set-key */ __wt_cursor_set_value, /* set-value */ __curfile_compare, /* compare */ __curfile_equals, /* equals */ __curfile_next, /* next */ __curfile_prev, /* prev */ __curfile_reset, /* reset */ __curfile_search, /* search */ __curfile_search_near, /* search-near */ __curfile_insert, /* insert */ __wt_cursor_modify_notsup, /* modify */ __curfile_update, /* update */ __curfile_remove, /* remove */ __curfile_reserve, /* reserve */ __wt_cursor_reconfigure, /* reconfigure */ __curfile_close); /* close */ WT_BTREE *btree; WT_CONFIG_ITEM cval; WT_CURSOR *cursor; WT_CURSOR_BTREE *cbt; WT_CURSOR_BULK *cbulk; WT_DECL_RET; size_t csize; WT_STATIC_ASSERT(offsetof(WT_CURSOR_BTREE, iface) == 0); cbt = NULL; btree = S2BT(session); WT_ASSERT(session, btree != NULL); csize = bulk ? sizeof(WT_CURSOR_BULK) : sizeof(WT_CURSOR_BTREE); WT_RET(__wt_calloc(session, 1, csize, &cbt)); cursor = &cbt->iface; *cursor = iface; cursor->session = &session->iface; cursor->internal_uri = btree->dhandle->name; cursor->key_format = btree->key_format; cursor->value_format = btree->value_format; cbt->btree = btree; /* * Increment the data-source's in-use counter; done now because closing * the cursor will decrement it, and all failure paths from here close * the cursor. */ __wt_cursor_dhandle_incr_use(session); if (session->dhandle->checkpoint != NULL) F_SET(cbt, WT_CBT_NO_TXN); if (bulk) { F_SET(cursor, WT_CURSTD_BULK); cbulk = (WT_CURSOR_BULK *)cbt; /* Optionally skip the validation of each bulk-loaded key. */ WT_ERR(__wt_config_gets_def( session, cfg, "skip_sort_check", 0, &cval)); WT_ERR(__wt_curbulk_init( session, cbulk, bitmap, cval.val == 0 ? 0 : 1)); } /* * Random retrieval, row-store only. * Random retrieval cursors support a limited set of methods. */ WT_ERR(__wt_config_gets_def(session, cfg, "next_random", 0, &cval)); if (cval.val != 0) { if (WT_CURSOR_RECNO(cursor)) WT_ERR_MSG(session, ENOTSUP, "next_random configuration not supported for " "column-store objects"); __wt_cursor_set_notsup(cursor); cursor->next = __wt_curfile_next_random; cursor->reset = __curfile_reset; WT_ERR(__wt_config_gets_def( session, cfg, "next_random_sample_size", 0, &cval)); if (cval.val != 0) cbt->next_random_sample_size = (u_int)cval.val; } /* Underlying btree initialization. */ __wt_btcur_open(cbt); /* * WT_CURSOR.modify supported on 'u' value formats, but the fast-path * through the btree code requires log file format changes, it's not * available in all versions. */ if (WT_STREQ(cursor->value_format, "u") && S2C(session)->compat_major >= WT_LOG_V2) cursor->modify = __curfile_modify; WT_ERR(__wt_cursor_init( cursor, cursor->internal_uri, owner, cfg, cursorp)); WT_STAT_CONN_INCR(session, cursor_create); WT_STAT_DATA_INCR(session, cursor_create); if (0) { err: /* * Our caller expects to release the data handle if we fail. * Disconnect it from the cursor before closing. */ if (session->dhandle != NULL) __wt_cursor_dhandle_decr_use(session); cbt->btree = NULL; WT_TRET(__curfile_close(cursor)); *cursorp = NULL; } return (ret); }