⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 db_cam.c

📁 这是国外的resip协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
	 *	 * (XXX: It would be nice to avoid this extra get, and have the	 * underlying put routines somehow pass us the old record	 * since they need to traverse the tree anyway.  I'm saving	 * this optimization for later, as it's a lot of work, and it	 * would be hard to fit into this locking paradigm anyway.)	 *	 * The simple thing to do would be to go get the old record before	 * we do anything else.  Unfortunately, though, doing so would	 * violate our "secondary, then primary" lock acquisition	 * ordering--even in the common case where no old primary record	 * exists, we'll still acquire and keep a lock on the page where	 * we're about to do the primary insert.	 *	 * To get around this, we do the following gyrations, which	 * hopefully solve this problem in the common case:	 *	 * 1) If this is a c_put(DB_CURRENT), go ahead and get the	 *    old record.  We already hold the lock on this page in	 *    the primary, so no harm done, and we'll need the primary	 *    key (which we weren't passed in this case) to do any	 *    secondary puts anyway.	 *	 * 2) If we're doing a partial put, we need to perform the	 *    get on the primary key right away, since we don't have	 *    the whole datum that the secondary key is based on.	 *    We may also need to pad out the record if the primary	 *    has a fixed record length.	 *	 * 3) Loop through the secondary indices, putting into each a	 *    new secondary key that corresponds to the new record.	 *	 * 4) If we haven't done so in (1) or (2), get the old primary	 *    key/data pair.  If one does not exist--the common case--we're	 *    done with secondary indices, and can go straight on to the	 *    primary put.	 *	 * 5) If we do have an old primary key/data pair, however, we need	 *    to loop through all the secondaries a second time and delete	 *    the old secondary in each.	 */	memset(&pkey, 0, sizeof(DBT));	have_oldrec = nodel = 0;	/*	 * Primary indices can't have duplicates, so only DB_CURRENT,	 * DB_KEYFIRST, and DB_KEYLAST make any sense.  Other flags	 * should have been caught by the checking routine, but	 * add a sprinkling of paranoia.	 */	DB_ASSERT(flags == DB_CURRENT ||	    flags == DB_KEYFIRST || flags == DB_KEYLAST);	/*	 * We'll want to use DB_RMW in a few places, but it's only legal	 * when locking is on.	 */	rmw = STD_LOCKING(dbc_arg) ? DB_RMW : 0;	if (flags == DB_CURRENT) {		/* Step 1. */		/*		 * This is safe to do on the cursor we already have;		 * error or no, it won't move.		 *		 * We use DB_RMW for all of these gets because we'll be		 * writing soon enough in the "normal" put code.  In		 * transactional databases we'll hold those write locks		 * even if we close the cursor we're reading with.		 *		 * The DB_KEYEMPTY return needs special handling -- if the		 * cursor is on a deleted key, we return DB_NOTFOUND.		 */		ret = __db_c_get(dbc_arg, &pkey, &olddata, rmw | DB_CURRENT);		if (ret == DB_KEYEMPTY)			ret = DB_NOTFOUND;		if (ret != 0)			goto err;		have_oldrec = 1; /* We've looked for the old record. */	} else {		/*		 * Set pkey so we can use &pkey everywhere instead of key.		 * If DB_CURRENT is set and there is a key at the current		 * location, pkey will be overwritten before it's used.		 */		pkey.data = key->data;		pkey.size = key->size;	}	/*	 * Check for partial puts (step 2).	 */	if (F_ISSET(data, DB_DBT_PARTIAL)) {		if (!have_oldrec && !nodel) {			/*			 * We're going to have to search the tree for the			 * specified key.  Dup a cursor (so we have the same			 * locking info) and do a c_get.			 */			if ((ret = __db_c_idup(dbc_arg, &pdbc, 0)) != 0)				goto err;			/* We should have gotten DB_CURRENT in step 1. */			DB_ASSERT(flags != DB_CURRENT);			ret = __db_c_get(pdbc, &pkey, &olddata, rmw | DB_SET);			if (ret == DB_KEYEMPTY || ret == DB_NOTFOUND) {				nodel = 1;				ret = 0;			}			if ((t_ret = __db_c_close(pdbc)) != 0)				ret = t_ret;			if (ret != 0)				goto err;			have_oldrec = 1;		}		/*		 * Now build the new datum from olddata and the partial		 * data we were given.		 */		if ((ret =		    __db_buildpartial(dbp, &olddata, data, &newdata)) != 0)			goto err;		ispartial = 1;	} else		ispartial = 0;	/*	 * Handle fixed-length records.  If the primary database has	 * fixed-length records, we need to pad out the datum before	 * we pass it into the callback function;  we always index the	 * "real" record.	 */	if ((dbp->type == DB_RECNO && F_ISSET(dbp, DB_AM_FIXEDLEN)) ||	    (dbp->type == DB_QUEUE)) {		if (dbp->type == DB_QUEUE) {			re_len = ((QUEUE *)dbp->q_internal)->re_len;			re_pad = ((QUEUE *)dbp->q_internal)->re_pad;		} else {			re_len = ((BTREE *)dbp->bt_internal)->re_len;			re_pad = ((BTREE *)dbp->bt_internal)->re_pad;		}		size = ispartial ? newdata.size : data->size;		if (size > re_len) {			ret = __db_rec_toobig(dbenv, size, re_len);			goto err;		} else if (size < re_len) {			/*			 * If we're not doing a partial put, copy			 * data->data into newdata.data, then pad out			 * newdata.data.			 *			 * If we're doing a partial put, the data			 * we want are already in newdata.data;  we			 * just need to pad.			 *			 * Either way, realloc is safe.			 */			if ((ret =			    __os_realloc(dbenv, re_len, &newdata.data)) != 0)				goto err;			if (!ispartial)				memcpy(newdata.data, data->data, size);			memset((u_int8_t *)newdata.data + size, re_pad,			    re_len - size);			newdata.size = re_len;			ispartial = 1;		}	}	/*	 * Loop through the secondaries.  (Step 3.)	 *	 * Note that __db_s_first and __db_s_next will take care of	 * thread-locking and refcounting issues.	 */	for (sdbp = __db_s_first(dbp);	    sdbp != NULL && ret == 0; ret = __db_s_next(&sdbp)) {		/*		 * Call the callback for this secondary, to get the		 * appropriate secondary key.		 */		memset(&skey, 0, sizeof(DBT));		if ((ret = sdbp->s_callback(sdbp,		    &pkey, ispartial ? &newdata : data, &skey)) != 0) {			if (ret == DB_DONOTINDEX)				/*				 * The callback returned a null value--don't				 * put this key in the secondary.  Just				 * move on to the next one--we'll handle				 * any necessary deletes in step 5.				 */				continue;			else				goto err;		}		/*		 * Open a cursor in this secondary.		 *		 * Use the same locker ID as our primary cursor, so that		 * we're guaranteed that the locks don't conflict (e.g. in CDB		 * or if we're subdatabases that share and want to lock a		 * metadata page).		 */		if ((ret = __db_cursor_int(sdbp, dbc_arg->txn, sdbp->type,		    PGNO_INVALID, 0, dbc_arg->locker, &sdbc)) != 0)			goto err;		/*		 * If we're in CDB, updates will fail since the new cursor		 * isn't a writer.  However, we hold the WRITE lock in the		 * primary and will for as long as our new cursor lasts,		 * and the primary and secondary share a lock file ID,		 * so it's safe to consider this a WRITER.  The close		 * routine won't try to put anything because we don't		 * really have a lock.		 */		if (CDB_LOCKING(dbenv)) {			DB_ASSERT(sdbc->mylock.off == LOCK_INVALID);			F_SET(sdbc, DBC_WRITER);		}		/*		 * There are three cases here--		 * 1) The secondary supports sorted duplicates.		 *	If we attempt to put a secondary/primary pair		 *	that already exists, that's a duplicate duplicate,		 *	and c_put will return DB_KEYEXIST (see __db_duperr).		 *	This will leave us with exactly one copy of the		 *	secondary/primary pair, and this is just right--we'll		 *	avoid deleting it later, as the old and new secondaries		 *	will match (since the old secondary is the dup dup		 *	that's already there).		 * 2) The secondary supports duplicates, but they're not		 *	sorted.  We need to avoid putting a duplicate		 *	duplicate, because the matching old and new secondaries		 *	will prevent us from deleting anything and we'll		 *	wind up with two secondary records that point to the		 *	same primary key.  Do a c_get(DB_GET_BOTH);  only		 *	do the put if the secondary doesn't exist.		 * 3) The secondary doesn't support duplicates at all.		 *	In this case, secondary keys must be unique;  if		 *	another primary key already exists for this		 *	secondary key, we have to either overwrite it or		 *	not put this one, and in either case we've		 *	corrupted the secondary index.  Do a c_get(DB_SET).		 *	If the secondary/primary pair already exists, do		 *	nothing;  if the secondary exists with a different		 *	primary, return an error;  and if the secondary		 *	does not exist, put it.		 */		if (!F_ISSET(sdbp, DB_AM_DUP)) {			/* Case 3. */			memset(&oldpkey, 0, sizeof(DBT));			F_SET(&oldpkey, DB_DBT_MALLOC);			ret = __db_c_get(sdbc,			    &skey, &oldpkey, rmw | DB_SET);			if (ret == 0) {				cmp = __bam_defcmp(sdbp, &oldpkey, &pkey);				__os_ufree(dbenv, oldpkey.data);				if (cmp != 0) {					__db_err(dbenv, "%s%s",			    "Put results in a non-unique secondary key in an ",			    "index not configured to support duplicates");					ret = EINVAL;					goto skipput;				}			} else if (ret != DB_NOTFOUND && ret != DB_KEYEMPTY)				goto skipput;		} else if (!F_ISSET(sdbp, DB_AM_DUPSORT)) {			/* Case 2. */			memset(&tempskey, 0, sizeof(DBT));			tempskey.data = skey.data;			tempskey.size = skey.size;			memset(&temppkey, 0, sizeof(DBT));			temppkey.data = pkey.data;			temppkey.size = pkey.size;			ret = __db_c_get(sdbc, &tempskey, &temppkey,			    rmw | DB_GET_BOTH);			if (ret != DB_NOTFOUND && ret != DB_KEYEMPTY)				goto skipput;		}		ret = __db_c_put(sdbc, &skey, &pkey, DB_UPDATE_SECONDARY);		/*		 * We don't know yet whether this was a put-overwrite that		 * in fact changed nothing.  If it was, we may get DB_KEYEXIST.		 * This is not an error.		 */		if (ret == DB_KEYEXIST)			ret = 0;skipput:	FREE_IF_NEEDED(sdbp, &skey)		if ((t_ret = __db_c_close(sdbc)) != 0 && ret == 0)			ret = t_ret;		if (ret != 0)			goto err;	}	if (ret != 0)		goto err;	/* If still necessary, go get the old primary key/data.  (Step 4.) */	if (!have_oldrec) {		/* See the comments in step 2.  This is real familiar. */		if ((ret = __db_c_idup(dbc_arg, &pdbc, 0)) != 0)			goto err;		DB_ASSERT(flags != DB_CURRENT);		pkey.data = key->data;		pkey.size = key->size;		ret = __db_c_get(pdbc, &pkey, &olddata, rmw | DB_SET);		if (ret == DB_KEYEMPTY || ret == DB_NOTFOUND) {			nodel = 1;			ret = 0;		}		if ((t_ret = __db_c_close(pdbc)) != 0 && ret == 0)			ret = t_ret;		if (ret != 0)			goto err;		have_oldrec = 1;	}	/*	 * If we don't follow this goto, we do in fact have an old record	 * we may need to go delete.  (Step 5).	 */	if (nodel)		goto skip_s_update;	for (sdbp = __db_s_first(dbp);	    sdbp != NULL && ret == 0; ret = __db_s_next(&sdbp)) {		/*		 * Call the callback for this secondary to get the		 * old secondary key.		 */		memset(&oldskey, 0, sizeof(DBT));		if ((ret = sdbp->s_callback(sdbp,		    &pkey, &olddata, &oldskey)) != 0) {			if (ret == DB_DONOTINDEX)				/*				 * The callback returned a null value--there's				 * nothing to delete.  Go on to the next				 * secondary.				 */				continue;			else				goto err;		}		memset(&skey, 0, sizeof(DBT));		if ((ret = sdbp->s_callback(sdbp,		    &pkey, ispartial ? &newdata : data, &skey)) != 0 &&		    ret != DB_DONOTINDEX)			goto err;		/*		 * If there is no new secondary key, or if the old secondary		 * key is different from the new secondary key, then		 * we need to delete the old one.		 *		 * Note that bt_compare is (and must be) set no matter		 * what access method we're in.		 */		sdbc = NULL;		if (ret == DB_DONOTINDEX ||		    ((BTREE *)sdbp->bt_internal)->bt_compare(sdbp,		    &oldskey, &skey) != 0) {			if ((ret = __db_cursor_int(			    sdbp, dbc_arg->txn, sdbp->type,			    PGNO_INVALID, 0, dbc_arg->locker, &sdbc)) != 0)				goto err;			if (CDB_LOCKING(dbenv)) {				DB_ASSERT(sdbc->mylock.off == LOCK_INVALID);				F_SET(sdbc, DBC_WRITER);			}			/*			 * Don't let c_get(DB_GET_BOTH) stomp on			 * our data.  Use a temp DBT instead.			 */			memset(&tempskey, 0, sizeof(DBT));			tempskey.data = oldskey.data;			tempskey.size = oldskey.size;			memset(&temppkey, 0, sizeof(DBT));			temppkey.data = pkey.data;			temppkey.size = pkey.size;			if ((ret = __db_c_get(sdbc,			    &tempskey, &temppkey, rmw | DB_GET_BOTH)) == 0)				ret = __db_c_del(sdbc, DB_UPDATE_SECONDARY);			else if (ret == DB_NOTFOUND)				ret = __db_secondary_corrupt(dbp);		}		FREE_IF_NEEDED(sdbp, &skey);		FREE_IF_NEEDED(sdbp, &oldskey);		if (sdbc != NULL && (t_ret = __db_c_close(sdbc)) != 0 &&		    ret == 0)			ret = t_ret;		if (ret != 0)			goto err;	}	/* Secondary index updates are now done.  On to the "real" stuff. */skip_s_update:	/*	 * If we have an off-page duplicates cursor, and the operation applies	 * to it, perform the operation.  Duplicate the cursor and call the	 * underlying function.	 *	 * Off-page duplicate trees are locked in the primary tree, that is,	 * we acquire a write lock in the primary tree and no locks in the	 * off-page dup tree.  If the put operation is done in an off-page	 * duplicate tree, call the primary cursor's upgrade routine first.	 */	if (dbc_arg->internal->opd != NULL &&	    (flags == DB_AFTER || flags == DB_BEFORE || flags == DB_CURRENT)) {		/*		 * A special case for hash off-page duplicates.  Hash doesn't		 * support (and is documented not to support) put operations		 * relative to a cursor which references an already deleted		 * item.  For consistency, apply the same criteria to off-page		 * duplicates as well.		 */		if (dbc_arg->dbtype == DB_HASH && F_ISSET(		    ((BTREE_CURSOR *)(dbc_arg->internal->opd->internal)),		    C_DELETED)) {			ret = DB_NOTFOUND;			goto err;		}		if ((ret = dbc_arg->c_am_writelock(dbc_arg)) != 0)			return (ret);		if ((ret = __db_c_dup(dbc_arg, &dbc_n, DB_POSITION)) != 0)			goto err;		opd = dbc_n->internal->opd;		if ((ret = opd->c_am_put(		    opd, key, data, flags, NULL)) != 0)			goto err;		goto done;	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -