Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support added for cascading row removal #561

Merged
merged 16 commits into from
Oct 31, 2014
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@

### API breaking changes:

* `Query::tableview()` removed as it might lead to wrong results
e.g., when sorting a sorted tableview.
* `Query::tableview()` removed as it might lead to wrong results e.g., when
sorting a sorted tableview.

### Enhancements:

* Support added for cascading row removal. See `Descriptor::set_link_type()` for
details. All previsouly created link columns will effectively have link-type
'weak'.
* Make the durability level settable in the `SharedGroup` constructor and
`open()` overloads taking a `Replication`.

Expand Down
12 changes: 12 additions & 0 deletions src/tightdb/column.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,18 @@ void ColumnBase::update_from_parent(size_t old_baseline) TIGHTDB_NOEXCEPT
}


void ColumnBase::erase_cascade(std::size_t, size_t, cascade_rows&) const
{
// No-op by default
}


void ColumnBase::clear_cascade(std::size_t, size_t, cascade_rows&) const
{
// No-op by default
}


#ifdef TIGHTDB_DEBUG

void ColumnBase::Verify(const Table&, size_t) const
Expand Down
40 changes: 40 additions & 0 deletions src/tightdb/column.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <stdint.h> // unint8_t etc
#include <cstdlib> // std::size_t
#include <vector>

#include <tightdb/util/unique_ptr.hpp>
#include <tightdb/array.hpp>
Expand Down Expand Up @@ -159,6 +160,40 @@ class ColumnBase {
/// table and link list accessors stay valid across a commit.
virtual void update_from_parent(std::size_t old_baseline) TIGHTDB_NOEXCEPT;

struct cascade_row {
size_t table_ndx, row_ndx;

/// Trivial lexicographic order
bool operator<(const cascade_row&r) const TIGHTDB_NOEXCEPT;
};

typedef std::vector<cascade_row> cascade_rows;

//@{

/// erase_cascade() is called when the row at \a row_ndx is about to be
/// removed, and clear_cascade() when the column is about to be cleared (all
/// rows removed). Link columns must override these functions and descend
/// into target rows. If a target row has no other strong links to it, that
/// target row must be added to \a rows, and the cascade must recurse from
/// that point. Rows that are added to \a rows will eventually be
/// cascade-removed.
///
/// \param stop_on_table_ndx If not equal to tightdb::npos, then do not
/// recurse into rows of the group-level table with that index in the group.
///
/// \param rows A sorted list of rows. Each entry is a pair (table_ndx,
/// row_ndx), where table_ndx is the index withing the group of a
/// group-level table. Insertions must therfore respect this order.

virtual void erase_cascade(std::size_t row_ndx, std::size_t stop_on_table_ndx,
cascade_rows& rows) const;

virtual void clear_cascade(std::size_t table_ndx, std::size_t num_rows,
cascade_rows& rows) const;

//@}

void discard_child_accessors() TIGHTDB_NOEXCEPT;

/// For columns that are able to contain subtables, this function returns
Expand Down Expand Up @@ -492,6 +527,11 @@ inline void ColumnBase::set_search_index_allow_duplicate_values(bool) TIGHTDB_NO
{
}

inline bool ColumnBase::cascade_row::operator<(const cascade_row& r) const TIGHTDB_NOEXCEPT
{
return table_ndx < r.table_ndx || (table_ndx == r.table_ndx && row_ndx < r.row_ndx);
}

inline void ColumnBase::discard_child_accessors() TIGHTDB_NOEXCEPT
{
do_discard_child_accessors();
Expand Down
39 changes: 39 additions & 0 deletions src/tightdb/column_link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*
**************************************************************************/

#include <algorithm>

#include "column_link.hpp"

using namespace std;
Expand All @@ -38,6 +40,7 @@ void ColumnLink::set_link(size_t row_ndx, size_t target_row_ndx)
m_backlink_column->add_backlink(target_row_ndx, row_ndx);
}


void ColumnLink::nullify_link(size_t row_ndx)
{
size_t ref = ColumnLinkBase::get(row_ndx);
Expand All @@ -50,6 +53,7 @@ void ColumnLink::nullify_link(size_t row_ndx)
ColumnLinkBase::set(row_ndx, 0);
}


void ColumnLink::remove_backlinks(size_t row_ndx)
{
size_t ref = ColumnLinkBase::get(row_ndx);
Expand All @@ -59,6 +63,7 @@ void ColumnLink::remove_backlinks(size_t row_ndx)
}
}


void ColumnLink::move_last_over(size_t target_row_ndx, size_t last_row_ndx)
{
TIGHTDB_ASSERT(target_row_ndx < last_row_ndx);
Expand All @@ -78,6 +83,7 @@ void ColumnLink::move_last_over(size_t target_row_ndx, size_t last_row_ndx)
ColumnLinkBase::move_last_over(target_row_ndx, last_row_ndx);
}


void ColumnLink::erase(size_t row_ndx, bool is_last)
{
TIGHTDB_ASSERT(is_last);
Expand All @@ -88,6 +94,7 @@ void ColumnLink::erase(size_t row_ndx, bool is_last)
ColumnLinkBase::erase(row_ndx, is_last);
}


void ColumnLink::clear()
{
size_t count = size();
Expand All @@ -97,6 +104,38 @@ void ColumnLink::clear()
}


void ColumnLink::erase_cascade(size_t row_ndx, size_t stop_on_table_ndx, cascade_rows& rows) const
{
if (m_weak_links || is_null_link(row_ndx))
return;

size_t target_table_ndx = m_target_table->get_index_in_group();
if (target_table_ndx == stop_on_table_ndx)
return;

size_t target_row_ndx = get_link(row_ndx);
erase_cascade_target_row(target_table_ndx, target_row_ndx, stop_on_table_ndx, rows); // Throws
}


void ColumnLink::clear_cascade(size_t table_ndx, size_t num_rows, cascade_rows& rows) const
{
if (m_weak_links)
return;

size_t target_table_ndx = m_target_table->get_index_in_group();
if (target_table_ndx == table_ndx)
return;

for (size_t i = 0; i < num_rows; ++i) {
if (is_null_link(i))
continue;
size_t target_row_ndx = get_link(i);
erase_cascade_target_row(target_table_ndx, target_row_ndx, table_ndx, rows); // Throws
}
}


#ifdef TIGHTDB_DEBUG

void ColumnLink::Verify(const Table& table, size_t col_ndx) const
Expand Down
3 changes: 3 additions & 0 deletions src/tightdb/column_link.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class ColumnLink: public ColumnLinkBase {
void erase(std::size_t, bool) TIGHTDB_OVERRIDE;
void move_last_over(std::size_t, std::size_t) TIGHTDB_OVERRIDE;

void erase_cascade(std::size_t, std::size_t, cascade_rows&) const TIGHTDB_OVERRIDE;
void clear_cascade(std::size_t, std::size_t, cascade_rows&) const TIGHTDB_OVERRIDE;

#ifdef TIGHTDB_DEBUG
void Verify(const Table&, std::size_t) const TIGHTDB_OVERRIDE;
#endif
Expand Down
33 changes: 33 additions & 0 deletions src/tightdb/column_link_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@ using namespace std;
using namespace tightdb;


void ColumnLinkBase::erase_cascade_target_row(size_t target_table_ndx, size_t target_row_ndx,
size_t stop_on_table_ndx, cascade_rows& rows) const
{
// Stop if there are other strong links to this row (this scheme fails to
// discover orphaned cycles)
typedef _impl::TableFriend tf;
size_t num_strong_backlinks = tf::get_num_strong_backlinks(*m_target_table, target_row_ndx);
if (num_strong_backlinks > 1)
return;

// Stop if the target row was already visited
cascade_row target_row;
target_row.table_ndx = target_table_ndx;
target_row.row_ndx = target_row_ndx;
typedef cascade_rows::iterator iter;
iter i = ::upper_bound(rows.begin(), rows.end(), target_row);
if (i != rows.end())
return;

// Recurse
rows.insert(i, target_row); // Throws
tf::erase_cascade(*m_target_table, target_row_ndx, stop_on_table_ndx, rows); // Throws
}


void ColumnLinkBase::refresh_accessor_tree(size_t col_ndx, const Spec& spec)
{
Column::refresh_accessor_tree(col_ndx, spec); // Throws
ColumnAttr attr = spec.get_column_attr(col_ndx);
m_weak_links = (attr & col_attr_StrongLinks) == 0;
}


#ifdef TIGHTDB_DEBUG

void ColumnLinkBase::Verify(const Table& table, size_t col_ndx) const
Expand Down
22 changes: 21 additions & 1 deletion src/tightdb/column_linkbase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class ColumnLinkBase: public Column {
public:
~ColumnLinkBase() TIGHTDB_NOEXCEPT TIGHTDB_OVERRIDE;

bool get_weak_links() const TIGHTDB_NOEXCEPT;
void set_weak_links(bool) TIGHTDB_NOEXCEPT;

Table& get_target_table() const TIGHTDB_NOEXCEPT;
void set_target_table(Table&) TIGHTDB_NOEXCEPT;
ColumnBackLink& get_backlink_column() const TIGHTDB_NOEXCEPT;
Expand All @@ -40,6 +43,11 @@ class ColumnLinkBase: public Column {
virtual void do_update_link(std::size_t row_ndx, std::size_t old_target_row_ndx,
std::size_t new_target_row_ndx) = 0;

void erase_cascade_target_row(std::size_t target_table_ndx, std::size_t target_row_ndx,
std::size_t stop_on_table_ndx, cascade_rows&) const;

void refresh_accessor_tree(std::size_t, const Spec&) TIGHTDB_OVERRIDE;

void adj_accessors_insert_rows(std::size_t, std::size_t) TIGHTDB_NOEXCEPT TIGHTDB_OVERRIDE;
void adj_accessors_erase_row(std::size_t) TIGHTDB_NOEXCEPT TIGHTDB_OVERRIDE;
void adj_accessors_move(std::size_t, std::size_t) TIGHTDB_NOEXCEPT TIGHTDB_OVERRIDE;
Expand All @@ -55,6 +63,7 @@ class ColumnLinkBase: public Column {
protected:
TableRef m_target_table;
ColumnBackLink* m_backlink_column;
bool m_weak_links; // True if these links are weak (not strong)

// Create unattached root array aaccessor.
ColumnLinkBase(Allocator&, ref_type);
Expand All @@ -67,14 +76,25 @@ class ColumnLinkBase: public Column {

inline ColumnLinkBase::ColumnLinkBase(Allocator& alloc, ref_type ref):
Column(alloc, ref),
m_backlink_column(0)
m_backlink_column(0),
m_weak_links(false)
{
}

inline ColumnLinkBase::~ColumnLinkBase() TIGHTDB_NOEXCEPT
{
}

inline bool ColumnLinkBase::get_weak_links() const TIGHTDB_NOEXCEPT
{
return m_weak_links;
}

inline void ColumnLinkBase::set_weak_links(bool value) TIGHTDB_NOEXCEPT
{
m_weak_links = value;
}

inline Table& ColumnLinkBase::get_target_table() const TIGHTDB_NOEXCEPT
{
return *m_target_table;
Expand Down
Loading