1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 2013 Oracle and/or its affiliates.  All rights reserved.
5  *
6  * $Id$
7  */
8 
9 #include "db_config.h"
10 
11 #include "db_int.h"
12 #include "dbinc/db_page.h"
13 #include "dbinc/db_am.h"
14 #include "dbinc/heap.h"
15 
16 /*
17  * __db_ret --
18  *	Build return DBT.
19  *
20  * PUBLIC: int __db_ret __P((DBC *,
21  * PUBLIC:    PAGE *, u_int32_t, DBT *, void **, u_int32_t *));
22  */
23 int
__db_ret(dbc,h,indx,dbt,memp,memsize)24 __db_ret(dbc, h, indx, dbt, memp, memsize)
25 	DBC *dbc;
26 	PAGE *h;
27 	u_int32_t indx;
28 	DBT *dbt;
29 	void **memp;
30 	u_int32_t *memsize;
31 {
32 	BKEYDATA *bk;
33 	BOVERFLOW *bo;
34 	DB *dbp;
35 	HEAPHDR *hdr;
36 	HOFFPAGE ho;
37 	u_int32_t len;
38 	u_int8_t *hk;
39 	void *data;
40 
41 	if (F_ISSET(dbt, DB_DBT_READONLY))
42 		return (0);
43 	dbp = dbc->dbp;
44 
45 	switch (TYPE(h)) {
46 	case P_HASH_UNSORTED:
47 	case P_HASH:
48 		hk = P_ENTRY(dbp, h, indx);
49 		if (HPAGE_PTYPE(hk) == H_OFFPAGE) {
50 			memcpy(&ho, hk, sizeof(HOFFPAGE));
51 			return (__db_goff(dbc, dbt,
52 			    ho.tlen, ho.pgno, memp, memsize));
53 		}
54 		len = LEN_HKEYDATA(dbp, h, dbp->pgsize, indx);
55 		data = HKEYDATA_DATA(hk);
56 		break;
57 	case P_HEAP:
58 		hdr = (HEAPHDR *)P_ENTRY(dbp, h, indx);
59 		if (F_ISSET(hdr,(HEAP_RECSPLIT | HEAP_RECFIRST)))
60 			return (__heapc_gsplit(dbc, dbt, memp, memsize));
61 		len = hdr->size;
62 		data = (u_int8_t *)hdr + sizeof(HEAPHDR);
63 		break;
64 	case P_LBTREE:
65 	case P_LDUP:
66 	case P_LRECNO:
67 		bk = GET_BKEYDATA(dbp, h, indx);
68 		if (B_TYPE(bk->type) == B_OVERFLOW) {
69 			bo = (BOVERFLOW *)bk;
70 			return (__db_goff(dbc, dbt,
71 			    bo->tlen, bo->pgno, memp, memsize));
72 		}
73 		len = bk->len;
74 		data = bk->data;
75 		break;
76 	default:
77 		return (__db_pgfmt(dbp->env, h->pgno));
78 	}
79 
80 	return (__db_retcopy(dbp->env, dbt, data, len, memp, memsize));
81 }
82 
83 /*
84  * __db_retcopy --
85  *	Copy the returned data into the user's DBT, handling special flags.
86  *
87  * PUBLIC: int __db_retcopy __P((ENV *, DBT *,
88  * PUBLIC:    void *, u_int32_t, void **, u_int32_t *));
89  */
90 int
__db_retcopy(env,dbt,data,len,memp,memsize)91 __db_retcopy(env, dbt, data, len, memp, memsize)
92 	ENV *env;
93 	DBT *dbt;
94 	void *data;
95 	u_int32_t len;
96 	void **memp;
97 	u_int32_t *memsize;
98 {
99 	int ret;
100 
101 	if (F_ISSET(dbt, DB_DBT_READONLY))
102 		return (0);
103 	ret = 0;
104 
105 	/* If returning a partial record, reset the length. */
106 	if (F_ISSET(dbt, DB_DBT_PARTIAL)) {
107 		data = (u_int8_t *)data + dbt->doff;
108 		if (len > dbt->doff) {
109 			len -= dbt->doff;
110 			if (len > dbt->dlen)
111 				len = dbt->dlen;
112 		} else
113 			len = 0;
114 	}
115 
116 	/*
117 	 * Allocate memory to be owned by the application: DB_DBT_MALLOC,
118 	 * DB_DBT_REALLOC.
119 	 *
120 	 * !!!
121 	 * We always allocate memory, even if we're copying out 0 bytes. This
122 	 * guarantees consistency, i.e., the application can always free memory
123 	 * without concern as to how many bytes of the record were requested.
124 	 *
125 	 * Use the memory specified by the application: DB_DBT_USERMEM.
126 	 *
127 	 * !!!
128 	 * If the length we're going to copy is 0, the application-supplied
129 	 * memory pointer is allowed to be NULL.
130 	 */
131 	if (F_ISSET(dbt, DB_DBT_USERCOPY)) {
132 		dbt->size = len;
133 		return (len == 0 ? 0 : env->dbt_usercopy(dbt, 0, data,
134 		    len, DB_USERCOPY_SETDATA));
135 
136 	} else if (F_ISSET(dbt, DB_DBT_MALLOC))
137 		ret = __os_umalloc(env, len, &dbt->data);
138 	else if (F_ISSET(dbt, DB_DBT_REALLOC)) {
139 		if (dbt->data == NULL || dbt->size == 0 || dbt->size < len)
140 			ret = __os_urealloc(env, len, &dbt->data);
141 	} else if (F_ISSET(dbt, DB_DBT_USERMEM)) {
142 		if (len != 0 && (dbt->data == NULL || dbt->ulen < len))
143 			ret = DB_BUFFER_SMALL;
144 	} else if (memp == NULL || memsize == NULL)
145 		ret = EINVAL;
146 	else {
147 		if (len != 0 && (*memsize == 0 || *memsize < len)) {
148 			if ((ret = __os_realloc(env, len, memp)) == 0)
149 				*memsize = len;
150 			else
151 				*memsize = 0;
152 		}
153 		if (ret == 0)
154 			dbt->data = *memp;
155 	}
156 
157 	if (ret == 0 && len != 0)
158 		memcpy(dbt->data, data, len);
159 
160 	/*
161 	 * Return the length of the returned record in the DBT size field.
162 	 * This satisfies the requirement that if we're using user memory
163 	 * and insufficient memory was provided, return the amount necessary
164 	 * in the size field.
165 	 */
166 	dbt->size = len;
167 
168 	return (ret);
169 }
170