1 /*-
2  * Copyright (c) 1996, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file LICENSE for license information.
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/mp.h"
14 #include "dbinc/log.h"
15 #include "dbinc/db_am.h"
16 #include "dbinc/txn.h"
17 
18 /*
19  * __db_ditem_nolog --
20  *	Remove an item from a page without affecting its recoverability.
21  *
22  * PUBLIC:  int __db_ditem_nolog __P((DBC *, PAGE *, u_int32_t, u_int32_t));
23  */
24 int
__db_ditem_nolog(dbc,pagep,indx,nbytes)25 __db_ditem_nolog(dbc, pagep, indx, nbytes)
26 	DBC *dbc;
27 	PAGE *pagep;
28 	u_int32_t indx, nbytes;
29 {
30 	DB *dbp;
31 	db_indx_t cnt, *inp, offset;
32 	u_int8_t *from;
33 
34 	dbp = dbc->dbp;
35 	DB_ASSERT(dbp->env, IS_DIRTY(pagep));
36 	DB_ASSERT(dbp->env, indx < NUM_ENT(pagep));
37 
38 	/*
39 	 * If there's only a single item on the page, we don't have to
40 	 * work hard.
41 	 */
42 	if (NUM_ENT(pagep) == 1) {
43 		NUM_ENT(pagep) = 0;
44 		HOFFSET(pagep) = dbp->pgsize;
45 		return (0);
46 	}
47 
48 	inp = P_INP(dbp, pagep);
49 	/*
50 	 * Pack the remaining key/data items at the end of the page.  Use
51 	 * memmove(3), the regions may overlap.
52 	 */
53 	from = (u_int8_t *)pagep + HOFFSET(pagep);
54 	DB_ASSERT(dbp->env, inp[indx] >= HOFFSET(pagep));
55 	memmove(from + nbytes, from, inp[indx] - HOFFSET(pagep));
56 	HOFFSET(pagep) += nbytes;
57 
58 	/* Adjust the indices' offsets. */
59 	offset = inp[indx];
60 	for (cnt = 0; cnt < NUM_ENT(pagep); ++cnt)
61 		if (inp[cnt] < offset)
62 			inp[cnt] += nbytes;
63 
64 	/* Shift the indices down. */
65 	--NUM_ENT(pagep);
66 	if (indx != NUM_ENT(pagep))
67 		memmove(&inp[indx], &inp[indx + 1],
68 		    sizeof(db_indx_t) * (NUM_ENT(pagep) - indx));
69 
70 	return (0);
71 }
72 
73 /*
74  * __db_ditem --
75  *	Remove an item from a page, logging it if enabled.
76  *
77  * PUBLIC:  int __db_ditem __P((DBC *, PAGE *, u_int32_t, u_int32_t));
78  */
79 int
__db_ditem(dbc,pagep,indx,nbytes)80 __db_ditem(dbc, pagep, indx, nbytes)
81 	DBC *dbc;
82 	PAGE *pagep;
83 	u_int32_t indx, nbytes;
84 {
85 	DB *dbp;
86 	DBT ldbt;
87 	int ret;
88 
89 	dbp = dbc->dbp;
90 
91 	if (DBC_LOGGING(dbc)) {
92 		ldbt.data = P_ENTRY(dbp, pagep, indx);
93 		ldbt.size = nbytes;
94 		if ((ret = __db_addrem_log(dbp, dbc->txn, &LSN(pagep), 0,
95 		    OP_SET(DB_REM_DUP, pagep), PGNO(pagep),
96 		    (u_int32_t)indx, nbytes, &ldbt, NULL, &LSN(pagep))) != 0)
97 			return (ret);
98 	} else
99 		LSN_NOT_LOGGED(LSN(pagep));
100 
101 	return (__db_ditem_nolog(dbc, pagep, indx, nbytes));
102 }
103 
104 /*
105  * __db_pitem_nolog --
106  *	Put an item on a page without logging.
107  *
108  * PUBLIC: int __db_pitem_nolog
109  * PUBLIC:     __P((DBC *, PAGE *, u_int32_t, u_int32_t, DBT *, DBT *));
110  */
111 int
__db_pitem_nolog(dbc,pagep,indx,nbytes,hdr,data)112 __db_pitem_nolog(dbc, pagep, indx, nbytes, hdr, data)
113 	DBC *dbc;
114 	PAGE *pagep;
115 	u_int32_t indx;
116 	u_int32_t nbytes;
117 	DBT *hdr, *data;
118 {
119 	BKEYDATA bk;
120 	DB *dbp;
121 	DBT thdr;
122 	db_indx_t *inp;
123 	u_int8_t *p;
124 
125 	dbp = dbc->dbp;
126 
127 	DB_ASSERT(dbp->env, IS_DIRTY(pagep));
128 
129 	if (nbytes > P_FREESPACE(dbp, pagep)) {
130 		DB_ASSERT(dbp->env, nbytes <= P_FREESPACE(dbp, pagep));
131 		return (EINVAL);
132 	}
133 
134 	if (hdr == NULL) {
135 		B_TSET(bk.type, B_KEYDATA);
136 		bk.len = data == NULL ? 0 : data->size;
137 
138 		thdr.data = &bk;
139 		thdr.size = SSZA(BKEYDATA, data);
140 		hdr = &thdr;
141 	}
142 	inp = P_INP(dbp, pagep);
143 
144 	/* Adjust the index table, then put the item on the page. */
145 	if (indx != NUM_ENT(pagep))
146 		memmove(&inp[indx + 1], &inp[indx],
147 		    sizeof(db_indx_t) * (NUM_ENT(pagep) - indx));
148 	HOFFSET(pagep) -= nbytes;
149 	inp[indx] = HOFFSET(pagep);
150 	++NUM_ENT(pagep);
151 
152 	p = P_ENTRY(dbp, pagep, indx);
153 	memcpy(p, hdr->data, hdr->size);
154 	if (data != NULL)
155 		memcpy(p + hdr->size, data->data, data->size);
156 
157 	return (0);
158 }
159 
160 /*
161  * __db_pitem --
162  *	Put an item on a page.
163  *
164  * PUBLIC: int __db_pitem
165  * PUBLIC:     __P((DBC *, PAGE *, u_int32_t, u_int32_t, DBT *, DBT *));
166  */
167 int
__db_pitem(dbc,pagep,indx,nbytes,hdr,data)168 __db_pitem(dbc, pagep, indx, nbytes, hdr, data)
169 	DBC *dbc;
170 	PAGE *pagep;
171 	u_int32_t indx;
172 	u_int32_t nbytes;
173 	DBT *hdr, *data;
174 {
175 	DB *dbp;
176 	MPOOLFILE *mpf;
177 	int ret;
178 
179 	dbp = dbc->dbp;
180 	mpf = dbp->mpf->mfp;
181 	/*
182 	 * Put a single item onto a page.  The logic figuring out where to
183 	 * insert and whether it fits is handled in the caller.  All we do
184 	 * here is manage the page shuffling.  We cheat a little bit in that
185 	 * we don't want to copy the dbt on a normal put twice.  If hdr is
186 	 * NULL, we create a BKEYDATA structure on the page, otherwise, just
187 	 * copy the caller's information onto the page.
188 	 *
189 	 * This routine is also used to put entries onto the page where the
190 	 * entry is pre-built, e.g., during recovery.  In this case, the hdr
191 	 * will point to the entry, and the data argument will be NULL.
192 	 *
193 	 * If transactional bulk loading is enabled in this
194 	 * transaction, and the page is above the file's extension
195 	 * watermark, skip logging, but do not invoke LSN_NOT_LOGGED.
196 	 *
197 	 * !!!
198 	 * There's a tremendous potential for off-by-one errors here, since
199 	 * the passed in header sizes must be adjusted for the structure's
200 	 * placeholder for the trailing variable-length data field.
201 	 */
202 	if (DBC_LOGGING(dbc)) {
203 		if (__txn_pg_above_fe_watermark(dbc->txn, mpf, PGNO(pagep))) {
204 			mpf->fe_nlws++; /* Note that logging was skipped. */
205 		} else if ((ret = __db_addrem_log(dbp, dbc->txn, &LSN(pagep),
206 		    0, OP_SET(DB_ADD_DUP, pagep), PGNO(pagep),
207 		    (u_int32_t)indx, nbytes, hdr, data, &LSN(pagep)))) {
208 			return (ret);
209 		}
210 	} else
211 		LSN_NOT_LOGGED(LSN(pagep));
212 
213 	return (__db_pitem_nolog(dbc, pagep, indx, nbytes, hdr, data));
214 }
215