void MaterializedViewMetadata::processTupleUpdate(TableTuple &oldTuple, TableTuple &newTuple) {
    // this approach is far from optimal, but should be technically correct
    processTupleDelete(oldTuple);
    processTupleInsert(newTuple);
}
MaterializedViewMetadata::MaterializedViewMetadata(
        PersistentTable *srcTable, PersistentTable *destTable, catalog::MaterializedViewInfo *metadata)
        : m_target(destTable), m_filterPredicate(NULL)
{
// DEBUG_STREAM_HERE("New mat view on source table " << srcTable->name() << " @" << srcTable << " view table " << m_target->name() << " @" << m_target);
    // best not to have to worry about the destination table disappearing out from under the source table that feeds it.
    m_target->incrementRefcount();
    srcTable->addMaterializedView(this);
    // try to load the predicate from the catalog view
    parsePredicate(metadata);

    // set up the group by columns from the catalog info
    m_groupByColumnCount = metadata->groupbycols().size();
    m_groupByColumns = new int32_t[m_groupByColumnCount];
    std::map<std::string, catalog::ColumnRef*>::const_iterator colRefIterator;
    for (colRefIterator = metadata->groupbycols().begin();
         colRefIterator != metadata->groupbycols().end();
         colRefIterator++)
    {
        int32_t grouping_order_offset = colRefIterator->second->index();
        m_groupByColumns[grouping_order_offset] = colRefIterator->second->column()->index();
    }

    // set up the mapping from input col to output col
    m_outputColumnCount = metadata->dest()->columns().size();
    m_outputColumnSrcTableIndexes = new int32_t[m_outputColumnCount];
    m_outputColumnAggTypes = new ExpressionType[m_outputColumnCount];
    std::map<std::string, catalog::Column*>::const_iterator colIterator;
    // iterate the source table
    for (colIterator = metadata->dest()->columns().begin(); colIterator != metadata->dest()->columns().end(); colIterator++) {
        const catalog::Column *destCol = colIterator->second;
        int destIndex = destCol->index();

        const catalog::Column *srcCol = destCol->matviewsource();

        if (srcCol) {
            m_outputColumnSrcTableIndexes[destIndex] = srcCol->index();
            m_outputColumnAggTypes[destIndex] = static_cast<ExpressionType>(destCol->aggregatetype());
        }
        else {
            m_outputColumnSrcTableIndexes[destIndex] = -1;
            m_outputColumnAggTypes[destIndex] = EXPRESSION_TYPE_INVALID;
        }
    }

    m_index = m_target->primaryKeyIndex();

    // When updateTupleWithSpecificIndexes needs to be called,
    // the context is lost that identifies which base table columns potentially changed.
    // So the minimal set of indexes that MIGHT need to be updated must include
    // any that are not solely based on primary key components.
    // Until the DDL compiler does this analysis and marks the indexes accordingly,
    // include all target table indexes except the actual primary key index on the group by columns.
    const std::vector<TableIndex*>& targetIndexes = m_target->allIndexes();
    BOOST_FOREACH(TableIndex *index, targetIndexes) {
        if (index != m_index) {
            m_updatableIndexList.push_back(index);
        }
    }

    allocateBackedTuples();

    // Catch up on pre-existing source tuples UNLESS target tuples have already been migrated in.
    if (( ! srcTable->isPersistentTableEmpty()) && m_target->isPersistentTableEmpty()) {
        TableTuple scannedTuple(srcTable->schema());
        TableIterator &iterator = srcTable->iterator();
        while (iterator.next(scannedTuple)) {
            processTupleInsert(scannedTuple, false);
        }
    }
}