📄 db_cam.c
字号:
int ret, t_ret; sdbp = sdbc->dbp; pdbp = sdbp->s_primary; dbenv = sdbp->dbenv; pdbc = NULL; ret = t_ret = 0; rmw = LF_ISSET(DB_RMW); memset(&discardme, 0, sizeof(DBT)); F_SET(&discardme, DB_DBT_USERMEM | DB_DBT_PARTIAL); oob = RECNO_OOB; /* * If the primary is an rbtree, we want its record number, whether * or not the secondary is one too. Fetch the recno into "data". * * If it's not an rbtree, return RECNO_OOB in "data". */ if (F_ISSET(pdbp, DB_AM_RECNUM)) { /* * Get the primary key, so we can find the record number * in the primary. (We're uninterested in the secondary key.) */ memset(&primary_key, 0, sizeof(DBT)); F_SET(&primary_key, DB_DBT_MALLOC); if ((ret = sdbc->c_real_get(sdbc, &discardme, &primary_key, rmw | DB_CURRENT)) != 0) return (ret); /* * Open a cursor on the primary, set it to the right record, * and fetch its recno into "data". * * (See __db_c_pget for a comment on the use of __db_icursor.) * * SET_RET_MEM so that the secondary DBC owns any returned-data * memory. */ if ((ret = __db_icursor(pdbp, sdbc->txn, pdbp->type, PGNO_INVALID, 0, sdbc->locker, &pdbc)) != 0) goto perr; SET_RET_MEM(pdbc, sdbc); if ((ret = pdbc->c_get(pdbc, &primary_key, &discardme, rmw | DB_SET)) != 0) goto perr; ret = pdbc->c_get(pdbc, &discardme, data, rmw | DB_GET_RECNO);perr: __os_ufree(sdbp->dbenv, primary_key.data); if (pdbc != NULL && (t_ret = pdbc->c_close(pdbc)) != 0 && ret == 0) ret = t_ret; if (ret != 0) return (ret); } else if ((ret = __db_retcopy(dbenv, data, &oob, sizeof(oob), &sdbc->rkey->data, &sdbc->rkey->ulen)) != 0) return (ret); /* * If the secondary is an rbtree, we want its record number, whether * or not the primary is one too. Fetch the recno into "pkey". * * If it's not an rbtree, return RECNO_OOB in "pkey". */ if (F_ISSET(sdbp, DB_AM_RECNUM)) return (sdbc->c_real_get(sdbc, &discardme, pkey, flags)); else return (__db_retcopy(dbenv, pkey, &oob, sizeof(oob), &sdbc->rdata->data, &sdbc->rdata->ulen));}/* * __db_wrlock_err -- do not have a write lock. */static int__db_wrlock_err(dbenv) DB_ENV *dbenv;{ __db_err(dbenv, "Write attempted on read-only cursor"); return (EPERM);}/* * __db_c_del_secondary -- * Perform a delete operation on a secondary index: call through * to the primary and delete the primary record that this record * points to. * * Note that deleting the primary record will call c_del on all * the secondaries, including this one; thus, it is not necessary * to execute both this function and an actual delete. * */static int__db_c_del_secondary(dbc) DBC *dbc;{ DB *pdbp; DBC *pdbc; DBT skey, pkey; int ret, t_ret; memset(&skey, 0, sizeof(DBT)); memset(&pkey, 0, sizeof(DBT)); /* * Get the current item that we're pointing at. * We don't actually care about the secondary key, just * the primary. */ F_SET(&skey, DB_DBT_PARTIAL | DB_DBT_USERMEM); if ((ret = dbc->c_real_get(dbc, &skey, &pkey, DB_CURRENT)) != 0) return (ret); /* * Create a cursor on the primary with our locker ID, * so that when it calls back, we don't conflict. * * We create a cursor explicitly because there's no * way to specify the same locker ID if we're using * locking but not transactions if we use the DB->del * interface. This shouldn't be any less efficient * anyway. */ pdbp = dbc->dbp->s_primary; if ((ret = __db_icursor(pdbp, dbc->txn, pdbp->type, PGNO_INVALID, 0, dbc->locker, &pdbc)) != 0) return (ret); /* * See comment in __db_c_put--if we're in CDB, * we already hold the locks we need, and we need to flag * the cursor as a WRITER so we don't run into errors * when we try to delete. */ if (CDB_LOCKING(pdbp->dbenv)) { DB_ASSERT(pdbc->mylock.off == LOCK_INVALID); F_SET(pdbc, DBC_WRITER); } /* * Set the new cursor to the correct primary key. Then * delete it. We don't really care about the datum; * just reuse our skey DBT. * * If the primary get returns DB_NOTFOUND, something is amiss-- * every record in the secondary should correspond to some record * in the primary. */ if ((ret = pdbc->c_get(pdbc, &pkey, &skey, (STD_LOCKING(dbc) ? DB_RMW : 0) | DB_SET)) == 0) ret = pdbc->c_del(pdbc, 0); else if (ret == DB_NOTFOUND) ret = __db_secondary_corrupt(pdbp); if ((t_ret = pdbc->c_close(pdbc)) != 0 && ret != 0) ret = t_ret; return (ret);}/* * __db_c_del_primary -- * Perform a delete operation on a primary index. Loop through * all the secondary indices which correspond to this primary * database, and delete any secondary keys that point at the current * record. * * PUBLIC: int __db_c_del_primary __P((DBC *)); */int__db_c_del_primary(dbc) DBC *dbc;{ DB *dbp, *sdbp; DBC *sdbc; DBT data, pkey, skey, temp; int ret, t_ret; dbp = dbc->dbp; /* * If we're called at all, we have at least one secondary. * (Unfortunately, we can't assert this without grabbing the mutex.) * Get the current record so that we can construct appropriate * secondary keys as needed. */ memset(&pkey, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); if ((ret = dbc->c_get(dbc, &pkey, &data, DB_CURRENT)) != 0) return (ret); for (sdbp = __db_s_first(dbp); sdbp != NULL && ret == 0; ret = __db_s_next(&sdbp)) { /* * Get the secondary key for this secondary and the current * item. */ memset(&skey, 0, sizeof(DBT)); if ((ret = sdbp->s_callback(sdbp, &pkey, &data, &skey)) != 0) { /* * If the current item isn't in this index, we * have no work to do. Proceed. */ if (ret == DB_DONOTINDEX) continue; /* We had a substantive error. Bail. */ FREE_IF_NEEDED(sdbp, &skey); goto done; } /* Open a secondary cursor. */ if ((ret = __db_icursor(sdbp, dbc->txn, sdbp->type, PGNO_INVALID, 0, dbc->locker, &sdbc)) != 0) goto done; /* See comment above and in __db_c_put. */ if (CDB_LOCKING(sdbp->dbenv)) { DB_ASSERT(sdbc->mylock.off == LOCK_INVALID); F_SET(sdbc, DBC_WRITER); } /* * Set the secondary cursor to the appropriate item. * Delete it. * * We want to use DB_RMW if locking is on; it's only * legal then, though. * * !!! * Don't stomp on any callback-allocated buffer in skey * when we do a c_get(DB_GET_BOTH); use a temp DBT instead. */ memset(&temp, 0, sizeof(DBT)); temp.data = skey.data; temp.size = skey.size; if ((ret = sdbc->c_real_get(sdbc, &temp, &pkey, (STD_LOCKING(dbc) ? DB_RMW : 0) | DB_GET_BOTH)) == 0) ret = sdbc->c_del(sdbc, DB_UPDATE_SECONDARY); FREE_IF_NEEDED(sdbp, &skey); if ((t_ret = sdbc->c_close(sdbc)) != 0 || ret != 0) { if (ret == 0) ret = t_ret; goto done; } }done: if (sdbp != NULL && (t_ret = __db_s_done(sdbp)) != 0 && ret == 0) return (t_ret); return (ret);}/* * __db_s_first -- * Get the first secondary, if any are present, from the primary. * * PUBLIC: DB *__db_s_first __P((DB *)); */DB *__db_s_first(pdbp) DB *pdbp;{ DB *sdbp; MUTEX_THREAD_LOCK(pdbp->dbenv, pdbp->mutexp); sdbp = LIST_FIRST(&pdbp->s_secondaries); /* See __db_s_next. */ if (sdbp != NULL) sdbp->s_refcnt++; MUTEX_THREAD_UNLOCK(pdbp->dbenv, pdbp->mutexp); return (sdbp);}/* * __db_s_next -- * Get the next secondary in the list. * * PUBLIC: int __db_s_next __P((DB **)); */int__db_s_next(sdbpp) DB **sdbpp;{ DB *sdbp, *pdbp, *closeme; int ret; /* * Secondary indices are kept in a linked list, s_secondaries, * off each primary DB handle. If a primary is free-threaded, * this list may only be traversed or modified while the primary's * thread mutex is held. * * The tricky part is that we don't want to hold the thread mutex * across the full set of secondary puts necessary for each primary * put, or we'll wind up essentially single-threading all the puts * to the handle; the secondary puts will each take about as * long as the primary does, and may require I/O. So we instead * hold the thread mutex only long enough to follow one link to the * next secondary, and then we release it before performing the * actual secondary put. * * The only danger here is that we might legitimately close a * secondary index in one thread while another thread is performing * a put and trying to update that same secondary index. To * prevent this from happening, we refcount the secondary handles. * If close is called on a secondary index handle while we're putting * to it, it won't really be closed--the refcount will simply drop, * and we'll be responsible for closing it here. */ sdbp = *sdbpp; pdbp = sdbp->s_primary; closeme = NULL; MUTEX_THREAD_LOCK(pdbp->dbenv, pdbp->mutexp); DB_ASSERT(sdbp->s_refcnt != 0); if (--sdbp->s_refcnt == 0) { LIST_REMOVE(sdbp, s_links); closeme = sdbp; } sdbp = LIST_NEXT(sdbp, s_links); if (sdbp != NULL) sdbp->s_refcnt++; MUTEX_THREAD_UNLOCK(pdbp->dbenv, pdbp->mutexp); *sdbpp = sdbp; /* * closeme->close() is a wrapper; call __db_close explicitly. */ ret = closeme != NULL ? __db_close(closeme, 0) : 0; return (ret);}/* * __db_s_done -- * Properly decrement the refcount on a secondary database handle we're * using, without calling __db_s_next. * * PUBLIC: int __db_s_done __P((DB *)); */int__db_s_done(sdbp) DB *sdbp;{ DB *pdbp; int doclose; pdbp = sdbp->s_primary; doclose = 0; MUTEX_THREAD_LOCK(pdbp->dbenv, pdbp->mutexp); DB_ASSERT(sdbp->s_refcnt != 0); if (--sdbp->s_refcnt == 0) { LIST_REMOVE(sdbp, s_links); doclose = 1; } MUTEX_THREAD_UNLOCK(pdbp->dbenv, pdbp->mutexp); return (doclose ? __db_close(sdbp, 0) : 0);}/* * __db_buildpartial -- * Build the record that will result after a partial put is applied to * an existing record. * * This should probably be merged with __bam_build, but that requires * a little trickery if we plan to keep the overflow-record optimization * in that function. */static int__db_buildpartial(dbp, oldrec, partial, newrec) DB *dbp; DBT *oldrec, *partial, *newrec;{ int ret; u_int8_t *buf; u_int32_t len, nbytes; DB_ASSERT(F_ISSET(partial, DB_DBT_PARTIAL)); memset(newrec, 0, sizeof(DBT)); nbytes = __db_partsize(oldrec->size, partial); newrec->size = nbytes; if ((ret = __os_malloc(dbp->dbenv, nbytes, &buf)) != 0) return (ret); newrec->data = buf; /* Nul or pad out the buffer, for any part that isn't specified. */ memset(buf, F_ISSET(dbp, DB_AM_FIXEDLEN) ? ((BTREE *)dbp->bt_internal)->re_pad : 0, nbytes); /* Copy in any leading data from the original record. */ memcpy(buf, oldrec->data, partial->doff > oldrec->size ? oldrec->size : partial->doff); /* Copy the data from partial. */ memcpy(buf + partial->doff, partial->data, partial->size); /* Copy any trailing data from the original record. */ len = partial->doff + partial->dlen; if (oldrec->size > len) memcpy(buf + partial->doff + partial->size, (u_int8_t *)oldrec->data + len, oldrec->size - len); return (0);}/* * __db_partsize -- * Given the number of bytes in an existing record and a DBT that * is about to be partial-put, calculate the size of the record * after the put. * * This code is called from __bam_partsize. * * PUBLIC: u_int32_t __db_partsize __P((u_int32_t, DBT *)); */u_int32_t__db_partsize(nbytes, data) u_int32_t nbytes; DBT *data;{ /* * There are really two cases here: * * Case 1: We are replacing some bytes that do not exist (i.e., they * are past the end of the record). In this case the number of bytes * we are replacing is irrelevant and all we care about is how many * bytes we are going to add from offset. So, the new record length * is going to be the size of the new bytes (size) plus wherever those * new bytes begin (doff). * * Case 2: All the bytes we are replacing exist. Therefore, the new * size is the oldsize (nbytes) minus the bytes we are replacing (dlen) * plus the bytes we are adding (size). */ if (nbytes < data->doff + data->dlen) /* Case 1 */ return (data->doff + data->size); return (nbytes + data->size - data->dlen); /* Case 2 */}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -