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