TEST_F(DRBinaryLogTest, VerifyHiddenColumns) { ASSERT_FALSE(flush(98)); // single row write transaction beginTxn(99, 99, 98, 70); TableTuple first_tuple = insertTuple(m_table, prepareTempTuple(m_table, 42, 55555, "349508345.34583", "a thing", "a totally different thing altogether", 5433)); endTxn(true); flushAndApply(99); TableTuple tuple = m_tableReplica->lookupTupleByValues(first_tuple); NValue drTimestamp = tuple.getHiddenNValue(m_table->getDRTimestampColumnIndex()); NValue drTimestampReplica = tuple.getHiddenNValue(m_tableReplica->getDRTimestampColumnIndex()); EXPECT_EQ(ValuePeeker::peekAsBigInt(drTimestamp), 70); EXPECT_EQ(0, drTimestamp.compare(drTimestampReplica)); }
NValue SubqueryExpression::eval(const TableTuple *tuple1, const TableTuple *tuple2) const { // Get the subquery context with the last evaluation result and parameters used to obtain that result ExecutorContext* exeContext = ExecutorContext::getExecutorContext(); SubqueryContext* context = exeContext->getSubqueryContext(m_subqueryId); bool hasPriorResult = (context != NULL) && context->hasValidResult(); bool paramsChanged = false; NValueArray& parameterContainer = *(exeContext->getParameterContainer()); VOLT_TRACE ("Running subquery: %d", m_subqueryId); // Substitute parameters. if (m_tveParams.get() != NULL) { size_t paramsCnt = m_tveParams->size(); for (size_t i = 0; i < paramsCnt; ++i) { AbstractExpression* tveParam = (*m_tveParams)[i]; NValue param = tveParam->eval(tuple1, tuple2); // compare the new param value with the previous one. Since this parameter is set // by this subquery, no other subquery can change its value. So, we don't need to // save its value on the side for future comparisons. NValue& prevParam = parameterContainer[m_paramIdxs[i]]; if (hasPriorResult) { if (param.compare(prevParam) == VALUE_COMPARE_EQUAL) { continue; } paramsChanged = true; } // Update the value stored in the executor context's parameter container: prevParam = param.copyNValue(); } } // Note the other (non-tve) parameter values and check if they've changed since the last invocation. if (hasPriorResult) { std::vector<NValue>& lastParams = context->accessLastParams(); assert(lastParams.size() == m_otherParamIdxs.size()); for (size_t i = 0; i < lastParams.size(); ++i) { NValue& prevParam = parameterContainer[m_otherParamIdxs[i]]; if (lastParams[i].compare(prevParam) != VALUE_COMPARE_EQUAL) { lastParams[i] = prevParam.copyNValue(); paramsChanged = true; } } if (paramsChanged) { // If parameters have changed since the last execution, // the cached result of the prior execution is obsolete. // In particular, it should not be mistaken for the correct result for the current // parameters in the event that the current execution fails. // This subquery context will be restored to validity when its new result is set // after execution succeeds. context->invalidateResult(); } else { // If the parameters haven't changed since the last execution, reuse the known result. return context->getResult(); } } // Out of luck. Need to run the executors. Clean up the output tables with cached results exeContext->cleanupExecutorsForSubquery(m_subqueryId); exeContext->executeExecutors(m_subqueryId); if (context == NULL) { // Preserve the value for the next run. Only 'other' parameters need to be copied std::vector<NValue> lastParams; lastParams.reserve(m_otherParamIdxs.size()); for (size_t i = 0; i < m_otherParamIdxs.size(); ++i) { NValue& prevParam = parameterContainer[m_otherParamIdxs[i]]; lastParams.push_back(prevParam.copyNValue()); } context = exeContext->setSubqueryContext(m_subqueryId, lastParams); } // Update the cached result for the current params. All params are already updated NValue retval = ValueFactory::getIntegerValue(m_subqueryId); context->setResult(retval); return retval; }