1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: hash_upgrade.c,v 12.15 2008/01/08 20:58:34 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/db_page.h"
13#include "dbinc/hash.h"
14#include "dbinc/db_upgrade.h"
15
16/*
17 * __ham_30_hashmeta --
18 *	Upgrade the database from version 4/5 to version 6.
19 *
20 * PUBLIC: int __ham_30_hashmeta __P((DB *, char *, u_int8_t *));
21 */
22int
23__ham_30_hashmeta(dbp, real_name, obuf)
24	DB *dbp;
25	char *real_name;
26	u_int8_t *obuf;
27{
28	ENV *env;
29	HASHHDR *oldmeta;
30	HMETA30 newmeta;
31	u_int32_t *o_spares, *n_spares;
32	u_int32_t fillf, i, maxb, max_entry, nelem;
33	int ret;
34
35	env = dbp->env;
36	memset(&newmeta, 0, sizeof(newmeta));
37
38	oldmeta = (HASHHDR *)obuf;
39
40	/*
41	 * The first 32 bytes are similar.  The only change is the version
42	 * and that we removed the ovfl_point and have the page type now.
43	 */
44
45	newmeta.dbmeta.lsn = oldmeta->lsn;
46	newmeta.dbmeta.pgno = oldmeta->pgno;
47	newmeta.dbmeta.magic = oldmeta->magic;
48	newmeta.dbmeta.version = 6;
49	newmeta.dbmeta.pagesize = oldmeta->pagesize;
50	newmeta.dbmeta.type = P_HASHMETA;
51
52	/* Move flags */
53	newmeta.dbmeta.flags = oldmeta->flags;
54
55	/* Copy the free list, which has changed its name but works the same. */
56	newmeta.dbmeta.free = oldmeta->last_freed;
57
58	/* Copy: max_bucket, high_mask, low-mask, ffactor, nelem, h_charkey */
59	newmeta.max_bucket = oldmeta->max_bucket;
60	newmeta.high_mask = oldmeta->high_mask;
61	newmeta.low_mask = oldmeta->low_mask;
62	newmeta.ffactor = oldmeta->ffactor;
63	newmeta.nelem = oldmeta->nelem;
64	newmeta.h_charkey = oldmeta->h_charkey;
65
66	/*
67	 * There was a bug in 2.X versions where the nelem could go negative.
68	 * In general, this is considered "bad."  If it does go negative
69	 * (that is, very large and positive), we'll die trying to dump and
70	 * load this database.  So, let's see if we can fix it here.
71	 */
72	nelem = newmeta.nelem;
73	fillf = newmeta.ffactor;
74	maxb = newmeta.max_bucket;
75
76	if ((fillf != 0 && fillf * maxb < 2 * nelem) ||
77	    (fillf == 0 && nelem > 0x8000000))
78		newmeta.nelem = 0;
79
80	/*
81	 * We now have to convert the spares array.  The old spares array
82	 * contained the total number of extra pages allocated prior to
83	 * the bucket that begins the next doubling.  The new spares array
84	 * contains the page number of the first bucket in the next doubling
85	 * MINUS the bucket number of that bucket.
86	 */
87	o_spares = oldmeta->spares;
88	n_spares = newmeta.spares;
89	max_entry = __db_log2(maxb + 1);   /* highest spares entry in use */
90	n_spares[0] = 1;
91	for (i = 1; i < NCACHED && i <= max_entry; i++)
92		n_spares[i] = 1 + o_spares[i - 1];
93
94					/* Replace the unique ID. */
95	if ((ret = __os_fileid(env, real_name, 1, newmeta.dbmeta.uid)) != 0)
96		return (ret);
97
98	/* Overwrite the original. */
99	memcpy(oldmeta, &newmeta, sizeof(newmeta));
100
101	return (0);
102}
103
104/*
105 * __ham_30_sizefix --
106 *	Make sure that all hash pages belonging to the current
107 *	hash doubling are within the bounds of the file.
108 *
109 * PUBLIC: int __ham_30_sizefix __P((DB *, DB_FH *, char *, u_int8_t *));
110 */
111int
112__ham_30_sizefix(dbp, fhp, realname, metabuf)
113	DB *dbp;
114	DB_FH *fhp;
115	char *realname;
116	u_int8_t *metabuf;
117{
118	u_int8_t buf[DB_MAX_PGSIZE];
119	ENV *env;
120	HMETA30 *meta;
121	db_pgno_t last_actual, last_desired;
122	int ret;
123	size_t nw;
124	u_int32_t pagesize;
125
126	env = dbp->env;
127	memset(buf, 0, DB_MAX_PGSIZE);
128
129	meta = (HMETA30 *)metabuf;
130	pagesize = meta->dbmeta.pagesize;
131
132	/*
133	 * Get the last page number.  To do this, we'll need dbp->pgsize
134	 * to be set right, so slam it into place.
135	 */
136	dbp->pgsize = pagesize;
137	if ((ret = __db_lastpgno(dbp, realname, fhp, &last_actual)) != 0)
138		return (ret);
139
140	/*
141	 * The last bucket in the doubling is equal to high_mask;  calculate
142	 * the page number that implies.
143	 */
144	last_desired = BS_TO_PAGE(meta->high_mask, meta->spares);
145
146	/*
147	 * If last_desired > last_actual, we need to grow the file.  Write
148	 * a zeroed page where last_desired would go.
149	 */
150	if (last_desired > last_actual) {
151		if ((ret = __os_seek(
152		    env, fhp, last_desired, pagesize, 0)) != 0)
153			return (ret);
154		if ((ret = __os_write(env, fhp, buf, pagesize, &nw)) != 0)
155			return (ret);
156	}
157
158	return (0);
159}
160
161/*
162 * __ham_31_hashmeta --
163 *	Upgrade the database from version 6 to version 7.
164 *
165 * PUBLIC: int __ham_31_hashmeta
166 * PUBLIC:      __P((DB *, char *, u_int32_t, DB_FH *, PAGE *, int *));
167 */
168int
169__ham_31_hashmeta(dbp, real_name, flags, fhp, h, dirtyp)
170	DB *dbp;
171	char *real_name;
172	u_int32_t flags;
173	DB_FH *fhp;
174	PAGE *h;
175	int *dirtyp;
176{
177	HMETA30 *oldmeta;
178	HMETA31 *newmeta;
179
180	COMPQUIET(dbp, NULL);
181	COMPQUIET(real_name, NULL);
182	COMPQUIET(fhp, NULL);
183
184	newmeta = (HMETA31 *)h;
185	oldmeta = (HMETA30 *)h;
186
187	/*
188	 * Copy the fields down the page.
189	 * The fields may overlap so start at the bottom and use memmove().
190	 */
191	memmove(newmeta->spares, oldmeta->spares, sizeof(oldmeta->spares));
192	newmeta->h_charkey = oldmeta->h_charkey;
193	newmeta->nelem = oldmeta->nelem;
194	newmeta->ffactor = oldmeta->ffactor;
195	newmeta->low_mask = oldmeta->low_mask;
196	newmeta->high_mask = oldmeta->high_mask;
197	newmeta->max_bucket = oldmeta->max_bucket;
198	memmove(newmeta->dbmeta.uid,
199	    oldmeta->dbmeta.uid, sizeof(oldmeta->dbmeta.uid));
200	newmeta->dbmeta.flags = oldmeta->dbmeta.flags;
201	newmeta->dbmeta.record_count = 0;
202	newmeta->dbmeta.key_count = 0;
203	ZERO_LSN(newmeta->dbmeta.unused3);
204
205	/* Update the version. */
206	newmeta->dbmeta.version = 7;
207
208	/* Upgrade the flags. */
209	if (LF_ISSET(DB_DUPSORT))
210		F_SET(&newmeta->dbmeta, DB_HASH_DUPSORT);
211
212	*dirtyp = 1;
213	return (0);
214}
215
216/*
217 * __ham_31_hash --
218 *	Upgrade the database hash leaf pages.
219 *
220 * PUBLIC: int __ham_31_hash
221 * PUBLIC:      __P((DB *, char *, u_int32_t, DB_FH *, PAGE *, int *));
222 */
223int
224__ham_31_hash(dbp, real_name, flags, fhp, h, dirtyp)
225	DB *dbp;
226	char *real_name;
227	u_int32_t flags;
228	DB_FH *fhp;
229	PAGE *h;
230	int *dirtyp;
231{
232	HKEYDATA *hk;
233	db_pgno_t pgno, tpgno;
234	db_indx_t indx;
235	int ret;
236
237	COMPQUIET(flags, 0);
238
239	ret = 0;
240	for (indx = 0; indx < NUM_ENT(h); indx += 2) {
241		hk = (HKEYDATA *)H_PAIRDATA(dbp, h, indx);
242		if (HPAGE_PTYPE(hk) == H_OFFDUP) {
243			memcpy(&pgno, HOFFDUP_PGNO(hk), sizeof(db_pgno_t));
244			tpgno = pgno;
245			if ((ret = __db_31_offdup(dbp, real_name, fhp,
246			    LF_ISSET(DB_DUPSORT) ? 1 : 0, &tpgno)) != 0)
247				break;
248			if (pgno != tpgno) {
249				*dirtyp = 1;
250				memcpy(HOFFDUP_PGNO(hk),
251				    &tpgno, sizeof(db_pgno_t));
252			}
253		}
254	}
255
256	return (ret);
257}
258
259/*
260 * __ham_46_hashmeta --
261 *	Upgrade the database from version 8 to version 9.
262 *
263 * PUBLIC: int __ham_46_hashmeta
264 * PUBLIC:      __P((DB *, char *, u_int32_t, DB_FH *, PAGE *, int *));
265 */
266int
267__ham_46_hashmeta(dbp, real_name, flags, fhp, h, dirtyp)
268	DB *dbp;
269	char *real_name;
270	u_int32_t flags;
271	DB_FH *fhp;
272	PAGE *h;
273	int *dirtyp;
274{
275	HMETA33 *newmeta;
276
277	COMPQUIET(dbp, NULL);
278	COMPQUIET(real_name, NULL);
279	COMPQUIET(flags, 0);
280	COMPQUIET(fhp, NULL);
281
282	newmeta = (HMETA33 *)h;
283	/* Update the version. */
284	newmeta->dbmeta.version = 9;
285	*dirtyp = 1;
286
287	return (0);
288}
289
290/*
291 * __ham_46_hash --
292 *	Upgrade the database hash leaf pages.
293 *	From version 8 databases to version 9.
294 *	Involves sorting leaf pages, no format change.
295 *
296 * PUBLIC: int __ham_46_hash
297 * PUBLIC:      __P((DB *, char *, u_int32_t, DB_FH *, PAGE *, int *));
298 */
299int
300__ham_46_hash(dbp, real_name, flags, fhp, h, dirtyp)
301	DB *dbp;
302	char *real_name;
303	u_int32_t flags;
304	DB_FH *fhp;
305	PAGE *h;
306	int *dirtyp;
307{
308	DBC *dbc;
309	int ret, t_ret;
310
311	COMPQUIET(real_name, NULL);
312	COMPQUIET(flags, 0);
313	COMPQUIET(fhp, NULL);
314
315	if ((ret = __db_cursor(dbp, NULL, NULL, &dbc, 0)) != 0)
316		return (ret);
317	*dirtyp = 1;
318	ret = __ham_sort_page(dbc, NULL, h);
319	if ((t_ret = __dbc_close(dbc)) != 0 && ret == 0)
320		ret = t_ret;
321
322	return (ret);
323}
324