Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .unreleased/pr_9736
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixes: #9736 Do logical sparse index comparison
125 changes: 123 additions & 2 deletions src/ts_catalog/compression_settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ static ScanTupleResult compression_settings_tuple_update(TupleInfo *ti, void *da
static HeapTuple compression_settings_formdata_make_tuple(const FormData_compression_settings *fd,
TupleDesc desc);
static Bitmapset *resolve_columns_to_attnos(List *column_names, Oid relid);
static bool sparse_index_values_equal(List *left, List *right);
static bool sparse_index_object_equal(SparseIndexSettingsObject *left,
SparseIndexSettingsObject *right);

/*
* Compare two compression settings for equality
Expand All @@ -38,7 +41,7 @@ ts_compression_settings_equal(const CompressionSettings *left, const Compression
ts_array_equal(left->fd.orderby, right->fd.orderby) &&
ts_array_equal(left->fd.orderby_desc, right->fd.orderby_desc) &&
ts_array_equal(left->fd.orderby_nullsfirst, right->fd.orderby_nullsfirst) &&
ts_jsonb_equal(left->fd.index, right->fd.index);
ts_sparse_index_equal(left->fd.index, right->fd.index);
}

/*
Expand All @@ -62,7 +65,125 @@ ts_compression_settings_equal_with_defaults(const CompressionSettings *ht,
ts_array_equal(ht->fd.orderby_desc, chunk->fd.orderby_desc)) &&
(ht->fd.orderby_nullsfirst == NULL ||
ts_array_equal(ht->fd.orderby_nullsfirst, chunk->fd.orderby_nullsfirst)) &&
(ht->fd.index == NULL || ts_jsonb_equal(ht->fd.index, chunk->fd.index));
(ht->fd.index == NULL || ts_sparse_index_equal(ht->fd.index, chunk->fd.index));
}

/*
* Compare two string value lists for equality (order-sensitive).
*/
static bool
sparse_index_values_equal(List *left, List *right)
{
if (list_length(left) != list_length(right))
{
return false;
}

ListCell *lc_left, *lc_right;
forboth (lc_left, left, lc_right, right)
{
if (strcmp((const char *) lfirst(lc_left), (const char *) lfirst(lc_right)) != 0)
{
return false;
}
}
return true;
}

/*
* Compare two sparse index objects for equality.
* Two objects are equal if they have the same pairs with the same values.
*/
static bool
sparse_index_object_equal(SparseIndexSettingsObject *left, SparseIndexSettingsObject *right)
{
if (list_length(left->pairs) != list_length(right->pairs))
{
return false;
}

foreach_ptr(SparseIndexSettingsPair, lpair, left->pairs)
{
bool found = false;
foreach_ptr(SparseIndexSettingsPair, rpair, right->pairs)
{
if (strcmp(lpair->key, rpair->key) == 0)
{
if (!sparse_index_values_equal(lpair->values, rpair->values))
{
return false;
}
found = true;
break;
}
}
if (!found)
{
return false;
}
}
return true;
}

/*
* Compare two sparse index JSONB settings for equality, independent of
* the order of objects in the array. Each object is matched by its
* key-value pairs (type, column, source).
*/
bool
ts_sparse_index_equal(const Jsonb *left, const Jsonb *right)
{
if (left == right)
{
return true;
}
if (left == NULL || right == NULL)
{
return false;
}

SparseIndexSettings *left_settings = ts_convert_to_sparse_index_settings((Jsonb *) left);
SparseIndexSettings *right_settings = ts_convert_to_sparse_index_settings((Jsonb *) right);

int n_left = list_length(left_settings->objects);
int n_right = list_length(right_settings->objects);

if (n_left != n_right)
{
ts_free_sparse_index_settings(left_settings);
ts_free_sparse_index_settings(right_settings);
return false;
}

/* for tracking */
bool *already_found = palloc0(sizeof(bool) * n_left);
bool equal = true;

foreach_ptr(SparseIndexSettingsObject, lobj, left_settings->objects)
{
int ri = 0;
bool found = false;
foreach_ptr(SparseIndexSettingsObject, robj, right_settings->objects)
{
if (!already_found[ri] && sparse_index_object_equal(lobj, robj))
{
already_found[ri] = true;
found = true;
break;
}
ri++;
}
if (!found)
{
equal = false;
break;
}
}

pfree(already_found);
ts_free_sparse_index_settings(left_settings);
ts_free_sparse_index_settings(right_settings);
return equal;
}

CompressionSettings *
Expand Down
1 change: 1 addition & 0 deletions src/ts_catalog/compression_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ TSDLLEXPORT bool ts_compression_settings_equal(const CompressionSettings *left,
const CompressionSettings *right);
TSDLLEXPORT bool ts_compression_settings_equal_with_defaults(const CompressionSettings *ht,
const CompressionSettings *chunk);
TSDLLEXPORT bool ts_sparse_index_equal(const Jsonb *left, const Jsonb *right);

TSDLLEXPORT int ts_compression_settings_update(CompressionSettings *settings);
TSDLLEXPORT void ts_compression_settings_rename_column_cascade(Oid parent_relid, const char *old,
Expand Down
133 changes: 133 additions & 0 deletions test/src/test_compression_settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,143 @@ test_convert_to_sparse_index_settings()
}
}

static void
test_sparse_index_equal()
{
/* Both NULL — equal */
TestAssertBoolEq(ts_sparse_index_equal(NULL, NULL), true);

/* One NULL, one non-NULL — not equal */
{
Jsonb *jb =
cstring_to_jsonb("[{\"type\": \"bloom\", \"column\": \"x\", \"source\": \"config\"}]");
TestAssertBoolEq(ts_sparse_index_equal(NULL, jb), false);
TestAssertBoolEq(ts_sparse_index_equal(jb, NULL), false);
pfree(jb);
}

/* Identical JSONB — equal */
{
Jsonb *jb = cstring_to_jsonb(
"[{\"type\": \"bloom\", \"column\": \"x\", \"source\": \"config\"}, "
"{\"type\": \"minmax\", \"column\": \"ts\", \"source\": \"orderby\"}]");
TestAssertBoolEq(ts_sparse_index_equal(jb, jb), true);
pfree(jb);
}

/* Same objects, different array order — equal */
{
Jsonb *a = cstring_to_jsonb(
"[{\"type\": \"bloom\", \"column\": \"x\", \"source\": \"config\"}, "
"{\"type\": \"minmax\", \"column\": \"ts\", \"source\": \"orderby\"}]");
Jsonb *b = cstring_to_jsonb(
"[{\"type\": \"minmax\", \"column\": \"ts\", \"source\": \"orderby\"}, "
"{\"type\": \"bloom\", \"column\": \"x\", \"source\": \"config\"}]");
TestAssertBoolEq(ts_sparse_index_equal(a, b), true);
pfree(a);
pfree(b);
}

/* Different column value — not equal */
{
Jsonb *a =
cstring_to_jsonb("[{\"type\": \"bloom\", \"column\": \"x\", \"source\": \"config\"}]");
Jsonb *b =
cstring_to_jsonb("[{\"type\": \"bloom\", \"column\": \"y\", \"source\": \"config\"}]");
TestAssertBoolEq(ts_sparse_index_equal(a, b), false);
pfree(a);
pfree(b);
}

/* Different number of objects — not equal */
{
Jsonb *a =
cstring_to_jsonb("[{\"type\": \"bloom\", \"column\": \"x\", \"source\": \"config\"}]");
Jsonb *b = cstring_to_jsonb(
"[{\"type\": \"bloom\", \"column\": \"x\", \"source\": \"config\"}, "
"{\"type\": \"minmax\", \"column\": \"ts\", \"source\": \"orderby\"}]");
TestAssertBoolEq(ts_sparse_index_equal(a, b), false);
pfree(a);
pfree(b);
}

/* Composite bloom columns, same order — equal */
{
Jsonb *a = cstring_to_jsonb(
"[{\"type\": \"bloom\", \"column\": [\"a\", \"b\"], \"source\": \"config\"}]");
Jsonb *b = cstring_to_jsonb(
"[{\"type\": \"bloom\", \"column\": [\"a\", \"b\"], \"source\": \"config\"}]");
TestAssertBoolEq(ts_sparse_index_equal(a, b), true);
pfree(a);
pfree(b);
}

/* Composite bloom columns, different column order — not equal. */
/* Should not be possible but keep this test to flag if something breaks this logic */
{
Jsonb *a = cstring_to_jsonb(
"[{\"type\": \"bloom\", \"column\": [\"a\", \"b\"], \"source\": \"config\"}]");
Jsonb *b = cstring_to_jsonb(
"[{\"type\": \"bloom\", \"column\": [\"b\", \"a\"], \"source\": \"config\"}]");
TestAssertBoolEq(ts_sparse_index_equal(a, b), false);
pfree(a);
pfree(b);
}

/* Different type — not equal */
{
Jsonb *a =
cstring_to_jsonb("[{\"type\": \"bloom\", \"column\": \"x\", \"source\": \"config\"}]");
Jsonb *b =
cstring_to_jsonb("[{\"type\": \"minmax\", \"column\": \"x\", \"source\": \"config\"}]");
TestAssertBoolEq(ts_sparse_index_equal(a, b), false);
pfree(a);
pfree(b);
}

Comment thread
Poroma-Banerjee marked this conversation as resolved.
/* Extra keys — not equal */
{
Jsonb *a =
cstring_to_jsonb("[{\"type\": \"bloom\", \"column\": \"x\", \"source\": \"config\"}]");
Jsonb *b = cstring_to_jsonb(
"[{\"type\": \"bloom\", \"column\": \"x\", \"source\": \"config\",\"foo\":\"bar\"}]");
TestAssertBoolEq(ts_sparse_index_equal(a, b), false);
pfree(a);
pfree(b);
}

/* Different source — not equal */
{
Jsonb *a = cstring_to_jsonb(
"[{\"type\": \"minmax\", \"column\": \"ts\", \"source\": \"config\"}]");
Jsonb *b = cstring_to_jsonb(
"[{\"type\": \"minmax\", \"column\": \"ts\", \"source\": \"orderby\"}]");
TestAssertBoolEq(ts_sparse_index_equal(a, b), false);
pfree(a);
pfree(b);
}

/* Three objects shuffled — equal */
{
Jsonb *a = cstring_to_jsonb(
"[{\"type\": \"bloom\", \"column\": \"a\", \"source\": \"config\"}, "
"{\"type\": \"bloom\", \"column\": \"b\", \"source\": \"config\"}, "
"{\"type\": \"minmax\", \"column\": \"ts\", \"source\": \"orderby\"}]");
Jsonb *b = cstring_to_jsonb(
"[{\"type\": \"minmax\", \"column\": \"ts\", \"source\": \"orderby\"}, "
"{\"type\": \"bloom\", \"column\": \"b\", \"source\": \"config\"}, "
"{\"type\": \"bloom\", \"column\": \"a\", \"source\": \"config\"}]");
TestAssertBoolEq(ts_sparse_index_equal(a, b), true);
pfree(a);
pfree(b);
}
}

TS_TEST_FN(ts_test_compression_settings)
{
test_alter_table_rename_column_effect_jsonb();
test_alter_table_drop_column_effect_jsonb();
test_convert_to_sparse_index_settings();
test_sparse_index_equal();
PG_RETURN_VOID();
}
Loading
Loading