/* * __find_column_format -- * Find the format of the named column. */ static int __find_column_format(WT_SESSION_IMPL *session, WT_TABLE *table, WT_CONFIG_ITEM *colname, bool value_only, WT_PACK_VALUE *pv) { WT_CONFIG conf; WT_CONFIG_ITEM k, v; WT_DECL_RET; WT_PACK pack; bool inkey; __wt_config_subinit(session, &conf, &table->colconf); WT_RET(__pack_init(session, &pack, table->key_format)); inkey = true; while ((ret = __wt_config_next(&conf, &k, &v)) == 0) { if ((ret = __pack_next(&pack, pv)) == WT_NOTFOUND && inkey) { ret = __pack_init(session, &pack, table->value_format); if (ret == 0) ret = __pack_next(&pack, pv); inkey = false; } if (ret != 0) return (ret); if (k.len == colname->len && strncmp(colname->str, k.str, k.len) == 0) { if (value_only && inkey) return (__wt_set_return(session, EINVAL)); return (0); } } return (ret); }
/* * __wt_struct_packv -- * Pack a byte string (va_list version). */ int __wt_struct_packv(WT_SESSION_IMPL *session, void *buffer, size_t size, const char *fmt, va_list ap) { WT_DECL_RET; WT_PACK pack; WT_PACK_VALUE pv; uint8_t *p, *end; WT_CLEAR(pv); /* -Wuninitialized */ WT_RET(__pack_init(session, &pack, fmt)); p = buffer; end = p + size; while ((ret = __pack_next(&pack, &pv)) == 0) { WT_PACK_GET(session, pv, ap); WT_RET(__pack_write(session, &pv, &p, (size_t)(end - p))); } WT_ASSERT(session, p <= end); if (ret != WT_NOTFOUND) return (ret); return (0); }
/* * __wt_struct_unpackv -- * Unpack a byte string (va_list version). */ int __wt_struct_unpackv(WT_SESSION_IMPL *session, const void *buffer, size_t size, const char *fmt, va_list ap) { WT_DECL_RET; WT_PACK pack; WT_PACK_VALUE pv; const uint8_t *p, *end; WT_RET(__pack_init(session, &pack, fmt)); p = buffer; end = p + size; WT_CLEAR(pv.u.item); /* GCC 4.6 lint */ while ((ret = __pack_next(&pack, &pv)) == 0) { WT_RET(__unpack_read(session, &pv, &p, (size_t)(end - p))); WT_UNPACK_PUT(session, pv, ap); } WT_ASSERT(session, p <= end); if (ret != WT_NOTFOUND) return (ret); return (0); }
/* * wiredtiger_pack_str -- * Pack a string. */ int wiredtiger_pack_str(WT_PACK_STREAM *ps, const char *s) { WT_DECL_PACK_VALUE(pv); WT_SESSION_IMPL *session; session = ps->pack.session; /* Lower-level packing routines treat a length of zero as unchecked. */ if (ps->p >= ps->end) return (ENOMEM); WT_RET(__pack_next(&ps->pack, &pv)); switch (pv.type) { case 'S': case 's': pv.u.s = s; WT_RET(__pack_write( session, &pv, &ps->p, (size_t)(ps->end - ps->p))); break; WT_ILLEGAL_VALUE(session); } return (0); }
/* * wiredtiger_pack_item -- * Pack an item. */ int wiredtiger_pack_item(WT_PACK_STREAM *ps, WT_ITEM *item) { WT_DECL_PACK_VALUE(pv); WT_SESSION_IMPL *session; session = ps->pack.session; /* Lower-level packing routines treat a length of zero as unchecked. */ if (ps->p >= ps->end) return (ENOMEM); WT_RET(__pack_next(&ps->pack, &pv)); switch (pv.type) { case 'U': case 'u': pv.u.item.data = item->data; pv.u.item.size = item->size; WT_RET(__pack_write( session, &pv, &ps->p, (size_t)(ps->end - ps->p))); break; WT_ILLEGAL_VALUE(session); } return (0); }
/* * wiredtiger_unpack_uint -- * Unpack an unsigned integer. */ int wiredtiger_unpack_uint(WT_PACK_STREAM *ps, uint64_t *up) { WT_DECL_PACK_VALUE(pv); WT_SESSION_IMPL *session; session = ps->pack.session; /* Lower-level packing routines treat a length of zero as unchecked. */ if (ps->p >= ps->end) return (ENOMEM); WT_RET(__pack_next(&ps->pack, &pv)); switch (pv.type) { case 'B': case 'H': case 'I': case 'L': case 'Q': case 'R': case 'r': case 't': WT_RET(__unpack_read(session, &pv, (const uint8_t **)&ps->p, (size_t)(ps->end - ps->p))); *up = pv.u.u; break; WT_ILLEGAL_VALUE(session); } return (0); }
/* * __wt_struct_check -- * Check that the specified packing format is valid, and whether it fits * into a fixed-sized bitfield. */ int __wt_struct_check(WT_SESSION_IMPL *session, const char *fmt, size_t len, bool *fixedp, uint32_t *fixed_lenp) { WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_PACK pack; int fields; WT_RET(__pack_initn(session, &pack, fmt, len)); for (fields = 0; (ret = __pack_next(&pack, &pv)) == 0; fields++) ; if (ret != WT_NOTFOUND) return (ret); if (fixedp != NULL && fixed_lenp != NULL) { if (fields == 0) { *fixedp = 1; *fixed_lenp = 0; } else if (fields == 1 && pv.type == 't') { *fixedp = 1; *fixed_lenp = pv.size; } else *fixedp = 0; } return (0); }
/* * __wt_struct_unpack_size -- * Determine the packed size of a buffer matching the format. */ int __wt_struct_unpack_size(WT_SESSION_IMPL *session, const void *buffer, size_t size, const char *fmt, size_t *resultp) { WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_PACK pack; const uint8_t *p, *end; p = buffer; end = p + size; WT_RET(__pack_init(session, &pack, fmt)); while ((ret = __pack_next(&pack, &pv)) == 0) WT_RET(__unpack_read(session, &pv, &p, (size_t)(end - p))); /* Be paranoid - __pack_write should never overflow. */ WT_ASSERT(session, p <= end); if (ret != WT_NOTFOUND) return (ret); *resultp = WT_PTRDIFF(p, buffer); return (0); }
/* * __wt_struct_repack -- * Return the subset of the packed buffer that represents part of * the format. If the result is not contiguous in the existing * buffer, a buffer is reallocated and filled. */ int __wt_struct_repack(WT_SESSION_IMPL *session, const char *infmt, const char *outfmt, const WT_ITEM *inbuf, WT_ITEM *outbuf) { WT_DECL_PACK_VALUE(pvin); WT_DECL_PACK_VALUE(pvout); WT_DECL_RET; WT_PACK packin, packout; const uint8_t *before, *end, *p; const void *start; start = NULL; p = inbuf->data; end = p + inbuf->size; WT_RET(__pack_init(session, &packout, outfmt)); WT_RET(__pack_init(session, &packin, infmt)); /* Outfmt should complete before infmt */ while ((ret = __pack_next(&packout, &pvout)) == 0) { if (p >= end) WT_RET(EINVAL); if (pvout.type == 'x' && pvout.size == 0 && pvout.havesize) continue; WT_RET(__pack_next(&packin, &pvin)); before = p; WT_RET(__unpack_read(session, &pvin, &p, (size_t)(end - p))); if (pvout.type != pvin.type) WT_RET(ENOTSUP); if (start == NULL) start = before; } WT_RET_NOTFOUND_OK(ret); /* Be paranoid - __pack_write should never overflow. */ WT_ASSERT(session, p <= end); outbuf->data = start; outbuf->size = WT_PTRDIFF(p, start); return (0); }
/* * __wt_schema_colcheck -- * Check that a list of columns matches a (key,value) format pair. */ int __wt_schema_colcheck(WT_SESSION_IMPL *session, const char *key_format, const char *value_format, WT_CONFIG_ITEM *colconf, u_int *kcolsp, u_int *vcolsp) { WT_CONFIG conf; WT_CONFIG_ITEM k, v; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_PACK pack; u_int kcols, ncols, vcols; WT_RET(__pack_init(session, &pack, key_format)); for (kcols = 0; (ret = __pack_next(&pack, &pv)) == 0; kcols++) ; WT_RET_NOTFOUND_OK(ret); WT_RET(__pack_init(session, &pack, value_format)); for (vcols = 0; (ret = __pack_next(&pack, &pv)) == 0; vcols++) ; WT_RET_TEST(ret != WT_NOTFOUND, ret); /* Walk through the named columns. */ __wt_config_subinit(session, &conf, colconf); for (ncols = 0; (ret = __wt_config_next(&conf, &k, &v)) == 0; ncols++) ; WT_RET_TEST(ret != WT_NOTFOUND, ret); if (ncols != 0 && ncols != kcols + vcols) WT_RET_MSG(session, EINVAL, "Number of columns in '%.*s' " "does not match key format '%s' plus value format '%s'", (int)colconf->len, colconf->str, key_format, value_format); if (kcolsp != NULL) *kcolsp = kcols; if (vcolsp != NULL) *vcolsp = vcols; return (0); }
/* * __json_struct_unpackv -- * Unpack a byte string to JSON (va_list version). */ static inline int __json_struct_unpackv(WT_SESSION_IMPL *session, const void *buffer, size_t size, const char *fmt, WT_CONFIG_ITEM *names, u_char *jbuf, size_t jbufsize, bool iskey, va_list ap) { WT_CONFIG_ITEM name; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_PACK pack; WT_PACK_NAME packname; size_t jsize; bool needcr; const uint8_t *p, *end; p = buffer; end = p + size; needcr = false; /* Unpacking a cursor marked as json implies a single arg. */ *va_arg(ap, const char **) = (char *)jbuf; WT_RET(__pack_name_init(session, names, iskey, &packname)); WT_RET(__pack_init(session, &pack, fmt)); while ((ret = __pack_next(&pack, &pv)) == 0) { if (needcr) { WT_ASSERT(session, jbufsize >= 3); strncat((char *)jbuf, ",\n", jbufsize); jbuf += 2; jbufsize -= 2; } needcr = true; WT_RET(__unpack_read(session, &pv, &p, (size_t)(end - p))); WT_RET(__pack_name_next(&packname, &name)); jsize = __json_unpack_put(session, (u_char *)&pv, jbuf, jbufsize, &name); WT_ASSERT(session, jsize <= jbufsize); jbuf += jsize; jbufsize -= jsize; } if (ret == WT_NOTFOUND) ret = 0; /* Be paranoid - __unpack_read should never overflow. */ WT_ASSERT(session, p <= end); WT_ASSERT(session, jbufsize == 1); return (ret); }
/* * __wt_struct_truncate -- * Return a packing string for the first N columns in a value. */ int __wt_struct_truncate(WT_SESSION_IMPL *session, const char *input_fmt, u_int ncols, WT_ITEM *format) { WT_DECL_PACK_VALUE(pv); WT_PACK pack; WT_RET(__pack_init(session, &pack, input_fmt)); while (ncols-- > 0) { WT_RET(__pack_next(&pack, &pv)); if (pv.havesize) WT_RET(__wt_buf_catfmt(session, format, "%" PRIu32 "%c", pv.size, pv.type)); else WT_RET(__wt_buf_catfmt(session, format, "%c", pv.type)); } return (0); }
/* * __wt_struct_sizev -- * Calculate the size of a packed byte string (va_list version). */ int __wt_struct_sizev( WT_SESSION_IMPL *session, size_t *sizep, const char *fmt, va_list ap) { WT_PACK pack; WT_PACK_VALUE pv; size_t total; WT_CLEAR(pv); /* -Wuninitialized */ WT_RET(__pack_init(session, &pack, fmt)); for (total = 0; __pack_next(&pack, &pv) == 0;) { WT_PACK_GET(session, pv, ap); total += __pack_size(session, &pv); } *sizep = total; return (0); }
/* * __wt_struct_truncate -- * Return a packing string for the first N columns in a value. */ int __wt_struct_truncate(WT_SESSION_IMPL *session, const char *input_fmt, u_int ncols, WT_ITEM *format) { WT_PACK pack; WT_PACK_VALUE pv; WT_CLEAR(pv); /* -Wuninitialized */ WT_RET(__pack_init(session, &pack, input_fmt)); while (ncols-- > 0) { WT_RET(__pack_next(&pack, &pv)); if (pv.havesize) WT_RET(__wt_buf_catfmt( session, format, "%d%c", (int)pv.size, pv.type)); else WT_RET(__wt_buf_catfmt(session, format, "%c", pv.type)); } return (0); }
/* * __json_struct_size -- * Calculate the size of a packed byte string as formatted for JSON. */ static inline int __json_struct_size(WT_SESSION_IMPL *session, const void *buffer, size_t size, const char *fmt, WT_CONFIG_ITEM *names, bool iskey, size_t *presult) { WT_CONFIG_ITEM name; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_PACK pack; WT_PACK_NAME packname; size_t result; bool needcr; const uint8_t *p, *end; p = buffer; end = p + size; result = 0; needcr = false; WT_RET(__pack_name_init(session, names, iskey, &packname)); WT_RET(__pack_init(session, &pack, fmt)); while ((ret = __pack_next(&pack, &pv)) == 0) { if (needcr) result += 2; needcr = true; WT_RET(__unpack_read(session, &pv, &p, (size_t)(end - p))); WT_RET(__pack_name_next(&packname, &name)); result += __json_unpack_put(session, &pv, NULL, 0, &name); } if (ret == WT_NOTFOUND) ret = 0; /* Be paranoid - __pack_write should never overflow. */ WT_ASSERT(session, p <= end); *presult = result; return (ret); }
/* * __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); }
/* * __create_index -- * Create an index. */ static int __create_index(WT_SESSION_IMPL *session, const char *name, int exclusive, const char *config) { WT_CONFIG kcols, pkcols; WT_CONFIG_ITEM ckey, cval, icols, kval; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_ITEM confbuf, extra_cols, fmt, namebuf; WT_PACK pack; WT_TABLE *table; const char *cfg[4] = { WT_CONFIG_BASE(session, index_meta), NULL, NULL, NULL }; const char *sourcecfg[] = { config, NULL, NULL }; const char *source, *sourceconf, *idxname, *tablename; char *idxconf; size_t tlen; int have_extractor; u_int i, npublic_cols; sourceconf = NULL; idxconf = NULL; WT_CLEAR(confbuf); WT_CLEAR(fmt); WT_CLEAR(extra_cols); WT_CLEAR(namebuf); have_extractor = 0; tablename = name; if (!WT_PREFIX_SKIP(tablename, "index:")) return (EINVAL); idxname = strchr(tablename, ':'); if (idxname == NULL) WT_RET_MSG(session, EINVAL, "Invalid index name, " "should be <table name>:<index name>: %s", name); tlen = (size_t)(idxname++ - tablename); if ((ret = __wt_schema_get_table(session, tablename, tlen, 1, &table)) != 0) WT_RET_MSG(session, ret, "Can't create an index for a non-existent table: %.*s", (int)tlen, tablename); if (table->is_simple) WT_RET_MSG(session, EINVAL, "%s requires a table with named columns", name); if (__wt_config_getones(session, config, "source", &cval) == 0) { WT_ERR(__wt_buf_fmt(session, &namebuf, "%.*s", (int)cval.len, cval.str)); source = namebuf.data; } else { WT_ERR(__wt_schema_index_source( session, table, idxname, config, &namebuf)); source = namebuf.data; /* Add the source name to the index config before collapsing. */ WT_ERR(__wt_buf_catfmt(session, &confbuf, ",source=\"%s\"", source)); } if (__wt_config_getones_none( session, config, "extractor", &cval) == 0 && cval.len != 0) { have_extractor = 1; /* Custom extractors must supply a key format. */ if ((ret = __wt_config_getones( session, config, "key_format", &kval)) != 0) WT_ERR_MSG(session, EINVAL, "%s: custom extractors require a key_format", name); } /* Calculate the key/value formats. */ WT_CLEAR(icols); if (__wt_config_getones(session, config, "columns", &icols) != 0 && !have_extractor) WT_ERR_MSG(session, EINVAL, "%s: requires 'columns' configuration", name); /* * Count the public columns using the declared columns for normal * indices or the key format for custom extractors. */ npublic_cols = 0; if (!have_extractor) { WT_ERR(__wt_config_subinit(session, &kcols, &icols)); while ((ret = __wt_config_next(&kcols, &ckey, &cval)) == 0) ++npublic_cols; WT_ERR_NOTFOUND_OK(ret); } else { WT_ERR(__pack_initn(session, &pack, kval.str, kval.len)); while ((ret = __pack_next(&pack, &pv)) == 0) ++npublic_cols; WT_ERR_NOTFOUND_OK(ret); } /* * The key format for an index is somewhat subtle: the application * specifies a set of columns that it will use for the key, but the * engine usually adds some hidden columns in order to derive the * primary key. These hidden columns are part of the source's * key_format, which we are calculating now, but not part of an index * cursor's key_format. */ WT_ERR(__wt_config_subinit(session, &pkcols, &table->colconf)); for (i = 0; i < table->nkey_columns && (ret = __wt_config_next(&pkcols, &ckey, &cval)) == 0; i++) { /* * If the primary key column is already in the secondary key, * don't add it again. */ if (__wt_config_subgetraw(session, &icols, &ckey, &cval) == 0) { if (have_extractor) WT_ERR_MSG(session, EINVAL, "an index with a custom extractor may not " "include primary key columns"); continue; } WT_ERR(__wt_buf_catfmt( session, &extra_cols, "%.*s,", (int)ckey.len, ckey.str)); } if (ret != 0 && ret != WT_NOTFOUND) goto err; /* Index values are empty: all columns are packed into the index key. */ WT_ERR(__wt_buf_fmt(session, &fmt, "value_format=,key_format=")); if (have_extractor) { WT_ERR(__wt_buf_catfmt(session, &fmt, "%.*s", (int)kval.len, kval.str)); WT_CLEAR(icols); } /* * Construct the index key format, or append the primary key columns * for custom extractors. */ WT_ERR(__wt_struct_reformat(session, table, icols.str, icols.len, (const char *)extra_cols.data, 0, &fmt)); /* Check for a record number index key, which makes no sense. */ WT_ERR(__wt_config_getones(session, fmt.data, "key_format", &cval)); if (cval.len == 1 && cval.str[0] == 'r') WT_ERR_MSG(session, EINVAL, "column-store index may not use the record number as its " "index key"); WT_ERR(__wt_buf_catfmt( session, &fmt, ",index_key_columns=%u", npublic_cols)); sourcecfg[1] = fmt.data; WT_ERR(__wt_config_merge(session, sourcecfg, NULL, &sourceconf)); WT_ERR(__wt_schema_create(session, source, sourceconf)); cfg[1] = sourceconf; cfg[2] = confbuf.data; WT_ERR(__wt_config_collapse(session, cfg, &idxconf)); if ((ret = __wt_metadata_insert(session, name, idxconf)) != 0) { /* * If the entry already exists in the metadata, we're done. * This is an error for exclusive creates but okay otherwise. */ if (ret == WT_DUPLICATE_KEY) ret = exclusive ? EEXIST : 0; goto err; } /* Make sure that the configuration is valid. */ WT_ERR(__wt_schema_open_index( session, table, idxname, strlen(idxname), NULL)); err: __wt_free(session, idxconf); __wt_free(session, sourceconf); __wt_buf_free(session, &confbuf); __wt_buf_free(session, &extra_cols); __wt_buf_free(session, &fmt); __wt_buf_free(session, &namebuf); __wt_schema_release_table(session, table); 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_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_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); }
/* * __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_struct_repack -- * Return the subset of the packed buffer that represents part of * the format. If the result is not contiguous in the existing * buffer, a buffer is reallocated and filled. */ int __wt_struct_repack(WT_SESSION_IMPL *session, const char *infmt, const char *outfmt, const WT_ITEM *inbuf, WT_ITEM *outbuf, void **reallocp) { WT_DECL_PACK_VALUE(pvin); WT_DECL_PACK_VALUE(pvout); WT_DECL_RET; WT_PACK packin, packout; const uint8_t *before, *end, *p; uint8_t *newbuf, *pout; size_t len; const void *start; start = newbuf = NULL; p = inbuf->data; end = p + inbuf->size; /* * Handle this non-contiguous case: 'U' -> 'u' at the end of the buf. * The former case has the size embedded before the item, the latter * does not. */ if ((len = strlen(outfmt)) > 1 && outfmt[len - 1] == 'u' && strlen(infmt) > len && infmt[len - 1] == 'U') { WT_ERR(__wt_realloc(session, NULL, inbuf->size, reallocp)); pout = *reallocp; } else pout = NULL; WT_ERR(__pack_init(session, &packout, outfmt)); WT_ERR(__pack_init(session, &packin, infmt)); /* Outfmt should complete before infmt */ while ((ret = __pack_next(&packout, &pvout)) == 0) { WT_ERR(__pack_next(&packin, &pvin)); before = p; WT_ERR(__unpack_read(session, &pvin, &p, (size_t)(end - p))); if (pvout.type != pvin.type) { if (pvout.type == 'u' && pvin.type == 'U') { /* Skip the prefixed size, we don't need it */ WT_ERR(__wt_struct_unpack_size(session, before, (size_t)(end - before), "I", &len)); before += len; } else WT_ERR(ENOTSUP); } if (pout != NULL) { memcpy(pout, before, WT_PTRDIFF(p, before)); pout += p - before; } else if (start == NULL) start = before; } WT_ERR_NOTFOUND_OK(ret); /* Be paranoid - __pack_write should never overflow. */ WT_ASSERT(session, p <= end); if (pout != NULL) { outbuf->data = *reallocp; outbuf->size = WT_PTRDIFF(pout, *reallocp); } else { outbuf->data = start; outbuf->size = WT_PTRDIFF(p, start); } err: return (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, 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_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); }