📄 db_join.c
字号:
* that cursor is sorted, then any * other sorted cursors can be safely * reset to the first duplicate * duplicate in the current set if we * have a pointer to it (we can't just * leave them be, or we'll miss * duplicate duplicates in the outer * relation). * * If the first cursor is unsorted, or * if cursor j is unsorted, we can * make no assumptions about what * we're looking for next or where it * will be, so we reset to the very * beginning (setting workcurs NULL * will achieve this next go-round). * * XXX: This is likely to break * horribly if any two cursors are * both sorted, but have different * specified sort functions. For, * now, we dismiss this as pathology * and let strange things happen--we * can't make rope childproof. */ if ((ret = jc->j_workcurs[j]->c_close( jc->j_workcurs[j])) != 0) goto err; if (!SORTED_SET(jc, 0) || !SORTED_SET(jc, j) || jc->j_fdupcurs[j] == NULL) /* * Unsafe conditions; * reset fully. */ jc->j_workcurs[j] = NULL; else /* Partial reset suffices. */ if ((jc->j_fdupcurs[j]->c_dup( jc->j_fdupcurs[j], &jc->j_workcurs[j], DB_POSITIONI)) != 0) goto err; jc->j_exhausted[j] = 0; } goto retry; /* NOTREACHED */ } /* * We're about to advance the cursor and need to * reset all of the workcurs[j] where j>i, so that * we don't miss any duplicate duplicates. */ for (j = i + 1; jc->j_workcurs[j] != NULL; j++) { if ((ret = jc->j_workcurs[j]->c_close( jc->j_workcurs[j])) != 0) goto err; jc->j_exhausted[j] = 0; if (jc->j_fdupcurs[j] != NULL && (ret = jc->j_fdupcurs[j]->c_dup( jc->j_fdupcurs[j], &jc->j_workcurs[j], DB_POSITIONI)) != 0) goto err; else jc->j_workcurs[j] = NULL; } goto retry2; /* NOTREACHED */ } if (ret == ENOMEM) { jc->j_key.ulen <<= 1; if ((ret = __os_realloc(dbp->dbenv, jc->j_key.ulen, &jc->j_key.data)) != 0) {mem_err: __db_err(dbp->dbenv, "Allocation failed for join key, len = %lu", (u_long)jc->j_key.ulen); goto err; } goto retry2; } if (ret != 0) goto err; /* * If we made it this far, we've found a matching * datum in cursor i. Mark the current cursor * unexhausted, so we don't miss any duplicate * duplicates the next go-round--unless this is the * very last cursor, in which case there are none to * miss, and we'll need that exhausted flag to finally * get a DB_NOTFOUND and move on to the next datum in * the outermost cursor. */ if (i + 1 != jc->j_ncurs) jc->j_exhausted[i] = 0; else jc->j_exhausted[i] = 1; /* * If jc->j_fdupcurs[i] is NULL and the ith cursor's dups are * sorted, then we're here for the first time since advancing * cursor 0, and we have a new datum of interest. * jc->j_workcurs[i] points to the beginning of a set of * duplicate duplicates; store this into jc->j_fdupcurs[i]. */ if (SORTED_SET(jc, i) && jc->j_fdupcurs[i] == NULL && (ret = cp->c_dup(cp, &jc->j_fdupcurs[i], DB_POSITIONI)) != 0) goto err; }err: if (ret != 0) return (ret); if (0) {samekey: /* * Get the key we tried and failed to return last time; * it should be the current datum of all the secondary cursors. */ if ((ret = jc->j_workcurs[0]->c_real_get(jc->j_workcurs[0], &jc->j_key, key_n, DB_CURRENT | opmods)) != 0) return (ret); F_CLR(jc, JOIN_RETRY); } /* * ret == 0; we have a key to return. * * If DB_DBT_USERMEM or DB_DBT_MALLOC is set, we need to copy the key * back into the dbt we were given for the key; call __db_retcopy. * Otherwise, assert that we do not need to copy anything and proceed. */ DB_ASSERT(F_ISSET( key_arg, DB_DBT_USERMEM | DB_DBT_MALLOC) || key_n == key_arg); if (F_ISSET(key_arg, DB_DBT_USERMEM | DB_DBT_MALLOC) && (ret = __db_retcopy(dbp->dbenv, key_arg, key_n->data, key_n->size, NULL, NULL)) != 0) { /* * The retcopy failed, most commonly because we have a user * buffer for the key which is too small. Set things up to * retry next time, and return. */ F_SET(jc, JOIN_RETRY); return (ret); } /* * If DB_JOIN_ITEM is set, we return it; otherwise we do the lookup * in the primary and then return. * * Note that we use key_arg here; it is safe (and appropriate) * to do so. */ if (operation == DB_JOIN_ITEM) return (0); /* * If data_arg->flags == 0--that is, if DB is managing the * data DBT's memory--it's not safe to just pass the DBT * through to the primary get call, since we don't want that * memory to belong to the primary DB handle (and if the primary * is free-threaded, it can't anyway). * * Instead, use memory that is managed by the join cursor, in * jc->j_rdata. */ if (!F_ISSET(data_arg, DB_DBT_MALLOC | DB_DBT_REALLOC | DB_DBT_USERMEM)) db_manage_data = 1; else db_manage_data = 0; if ((ret = __db_join_primget(jc->j_primary, jc->j_curslist[0]->txn, jc->j_curslist[0]->locker, key_arg, db_manage_data ? &jc->j_rdata : data_arg, opmods)) != 0) { if (ret == DB_NOTFOUND) /* * If ret == DB_NOTFOUND, the primary and secondary * are out of sync; every item in each secondary * should correspond to something in the primary, * or we shouldn't have done the join this way. * Wail. */ ret = __db_secondary_corrupt(jc->j_primary); else /* * The get on the primary failed for some other * reason, most commonly because we're using a user * buffer that's not big enough. Flag our failure * so we can return the same key next time. */ F_SET(jc, JOIN_RETRY); } if (db_manage_data && ret == 0) { data_arg->data = jc->j_rdata.data; data_arg->size = jc->j_rdata.size; } return (ret);}static int__db_join_close(dbc) DBC *dbc;{ DB *dbp; DB_ENV *dbenv; JOIN_CURSOR *jc; int ret, t_ret; u_int32_t i; jc = (JOIN_CURSOR *)dbc->internal; dbp = dbc->dbp; dbenv = dbp->dbenv; ret = t_ret = 0; /* * Remove from active list of join cursors. Note that this * must happen before any action that can fail and return, or else * __db_close may loop indefinitely. */ MUTEX_THREAD_LOCK(dbenv, dbp->mutexp); TAILQ_REMOVE(&dbp->join_queue, dbc, links); MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp); PANIC_CHECK(dbenv); /* * Close any open scratch cursors. In each case, there may * not be as many outstanding as there are cursors in * curslist, but we want to close whatever's there. * * If any close fails, there's no reason not to close everything else; * we'll just return the error code of the last one to fail. There's * not much the caller can do anyway, since these cursors only exist * hanging off a db-internal data structure that they shouldn't be * mucking with. */ for (i = 0; i < jc->j_ncurs; i++) { if (jc->j_workcurs[i] != NULL && (t_ret = jc->j_workcurs[i]->c_close(jc->j_workcurs[i])) != 0) ret = t_ret; if (jc->j_fdupcurs[i] != NULL && (t_ret = jc->j_fdupcurs[i]->c_close(jc->j_fdupcurs[i])) != 0) ret = t_ret; } __os_free(dbenv, jc->j_exhausted); __os_free(dbenv, jc->j_curslist); __os_free(dbenv, jc->j_workcurs); __os_free(dbenv, jc->j_fdupcurs); __os_free(dbenv, jc->j_key.data); if (jc->j_rdata.data != NULL) __os_ufree(dbenv, jc->j_rdata.data); __os_free(dbenv, jc); __os_free(dbenv, dbc); return (ret);}/* * __db_join_getnext -- * This function replaces the DBC_CONTINUE and DBC_KEYSET * functionality inside the various cursor get routines. * * If exhausted == 0, we're not done with the current datum; * return it if it matches "matching", otherwise search * using DB_GET_BOTHC (which is faster than iteratively doing * DB_NEXT_DUP) forward until we find one that does. * * If exhausted == 1, we are done with the current datum, so just * leap forward to searching NEXT_DUPs. * * If no matching datum exists, returns DB_NOTFOUND, else 0. */static int__db_join_getnext(dbc, key, data, exhausted, opmods) DBC *dbc; DBT *key, *data; u_int32_t exhausted, opmods;{ int ret, cmp; DB *dbp; DBT ldata; int (*func) __P((DB *, const DBT *, const DBT *)); dbp = dbc->dbp; func = (dbp->dup_compare == NULL) ? __bam_defcmp : dbp->dup_compare; switch (exhausted) { case 0: /* * We don't want to step on data->data; use a new * DBT and malloc so we don't step on dbc's rdata memory. */ memset(&ldata, 0, sizeof(DBT)); F_SET(&ldata, DB_DBT_MALLOC); if ((ret = dbc->c_real_get(dbc, key, &ldata, opmods | DB_CURRENT)) != 0) break; cmp = func(dbp, data, &ldata); if (cmp == 0) { /* * We have to return the real data value. Copy * it into data, then free the buffer we malloc'ed * above. */ if ((ret = __db_retcopy(dbp->dbenv, data, ldata.data, ldata.size, &data->data, &data->size)) != 0) return (ret); __os_ufree(dbp->dbenv, ldata.data); return (0); } /* * Didn't match--we want to fall through and search future * dups. We just forget about ldata and free * its buffer--data contains the value we're searching for. */ __os_ufree(dbp->dbenv, ldata.data); /* FALLTHROUGH */ case 1: ret = dbc->c_real_get(dbc, key, data, opmods | DB_GET_BOTHC); break; default: ret = EINVAL; break; } return (ret);}/* * __db_join_cmp -- * Comparison function for sorting DBCs in cardinality order. */static int__db_join_cmp(a, b) const void *a, *b;{ DBC *dbca, *dbcb; db_recno_t counta, countb; /* In case c_count fails, pretend cursors are equal. */ counta = countb = 0; dbca = *((DBC * const *)a); dbcb = *((DBC * const *)b); if (dbca->c_count(dbca, &counta, 0) != 0 || dbcb->c_count(dbcb, &countb, 0) != 0) return (0); return (counta - countb);}/* * __db_join_primget -- * Perform a DB->get in the primary, being careful not to use a new * locker ID if we're doing CDB locking. */static int__db_join_primget(dbp, txn, lockerid, key, data, flags) DB *dbp; DB_TXN *txn; u_int32_t lockerid; DBT *key, *data; u_int32_t flags;{ DBC *dbc; int dirty, ret, rmw, t_ret; /* * The only allowable flags here are the two flags copied into * "opmods" in __db_join_get, DB_RMW and DB_DIRTY_READ. The former * is an op on the c_get call, the latter on the cursor call. * It's a DB bug if we allow any other flags down in here. */ rmw = LF_ISSET(DB_RMW); dirty = LF_ISSET(DB_DIRTY_READ); LF_CLR(DB_RMW | DB_DIRTY_READ); DB_ASSERT(flags == 0); if ((ret = __db_icursor(dbp, txn, dbp->type, PGNO_INVALID, 0, lockerid, &dbc)) != 0) return (ret); if (dirty || (txn != NULL && F_ISSET(txn, TXN_DIRTY_READ))) F_SET(dbc, DBC_DIRTY_READ); F_SET(dbc, DBC_TRANSIENT); /* * This shouldn't be necessary, thanks to the fact that join cursors * swap in their own DB_DBT_REALLOC'ed buffers, but just for form's * sake, we mirror what __db_get does. */ SET_RET_MEM(dbc, dbp); ret = dbc->c_get(dbc, key, data, DB_SET | rmw); if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0) ret = t_ret; return (ret);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -