Skip to content

Commit f04442a

Browse files
authored
Merge branch 'main' into ci-cd-optimize
2 parents 03ae046 + 82ad034 commit f04442a

10 files changed

Lines changed: 1221 additions & 122 deletions

cpp/deeplake_pg/extension_init.cpp

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ namespace pg {
4646
bool use_parallel_workers = false;
4747
bool use_deeplake_executor = true;
4848
bool explain_query_before_execute = false;
49-
bool ignore_primary_keys = true;
5049
bool print_runtime_stats = false;
5150
bool support_json_index = false;
5251
bool is_filter_pushdown_enabled = true;
@@ -133,17 +132,6 @@ void initialize_guc_parameters()
133132
nullptr // check_hook, assign_hook, show_hook
134133
);
135134

136-
DefineCustomBoolVariable("pg_deeplake.ignore_primary_keys",
137-
"If set to true, PRIMARY KEY constraints will be ignored during table creation.",
138-
nullptr, // optional long description
139-
&pg::ignore_primary_keys, // linked C variable
140-
true, // default value
141-
PGC_USERSET, // context (USERSET, SUSET, etc.)
142-
0, // flags
143-
nullptr,
144-
nullptr,
145-
nullptr // check_hook, assign_hook, show_hook
146-
);
147135

148136
DefineCustomBoolVariable("pg_deeplake.print_runtime_stats",
149137
"Enable runtime statistics printing for pg_deeplake operations.",
@@ -625,70 +613,6 @@ static void process_utility(PlannedStmt* pstmt,
625613
list_free_deep(stmt->options);
626614
stmt->options = NIL;
627615
}
628-
// Remove PRIMARY KEY constraints
629-
if (pg::ignore_primary_keys && deeplake_table && stmt->tableElts != nullptr) {
630-
List* new_table_elts = NIL;
631-
ListCell* lc = nullptr;
632-
bool has_primary_key = false;
633-
// Get table name from the CreateStmt
634-
std::string table_name = stmt->relation ? stmt->relation->relname : "";
635-
std::map<std::string, std::set<std::string>> primary_keys;
636-
foreach (lc, stmt->tableElts) {
637-
Node* element = (Node*)lfirst(lc);
638-
// Handle table-level PRIMARY KEY constraints
639-
if (IsA(element, Constraint)) {
640-
Constraint* constraint = (Constraint*)element;
641-
if (constraint->contype == CONSTR_PRIMARY) {
642-
has_primary_key = true;
643-
// Extract primary key column names
644-
if (constraint->keys != nullptr) {
645-
ListCell* key_lc = nullptr;
646-
foreach (key_lc, constraint->keys) {
647-
std::string col_name = strVal(lfirst(key_lc));
648-
elog(DEBUG1,
649-
"Removing table-level PRIMARY KEY constraint on table '%s' for column: %s",
650-
table_name.c_str(),
651-
col_name.c_str());
652-
primary_keys[table_name].insert(col_name);
653-
}
654-
}
655-
continue; // Skip adding this constraint
656-
}
657-
}
658-
659-
// Handle column-level PRIMARY KEY constraints (e.g., "col INT PRIMARY KEY")
660-
if (IsA(element, ColumnDef)) {
661-
ColumnDef* coldef = (ColumnDef*)element;
662-
if (coldef->constraints != nullptr) {
663-
List* new_constraints = NIL;
664-
ListCell* const_lc = nullptr;
665-
666-
foreach (const_lc, coldef->constraints) {
667-
Constraint* constraint = (Constraint*)lfirst(const_lc);
668-
if (constraint->contype == CONSTR_PRIMARY) {
669-
has_primary_key = true;
670-
elog(DEBUG1,
671-
"Removing column-level PRIMARY KEY constraint on table '%s' for column: %s",
672-
table_name.c_str(),
673-
coldef->colname);
674-
primary_keys[table_name].insert(coldef->colname);
675-
continue;
676-
}
677-
new_constraints = lappend(new_constraints, constraint);
678-
}
679-
680-
// Update column's constraints list
681-
coldef->constraints = new_constraints;
682-
}
683-
}
684-
685-
new_table_elts = lappend(new_table_elts, element);
686-
}
687-
if (has_primary_key) {
688-
stmt->tableElts = new_table_elts;
689-
pg::table_storage::instance().set_primary_keys(std::move(primary_keys));
690-
}
691-
}
692616
}
693617

694618
std::optional<pg::utils::parallel_workers_switcher> switcher;

cpp/deeplake_pg/table_am.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ extern "C" {
2828
#include <utils/lsyscache.h>
2929
#include <utils/rel.h>
3030
#include <utils/relcache.h>
31+
#include <utils/snapshot.h> // For SNAPSHOT_DIRTY and snapshot types
3132
#include <utils/varlena.h> // For text functions
3233

3334
#ifdef __cplusplus
@@ -966,13 +967,27 @@ bool deeplake_table_am_routine::index_fetch_tuple(struct IndexFetchTableData* sc
966967

967968
pg::utils::memory_context_switcher context_switcher(idx_scan->memory_context);
968969
idx_scan->scan_state.set_current_position(utils::tid_to_row_number(tid));
970+
969971
if (!idx_scan->scan_state.get_next_tuple(slot)) {
970-
*all_dead = true;
972+
if (all_dead != nullptr) {
973+
*all_dead = true;
974+
}
971975
return false;
972976
}
973977

978+
// For SNAPSHOT_DIRTY (used by btree unique checking),
979+
// we need to indicate that no in-progress transaction is affecting this tuple.
980+
// This prevents PostgreSQL from trying to look up transaction status in pg_subtrans.
981+
// Deeplake tuples are always immediately visible (no MVCC).
982+
if (snapshot != nullptr && snapshot->snapshot_type == SNAPSHOT_DIRTY) {
983+
snapshot->xmin = InvalidTransactionId;
984+
snapshot->xmax = InvalidTransactionId;
985+
}
986+
974987
*call_again = false;
975-
*all_dead = false;
988+
if (all_dead != nullptr) {
989+
*all_dead = false;
990+
}
976991
return true;
977992
}
978993

cpp/deeplake_pg/table_data.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ struct table_data
7777
inline void clear_delete_rows() noexcept;
7878
inline void add_update_row(int64_t row_id, icm::string_map<nd::array> update_row);
7979
inline void clear_update_rows() noexcept;
80-
inline void set_primary_keys(const std::set<std::string>& primary_keys);
8180
inline Oid get_table_oid() const noexcept;
8281
inline bool flush();
8382

cpp/deeplake_pg/table_data_impl.hpp

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -530,32 +530,6 @@ inline bool table_data::flush_updates()
530530
return true;
531531
}
532532

533-
inline void table_data::set_primary_keys(const std::set<std::string>& primary_keys)
534-
{
535-
get_dataset()->set_indexing_mode(deeplake::indexing_mode::always);
536-
bool index_created = false;
537-
for (const auto& column_name : primary_keys) {
538-
auto& column = get_dataset()->get_column(column_name);
539-
auto index_holder = column.index_holder();
540-
if (index_holder != nullptr) {
541-
continue;
542-
}
543-
auto column_type_kind = column.type().kind();
544-
if (column_type_kind == deeplake_core::type_kind::generic && !column.type().data_type().is_array() &&
545-
nd::dtype_is_numeric(column.type().data_type().get_dtype())) {
546-
column.create_index(deeplake_core::index_type(
547-
deeplake_core::numeric_index_type(deeplake_core::deeplake_index_type::type::inverted_index)));
548-
index_created = true;
549-
elog(DEBUG1, "Created numeric index on table '%s' column '%s'", table_name_.c_str(), column_name.c_str());
550-
} else {
551-
elog(DEBUG1, "Column %s is not supported yet for indexing", column_name.c_str());
552-
}
553-
}
554-
if (index_created) {
555-
commit();
556-
}
557-
}
558-
559533
inline Oid table_data::get_table_oid() const noexcept
560534
{
561535
return table_oid_;

cpp/deeplake_pg/table_storage.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -677,14 +677,6 @@ void table_storage::create_table(const std::string& table_name, Oid table_id, Tu
677677
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("%s", e.what())));
678678
}
679679

680-
if (auto it = primary_keys_.find(simple_table_name); it != primary_keys_.end() && !it->second.empty()) {
681-
try {
682-
td.set_primary_keys(it->second);
683-
} catch (const base::exception& e) {
684-
elog(WARNING, "Failed to set primary keys for table %s: %s", table_name.c_str(), e.what());
685-
}
686-
}
687-
688680
tables_.emplace(table_id, std::move(td));
689681
up_to_date_ = false;
690682
}
@@ -792,7 +784,10 @@ bool table_storage::fetch_tuple(Oid table_id, ItemPointer tid, TupleTableSlot* s
792784
ExecClearTuple(slot);
793785

794786
const auto row_number = utils::tid_to_row_number(tid);
795-
if (row_number >= table_data.num_rows()) {
787+
// Use num_total_rows() to include uncommitted rows in the current transaction.
788+
// This is necessary for AFTER triggers (like FK checks) that need to see
789+
// rows inserted earlier in the same transaction.
790+
if (row_number >= table_data.num_total_rows()) {
796791
return false;
797792
}
798793
Datum* values = slot->tts_values;

cpp/deeplake_pg/table_storage.hpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,6 @@ class table_storage
223223
up_to_date_ = up_to_date;
224224
}
225225

226-
inline void set_primary_keys(std::map<std::string, std::set<std::string>>&& primary_keys) noexcept
227-
{
228-
primary_keys_ = std::move(primary_keys);
229-
}
230-
231226
private:
232227
table_storage() = default;
233228

@@ -236,7 +231,6 @@ class table_storage
236231

237232
std::unordered_map<Oid, table_data> tables_;
238233
std::unordered_map<Oid, std::pair<std::string, std::string>> views_;
239-
std::map<std::string, std::set<std::string>> primary_keys_;
240234
std::string schema_name_ = "public";
241235
bool tables_loaded_ = false;
242236
bool up_to_date_ = true;

cpp/deeplake_pg/utils.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ inline static constexpr const char* index_type_option_name = "index_type";
4646
extern bool use_parallel_workers;
4747
extern bool use_deeplake_executor;
4848
extern bool explain_query_before_execute;
49-
extern bool ignore_primary_keys;
5049
extern bool print_runtime_stats;
5150
extern bool is_filter_pushdown_enabled;
5251
extern int32_t max_streamable_column_width;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""
2+
Tests for constraint enforcement in pg_deeplake.
3+
4+
These tests verify that PRIMARY KEY, UNIQUE, and FOREIGN KEY constraints
5+
work correctly with deeplake tables.
6+
"""
7+
import pytest
8+
import asyncpg
9+
10+
11+
@pytest.mark.asyncio
12+
async def test_primary_key_rejects_duplicates(db_conn: asyncpg.Connection):
13+
"""PRIMARY KEY should reject duplicate values."""
14+
try:
15+
await db_conn.execute("DROP TABLE IF EXISTS pk_test CASCADE")
16+
await db_conn.execute("""
17+
CREATE TABLE pk_test (id INT PRIMARY KEY, name TEXT) USING deeplake
18+
""")
19+
20+
await db_conn.execute("INSERT INTO pk_test VALUES (1, 'alice')")
21+
22+
with pytest.raises(asyncpg.UniqueViolationError):
23+
await db_conn.execute("INSERT INTO pk_test VALUES (1, 'bob')")
24+
25+
finally:
26+
await db_conn.execute("DROP TABLE IF EXISTS pk_test CASCADE")
27+
28+
29+
@pytest.mark.asyncio
30+
async def test_unique_constraint_rejects_duplicates(db_conn: asyncpg.Connection):
31+
"""UNIQUE constraint should reject duplicate values."""
32+
try:
33+
await db_conn.execute("DROP TABLE IF EXISTS unique_test CASCADE")
34+
await db_conn.execute("""
35+
CREATE TABLE unique_test (id INT PRIMARY KEY, email TEXT UNIQUE) USING deeplake
36+
""")
37+
38+
await db_conn.execute("INSERT INTO unique_test VALUES (1, 'alice@test.com')")
39+
40+
with pytest.raises(asyncpg.UniqueViolationError):
41+
await db_conn.execute("INSERT INTO unique_test VALUES (2, 'alice@test.com')")
42+
43+
finally:
44+
await db_conn.execute("DROP TABLE IF EXISTS unique_test CASCADE")
45+
46+
47+
@pytest.mark.asyncio
48+
async def test_foreign_key_insert(db_conn: asyncpg.Connection):
49+
"""INSERT into child table with FK should trigger parent lookup."""
50+
try:
51+
await db_conn.execute("DROP TABLE IF EXISTS fk_child CASCADE")
52+
await db_conn.execute("DROP TABLE IF EXISTS fk_parent CASCADE")
53+
54+
await db_conn.execute("""
55+
CREATE TABLE fk_parent (id INT PRIMARY KEY) USING deeplake
56+
""")
57+
await db_conn.execute("""
58+
CREATE TABLE fk_child (
59+
id INT PRIMARY KEY,
60+
parent_id INT REFERENCES fk_parent(id)
61+
) USING deeplake
62+
""")
63+
64+
await db_conn.execute("INSERT INTO fk_parent VALUES (1)")
65+
await db_conn.execute("INSERT INTO fk_child VALUES (1, 1)")
66+
67+
finally:
68+
await db_conn.execute("DROP TABLE IF EXISTS fk_child CASCADE")
69+
await db_conn.execute("DROP TABLE IF EXISTS fk_parent CASCADE")

0 commit comments

Comments
 (0)