Skip to content

Commit

Permalink
SetIntUnique, SetStringUnique
Browse files Browse the repository at this point in the history
  • Loading branch information
simonask committed Oct 6, 2015
1 parent 19650c8 commit 1e9cb7f
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 3 deletions.
10 changes: 10 additions & 0 deletions src/realm/group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,11 @@ class Group::TransactAdvancer {
return true; // No-op
}

bool set_int_unique(size_t, size_t, int_fast64_t) noexcept
{
return true; // No-op
}

bool set_bool(size_t, size_t, bool) noexcept
{
return true; // No-op
Expand All @@ -1072,6 +1077,11 @@ class Group::TransactAdvancer {
return true; // No-op
}

bool set_string_unique(size_t, size_t, StringData) noexcept
{
return true; // No-op
}

bool set_binary(size_t, size_t, BinaryData) noexcept
{
return true; // No-op
Expand Down
72 changes: 72 additions & 0 deletions src/realm/impl/transact_log.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ enum Instruction {
instr_RenameGroupLevelTable = 3,
instr_SelectTable = 4,
instr_SetInt = 5,
instr_SetIntUnique = 30,
instr_SetBool = 6,
instr_SetFloat = 7,
instr_SetDouble = 8,
instr_SetString = 9,
instr_SetStringUnique = 31,
instr_SetBinary = 10,
instr_SetDateTime = 11,
instr_SetTable = 12,
Expand Down Expand Up @@ -137,10 +139,12 @@ class NullInstructionObserver {
bool erase_rows(size_t, size_t, size_t, bool) { return true; }
bool clear_table() { return true; }
bool set_int(std::size_t, std::size_t, int_fast64_t) { return true; }
bool set_int_unique(std::size_t, std::size_t, int_fast64_t) { return true; }
bool set_bool(std::size_t, std::size_t, bool) { return true; }
bool set_float(std::size_t, std::size_t, float) { return true; }
bool set_double(std::size_t, std::size_t, double) { return true; }
bool set_string(std::size_t, std::size_t, StringData) { return true; }
bool set_string_unique(std::size_t, std::size_t, StringData) { return true; }
bool set_binary(std::size_t, std::size_t, BinaryData) { return true; }
bool set_date_time(std::size_t, std::size_t, DateTime) { return true; }
bool set_table(std::size_t, std::size_t) { return true; }
Expand Down Expand Up @@ -195,10 +199,12 @@ class TransactLogEncoder {
bool unordered);
bool clear_table();
bool set_int(std::size_t col_ndx, std::size_t row_ndx, int_fast64_t);
bool set_int_unique(std::size_t col_ndx, std::size_t row_ndx, int_fast64_t);
bool set_bool(std::size_t col_ndx, std::size_t row_ndx, bool);
bool set_float(std::size_t col_ndx, std::size_t row_ndx, float);
bool set_double(std::size_t col_ndx, std::size_t row_ndx, double);
bool set_string(std::size_t col_ndx, std::size_t row_ndx, StringData);
bool set_string_unique(std::size_t col_ndx, std::size_t row_ndx, StringData);
bool set_binary(std::size_t col_ndx, std::size_t row_ndx, BinaryData);
bool set_date_time(std::size_t col_ndx, std::size_t row_ndx, DateTime);
bool set_table(std::size_t col_ndx, std::size_t row_ndx);
Expand Down Expand Up @@ -294,10 +300,12 @@ class TransactLogConvenientEncoder {
void rename_column(const Descriptor&, std::size_t col_ndx, StringData name);

void set_int(const Table*, std::size_t col_ndx, std::size_t ndx, int_fast64_t value);
void set_int_unique(const Table*, std::size_t col_ndx, std::size_t ndx, int_fast64_t value);
void set_bool(const Table*, std::size_t col_ndx, std::size_t ndx, bool value);
void set_float(const Table*, std::size_t col_ndx, std::size_t ndx, float value);
void set_double(const Table*, std::size_t col_ndx, std::size_t ndx, double value);
void set_string(const Table*, std::size_t col_ndx, std::size_t ndx, StringData value);
void set_string_unique(const Table*, std::size_t col_ndx, std::size_t ndx, StringData value);
void set_binary(const Table*, std::size_t col_ndx, std::size_t ndx, BinaryData value);
void set_date_time(const Table*, std::size_t col_ndx, std::size_t ndx, DateTime value);
void set_table(const Table*, std::size_t col_ndx, std::size_t ndx);
Expand Down Expand Up @@ -886,6 +894,19 @@ inline void TransactLogConvenientEncoder::set_int(const Table* t, std::size_t co
m_encoder.set_int(col_ndx, ndx, value); // Throws
}

inline bool TransactLogEncoder::set_int_unique(std::size_t col_ndx, std::size_t ndx, int_fast64_t value)
{
simple_cmd(instr_SetIntUnique, util::tuple(col_ndx, ndx, value));
return true;
}

inline void TransactLogConvenientEncoder::set_int_unique(const Table* t, std::size_t col_ndx,
std::size_t ndx, int_fast64_t value)
{
select_table(t); // Throws
m_encoder.set_int_unique(col_ndx, ndx, value); // Throws
}

inline bool TransactLogEncoder::set_bool(std::size_t col_ndx, std::size_t ndx, bool value)
{
simple_cmd(instr_SetBool, util::tuple(col_ndx, ndx, value));
Expand Down Expand Up @@ -943,6 +964,24 @@ inline void TransactLogConvenientEncoder::set_string(const Table* t, std::size_t
m_encoder.set_string(col_ndx, ndx, value); // Throws
}

inline bool TransactLogEncoder::set_string_unique(std::size_t col_ndx, std::size_t ndx, StringData value)
{
if (value.is_null()) {
set_null(col_ndx, ndx); // Throws
}
else {
string_cmd(instr_SetStringUnique, col_ndx, ndx, value.data(), value.size()); // Throws
}
return true;
}

inline void TransactLogConvenientEncoder::set_string_unique(const Table* t, std::size_t col_ndx,
std::size_t ndx, StringData value)
{
select_table(t); // Throws
m_encoder.set_string_unique(col_ndx, ndx, value); // Throws
}

inline bool TransactLogEncoder::set_binary(std::size_t col_ndx, std::size_t ndx, BinaryData value)
{
if (value.is_null()) {
Expand Down Expand Up @@ -1324,6 +1363,17 @@ void TransactLogParser::parse_one(InstructionHandler& handler)
parser_error();
return;
}
case instr_SetIntUnique: {
std::size_t col_ndx = read_int<std::size_t>(); // Throws
std::size_t row_ndx = read_int<std::size_t>(); // Throws
// FIXME: Don't depend on the existence of int64_t,
// but don't allow values to use more than 64 bits
// either.
int_fast64_t value = read_int<int64_t>(); // Throws
if (!handler.set_int_unique(col_ndx, row_ndx, value)) // Throws
parser_error();
return;
}
case instr_SetBool: {
std::size_t col_ndx = read_int<std::size_t>(); // Throws
std::size_t row_ndx = read_int<std::size_t>(); // Throws
Expand Down Expand Up @@ -1356,6 +1406,14 @@ void TransactLogParser::parse_one(InstructionHandler& handler)
parser_error();
return;
}
case instr_SetStringUnique: {
std::size_t col_ndx = read_int<std::size_t>(); // Throws
std::size_t row_ndx = read_int<std::size_t>(); // Throws
StringData value = read_string(m_string_buffer); // Throws
if (!handler.set_string_unique(col_ndx, row_ndx, value)) // Throws
parser_error();
return;
}
case instr_SetBinary: {
std::size_t col_ndx = read_int<std::size_t>(); // Throws
std::size_t row_ndx = read_int<std::size_t>(); // Throws
Expand Down Expand Up @@ -1914,6 +1972,13 @@ class TransactReverser {
return true;
}

bool set_int_unique(std::size_t col_ndx, std::size_t row_ndx, int_fast64_t value)
{
m_encoder.set_int_unique(col_ndx, row_ndx, value);
append_instruction();
return true;
}

bool set_bool(std::size_t col_ndx, std::size_t row_ndx, bool value)
{
m_encoder.set_bool(col_ndx, row_ndx, value);
Expand Down Expand Up @@ -1942,6 +2007,13 @@ class TransactReverser {
return true;
}

bool set_string_unique(std::size_t col_ndx, std::size_t row_ndx, StringData value)
{
m_encoder.set_string_unique(col_ndx, row_ndx, value);
append_instruction();
return true;
}

bool set_binary(std::size_t col_ndx, std::size_t row_ndx, BinaryData value)
{
m_encoder.set_binary(col_ndx, row_ndx, value);
Expand Down
26 changes: 26 additions & 0 deletions src/realm/replication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ class Replication::TransactLogApplier {
return false;
}

bool set_int_unique(size_t col_ndx, size_t row_ndx, int_fast64_t value)
{
if (REALM_LIKELY(check_set_cell(col_ndx, row_ndx))) {
#ifdef REALM_DEBUG
if (m_log)
*m_log << "table->set_int_unique("<<col_ndx<<", "<<row_ndx<<", "<<value<<")\n";

This comment has been minimized.

Copy link
@teotwaki

teotwaki Nov 24, 2015

Contributor

Spacing around stream operators.

#endif
m_table->set_int_unique(col_ndx, row_ndx, value); // Throws
return true;
}
return false;
}

bool set_bool(size_t col_ndx, size_t row_ndx, bool value)
{
if (REALM_LIKELY(check_set_cell(col_ndx, row_ndx))) {
Expand Down Expand Up @@ -110,6 +123,19 @@ class Replication::TransactLogApplier {
return false;
}

bool set_string_unique(size_t col_ndx, size_t row_ndx, StringData value)
{
if (REALM_LIKELY(check_set_cell(col_ndx, row_ndx))) {
#ifdef REALM_DEBUG
if (m_log)
*m_log << "table->set_string_unique("<<col_ndx<<", "<<row_ndx<<", "<<value<<")\n";

This comment has been minimized.

Copy link
@teotwaki

teotwaki Nov 24, 2015

Contributor

Spacing around stream operators.

This comment has been minimized.

Copy link
@simonask

simonask Nov 24, 2015

Author Contributor

This just follows the pattern used in the rest of the file. :)

This comment has been minimized.

Copy link
@teotwaki

teotwaki Nov 24, 2015

Contributor

So we get castigated when we submit PRs that improves things across the whole codebase, but we can't improve things in a file we're working on because we don't want to change the file's "patterns"?

Seems a bit like we're setting ourselves up to never improve anything. Also, the file appears to be inconsistent with itself: there's spacing around the stream operator in the first half of the line, but not in the latter.

Anyway, not a big deal or showstopper for the content of this PR.

This comment has been minimized.

Copy link
@simonask

simonask Nov 24, 2015

Author Contributor

Too many "we"s here. I don't mind large, simple improvements, so I think the accusation is misdirected. :-)

The question for me is more whether I want to expend the time to fix it, so it's a matter of finding an excuse (where following a pattern in my mind is perfectly valid), more than some golden philosophy...

#endif
m_table->set_string_unique(col_ndx, row_ndx, value); // Throws
return true;
}
return false;
}

bool set_binary(size_t col_ndx, size_t row_ndx, BinaryData value)
{
if (REALM_LIKELY(check_set_cell(col_ndx, row_ndx))) {
Expand Down
64 changes: 64 additions & 0 deletions src/realm/table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2275,6 +2275,36 @@ void Table::set_int(size_t col_ndx, size_t ndx, int_fast64_t value)
}


void Table::set_int_unique(size_t col_ndx, size_t ndx, int_fast64_t value)
{
REALM_ASSERT_3(col_ndx, <, get_column_count());
REALM_ASSERT_3(ndx, <, m_size);
bump_version();

if (!has_search_index(col_ndx)) {
throw LogicError{LogicError::no_search_index};
}

if (is_nullable(col_ndx)) {
auto& col = get_column_int_null(col_ndx);
if (col.find_first(value) != not_found) {
throw LogicError{LogicError::unique_constraint_violation};
}
col.set(ndx, value);
}
else {
auto& col = get_column(col_ndx);
if (col.find_first(value) != not_found) {
throw LogicError{LogicError::unique_constraint_violation};
}
col.set(ndx, value);
}

if (Replication* repl = get_repl())
repl->set_int_unique(this, col_ndx, ndx, value); // Throws
}


bool Table::get_bool(size_t col_ndx, size_t ndx) const noexcept
{
REALM_ASSERT_3(col_ndx, <, get_column_count());
Expand Down Expand Up @@ -2455,6 +2485,40 @@ void Table::set_string(size_t col_ndx, size_t ndx, StringData value)
}


void Table::set_string_unique(size_t col_ndx, size_t ndx, StringData value)
{
if (REALM_UNLIKELY(value.size() > max_string_size))
throw LogicError(LogicError::string_too_big);
if (REALM_UNLIKELY(!is_attached()))
throw LogicError(LogicError::detached_accessor);
if (REALM_UNLIKELY(ndx >= m_size))
throw LogicError(LogicError::row_index_out_of_range);
// For a degenerate subtable, `m_cols.size()` is zero, even when it has a
// column, however, the previous row index check guarantees that `m_size >
// 0`, and since `m_size` is also zero for a degenerate subtable, the table
// cannot be degenerate if we got this far.
if (REALM_UNLIKELY(col_ndx >= m_cols.size()))
throw LogicError(LogicError::column_index_out_of_range);

if (!is_nullable(col_ndx) && value.is_null())
throw LogicError(LogicError::column_not_nullable);

if (!has_search_index(col_ndx))
throw LogicError(LogicError::no_search_index);

bump_version();

StringColumn& col = get_column_string(col_ndx);
if (col.find_first(value) != not_found)
throw LogicError(LogicError::unique_constraint_violation);

col.set_string(ndx, value); // Throws

if (Replication* repl = get_repl())
repl->set_string_unique(this, col_ndx, ndx, value); // Throws
}


BinaryData Table::get_binary(size_t col_ndx, size_t ndx) const noexcept
{
REALM_ASSERT_3(col_ndx, <, m_columns.size());
Expand Down
12 changes: 9 additions & 3 deletions src/realm/table.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,20 +413,26 @@ class Table {
/// requirements also apply when calling set_mixed(). Passing an oversized
/// string or binary data value will cause an exception to be thrown.
///
/// It is an error to assign a value to a column that is part of a primary
/// key, if that would result in a violation the implied *unique constraint*
/// of that primary key. The consequenses of doing so are unspecified.
/// The 'unique' variants of these methods treats the given column as a
/// logical "primary key" column. It is an error to use these methods with
/// a column that does not have a search index. It is an error to assign a
/// value to a column that is part of a primary key, if that would result
/// in a violation the implied *unique constraint* of that primary key.
/// The consequenses of doing so are unspecified.
// FIXME: Specify the consequences of a primary key constraint violation.

static const std::size_t max_string_size = 0xFFFFF8 - Array::header_size - 1;
static const std::size_t max_binary_size = 0xFFFFF8 - Array::header_size;

void set_int(std::size_t column_ndx, std::size_t row_ndx, int_fast64_t value);
void set_int_unique(std::size_t column_ndx, std::size_t row_ndx, int_fast64_t value);
void set_bool(std::size_t column_ndx, std::size_t row_ndx, bool value);
void set_datetime(std::size_t column_ndx, std::size_t row_ndx, DateTime value);
template<class E> void set_enum(std::size_t column_ndx, std::size_t row_ndx, E value);
void set_float(std::size_t column_ndx, std::size_t row_ndx, float value);
void set_double(std::size_t column_ndx, std::size_t row_ndx, double value);
void set_string(std::size_t column_ndx, std::size_t row_ndx, StringData value);
void set_string_unique(std::size_t column_ndx, std::size_t row_ndx, StringData value);
void set_binary(std::size_t column_ndx, std::size_t row_ndx, BinaryData value);
void set_mixed(std::size_t column_ndx, std::size_t row_ndx, Mixed value);
void set_link(std::size_t column_ndx, std::size_t row_ndx, std::size_t target_row_ndx);
Expand Down
2 changes: 2 additions & 0 deletions test/test_lang_bind_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6417,10 +6417,12 @@ class NoOpTransactionLogParser {
bool link_list_move(size_t, size_t) { return false; }
bool link_list_swap(size_t, size_t) { return false; }
bool set_int(size_t, size_t, int_fast64_t) { return false; }
bool set_int_unique(size_t, size_t, int_fast64_t) { return false; }
bool set_bool(size_t, size_t, bool) { return false; }
bool set_float(size_t, size_t, float) { return false; }
bool set_double(size_t, size_t, double) { return false; }
bool set_string(size_t, size_t, StringData) { return false; }
bool set_string_unique(size_t, size_t, StringData) { return false; }
bool set_binary(size_t, size_t, BinaryData) { return false; }
bool set_date_time(size_t, size_t, DateTime) { return false; }
bool set_table(size_t, size_t) { return false; }
Expand Down
32 changes: 32 additions & 0 deletions test/test_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,38 @@ TEST(Table_IndexInteger)
}


TEST(Table_SetIntUnique)
{
Table table;
table.add_column(type_Int, "ints");
table.add_column(type_String, "strings");
table.add_empty_row(10);

CHECK_LOGIC_ERROR(table.set_int_unique(0, 0, 123), LogicError::no_search_index);
table.add_search_index(0);

table.set_int_unique(0, 0, 123);

CHECK_LOGIC_ERROR(table.set_int_unique(0, 1, 123), LogicError::unique_constraint_violation);
}


TEST(Table_SetStringUnique)
{
Table table;
table.add_column(type_Int, "ints");
table.add_column(type_String, "strings");
table.add_empty_row(10);

CHECK_LOGIC_ERROR(table.set_string_unique(1, 0, "foo"), LogicError::no_search_index);
table.add_search_index(1);

table.set_string_unique(1, 0, "bar");

CHECK_LOGIC_ERROR(table.set_string_unique(1, 1, "bar"), LogicError::unique_constraint_violation);
}


TEST(Table_Distinct)
{
TestTableEnum table;
Expand Down

0 comments on commit 1e9cb7f

Please sign in to comment.