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: 0 additions & 1 deletion pg_client/src/catalog/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ add_library(lbug_pg_client_catalog
OBJECT
pg_client_catalog.cpp
pg_client_table_catalog_entry.cpp)
add_dependencies(lbug_pg_client_catalog lbug_common lbug_catalog)

set(PG_CLIENT_EXTENSION_OBJECT_FILES
${PG_CLIENT_EXTENSION_OBJECT_FILES} $<TARGET_OBJECTS:lbug_pg_client_catalog>
Expand Down
35 changes: 25 additions & 10 deletions pg_client/src/catalog/pg_client_catalog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,26 @@ void PgClientCatalog::init() {
std::string tableName = row.cells[0].value;
auto lowerName = tableName;
common::StringUtils::toLower(lowerName);
if (lowerName.rfind("rel_", 0) == 0) {
createForeignRelTable(tableName);
} else if (lowerName.rfind("node_", 0) == 0) {
if (lowerName.rfind("node_", 0) == 0) {
createForeignNodeTable(tableName);
}
}
// Second pass: register rel tables. Node tables must be registered first so that
// FK-based rel tables can resolve their src/dst node table IDs.
for (auto& row : result.rows) {
std::string tableName = row.cells[0].value;
auto lowerName = tableName;
common::StringUtils::toLower(lowerName);
if (lowerName.rfind("fkrel_", 0) == 0) {
// Foreign-key-based rel table: scan-driven, optimizer generates a join.
// No CSR columns; backed by a ForeignRelTable.
createForeignRelTable(tableName);
} else if (lowerName.rfind("rel_", 0) == 0) {
// CSR-based rel table: materialized into a local on-disk CSR rel table.
// TODO: COPY data from PostgreSQL into a local RelTable.
createForeignRelTable(tableName);
}
}
}

static std::vector<PgClientColumnInfo> getTableColumnInfoFromConnector(
Expand Down Expand Up @@ -253,17 +267,19 @@ void PgClientCatalog::createForeignRelTable(const std::string& tableName) {
auto columnNames = getColumnNames(columnInfo);
auto columnTypes = getColumnTypes(columnInfo);

// Look up src/dst node tables in the main catalog to get table IDs
auto* catalog = context_->getDatabase()->getCatalog();
auto* srcEntry = catalog->getTableCatalogEntry(&transaction::DUMMY_TRANSACTION, srcTableName);
auto* dstEntry = catalog->getTableCatalogEntry(&transaction::DUMMY_TRANSACTION, dstTableName);
// Look up src/dst node tables in the attached catalog (the foreign PgClientTableCatalogEntry
// entries), not the main catalog shadows. fkrel_ tables join against the foreign node
// entries directly, so the rel's src/dst table IDs must match the entries that
// `testdb.node_person` (and bare `node_person` via shadow) resolve to.
auto* srcEntry = tables->getEntry(&transaction::DUMMY_TRANSACTION, srcTableName);
auto* dstEntry = tables->getEntry(&transaction::DUMMY_TRANSACTION, dstTableName);
if (srcEntry == nullptr || dstEntry == nullptr) {
createForeignNodeTable(tableName);
return;
}

common::table_id_t srcTableID = srcEntry->getTableID();
common::table_id_t dstTableID = dstEntry->getTableID();
common::table_id_t srcTableID = srcEntry->cast<catalog::TableCatalogEntry>().getTableID();
common::table_id_t dstTableID = dstEntry->cast<catalog::TableCatalogEntry>().getTableID();

// Build scan function
auto query = std::format("SELECT {{}} FROM \"{}\".\"{}\"",
Expand All @@ -276,7 +292,6 @@ void PgClientCatalog::createForeignRelTable(const std::string& tableName) {
// Create foreign table entry in attached catalog
auto foreignTableEntry = std::make_unique<catalog::PgClientTableCatalogEntry>(
tableName, scanFunc, scanInfo);
auto* attachedEntryPtr = foreignTableEntry.get();
for (auto& def : propertyDefs) {
foreignTableEntry->addProperty(def);
}
Expand Down
1 change: 0 additions & 1 deletion pg_client/src/connector/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ add_library(lbug_pg_client_connector
OBJECT
pg_client_connector.cpp)

add_dependencies(lbug_pg_client_connector lbug_common)

set(PG_CLIENT_EXTENSION_OBJECT_FILES
${PG_CLIENT_EXTENSION_OBJECT_FILES} $<TARGET_OBJECTS:lbug_pg_client_connector>
Expand Down
1 change: 0 additions & 1 deletion pg_client/src/function/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
add_library(lbug_pg_client_function OBJECT pg_client_scan.cpp)
add_dependencies(lbug_pg_client_function lbug_common)

set(PG_CLIENT_EXTENSION_OBJECT_FILES
${PG_CLIENT_EXTENSION_OBJECT_FILES} $<TARGET_OBJECTS:lbug_pg_client_function>
Expand Down
1 change: 0 additions & 1 deletion pg_client/src/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ add_library(lbug_pg_client_main
OBJECT
pg_client_extension.cpp)

add_dependencies(lbug_pg_client_main lbug_common)

set(PG_CLIENT_EXTENSION_OBJECT_FILES
${PG_CLIENT_EXTENSION_OBJECT_FILES} $<TARGET_OBJECTS:lbug_pg_client_main>
Expand Down
1 change: 0 additions & 1 deletion pg_client/src/storage/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ add_library(lbug_pg_client_storage
OBJECT
pg_client_storage.cpp)

add_dependencies(lbug_pg_client_storage lbug_common)

set(PG_CLIENT_EXTENSION_OBJECT_FILES
${PG_CLIENT_EXTENSION_OBJECT_FILES} $<TARGET_OBJECTS:lbug_pg_client_storage>
Expand Down
18 changes: 9 additions & 9 deletions pg_client/test/test_files/create_pg_client_test_db.sql
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ SET row_security = off;
--

CREATE TABLE public.node_person (
id integer NOT NULL,
id integer NOT NULL PRIMARY KEY,
name character varying(100) NOT NULL,
age integer NOT NULL
);
Expand All @@ -30,18 +30,18 @@ CREATE TABLE public.node_person (
ALTER TABLE public.node_person OWNER TO ci;

--
-- Table: rel_knows
-- Table: fkrel_knows
--

CREATE TABLE public.rel_knows (
CREATE TABLE public.fkrel_knows (
id integer NOT NULL,
from_id integer NOT NULL,
to_id integer NOT NULL,
from_id integer NOT NULL REFERENCES public.node_person(id),
to_id integer NOT NULL REFERENCES public.node_person(id),
since date NOT NULL
);


ALTER TABLE public.rel_knows OWNER TO ci;
ALTER TABLE public.fkrel_knows OWNER TO ci;

--
-- Data for node_person
Expand All @@ -57,14 +57,14 @@ INSERT INTO public.node_person (id, name, age) VALUES
SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('public.node_person', 'id'), 5, true);

--
-- Data for rel_knows
-- Data for fkrel_knows
--

INSERT INTO public.rel_knows (id, from_id, to_id, since) VALUES
INSERT INTO public.fkrel_knows (id, from_id, to_id, since) VALUES
(1, 1, 2, '2020-01-15'),
(2, 1, 3, '2021-03-20'),
(3, 2, 4, '2022-06-10'),
(4, 3, 4, '2023-08-05'),
(5, 4, 5, '2023-12-01');

SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('public.rel_knows', 'id'), 5, true);
SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('public.fkrel_knows', 'id'), 5, true);
15 changes: 9 additions & 6 deletions pg_client/test/test_files/pg_client.test
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ Attached database successfully.
Charlie|35
Eve|32

-CASE ScanRelTable
-CASE ScanFkRelTable
-SKIP_FSM_LEAK_CHECK
-LOAD_DYNAMIC_EXTENSION pg_client
-STATEMENT ATTACH '${PG_CLIENT_CONNECTION_STRING}' as testdb (dbtype PG_CLIENT);
---- 1
Attached database successfully.
-STATEMENT LOAD FROM testdb.rel_knows RETURN from_id, to_id, since ORDER BY id;
-STATEMENT LOAD FROM testdb.fkrel_knows RETURN from_id, to_id, since ORDER BY id;
---- 5
1|2|2020-01-15
1|3|2021-03-20
Expand All @@ -72,13 +72,14 @@ Alice|30
Eve|32
Charlie|35

-CASE MatchOnRelTable
-CASE MatchOnFkRelTable
-SKIP
-SKIP_FSM_LEAK_CHECK
-LOAD_DYNAMIC_EXTENSION pg_client
-STATEMENT ATTACH '${PG_CLIENT_CONNECTION_STRING}' as testdb (dbtype PG_CLIENT);
---- 1
Attached database successfully.
-STATEMENT MATCH (a:testdb.node_person)-[k:rel_knows]->(b:testdb.node_person) RETURN count(*);
-STATEMENT MATCH (a:testdb.node_person)-[k:fkrel_knows]->(b:testdb.node_person) RETURN count(*);
---- 1
5

Expand All @@ -89,6 +90,8 @@ Attached database successfully.
---- 1
Attached database successfully.
-STATEMENT CALL SHOW_TABLES() RETURN *;
---- 2
---- 4
0|node_person|ATTACHED|testdb(PG_CLIENT)|
1|rel_knows|ATTACHED|testdb(PG_CLIENT)|
0|node_person|NODE|shadow(graph)|
1|fkrel_knows|ATTACHED|testdb(PG_CLIENT)|
1|fkrel_knows|REL|public.fkrel_knows|
24 changes: 12 additions & 12 deletions pg_client/test/test_pg_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ def setUpClass(cls):
('Initech', 500000.00)
"""))

# Create rel_knows table with FK columns named src_id/dst_id
# Create fkrel_knows table with FK columns named src_id/dst_id
# Prefix rel_ + FK constraints → auto-register as relationship table
conn.execute(sa.text("""
CREATE TABLE rel_knows (
CREATE TABLE fkrel_knows (
id SERIAL PRIMARY KEY,
src_id INTEGER NOT NULL,
dst_id INTEGER NOT NULL,
Expand All @@ -102,31 +102,31 @@ def setUpClass(cls):

# Add actual FOREIGN KEY constraints so the FK query detects src/dst tables
conn.execute(sa.text("""
ALTER TABLE rel_knows
ALTER TABLE fkrel_knows
ADD CONSTRAINT fk_src FOREIGN KEY (src_id) REFERENCES node_person(id),
ADD CONSTRAINT fk_dst FOREIGN KEY (dst_id) REFERENCES node_person(id)
"""))

conn.execute(sa.text("""
INSERT INTO rel_knows (src_id, dst_id, since) VALUES
INSERT INTO fkrel_knows (src_id, dst_id, since) VALUES
(1, 2, '2020-01-15'),
(1, 3, '2021-03-20'),
(2, 4, '2022-06-10'),
(3, 4, '2023-08-05'),
(4, 5, '2023-12-01')
"""))

# Create rel_works_at table with FK constraints
# Create fkrel_works_at table with FK constraints
conn.execute(sa.text("""
CREATE TABLE rel_works_at (
CREATE TABLE fkrel_works_at (
id SERIAL PRIMARY KEY,
src_id INTEGER NOT NULL REFERENCES node_person(id),
dst_id INTEGER NOT NULL REFERENCES node_company(id)
)
"""))

conn.execute(sa.text("""
INSERT INTO rel_works_at (src_id, dst_id) VALUES
INSERT INTO fkrel_works_at (src_id, dst_id) VALUES
(1, 1),
(2, 2),
(3, 1),
Expand Down Expand Up @@ -199,7 +199,7 @@ def test_06_load_rel_table(self):
script = f"""
LOAD EXTENSION '{PG_CLIENT_EXT}';
ATTACH '{self.conn_str}' AS testdb (DBTYPE PG_CLIENT);
LOAD FROM testdb.rel_knows RETURN src_id, dst_id, since ORDER BY id;
LOAD FROM testdb.fkrel_knows RETURN src_id, dst_id, since ORDER BY id;
"""
stdout, stderr = run_lbug(script)
self.assertIn("2020-01-15", stdout, f"Rel load failed: {stderr}")
Expand All @@ -226,7 +226,7 @@ def test_06b_match_rel_table(self):
script = f"""
LOAD EXTENSION '{PG_CLIENT_EXT}';
ATTACH '{self.conn_str}' AS testdb (DBTYPE PG_CLIENT);
MATCH (a:node_person)-[k:rel_knows]->(b:node_person)
MATCH (a:node_person)-[k:fkrel_knows]->(b:node_person)
RETURN count(*);
"""
stdout, stderr = run_lbug(script)
Expand All @@ -244,7 +244,7 @@ def test_06c_match_rel_count_parallel(self):
script = f"""
LOAD EXTENSION '{PG_CLIENT_EXT}';
ATTACH '{self.conn_str}' AS testdb (DBTYPE PG_CLIENT);
MATCH (a:node_person)-[k:rel_knows]->(b:node_person)
MATCH (a:node_person)-[k:fkrel_knows]->(b:node_person)
RETURN count(*);
"""
stdout, stderr = run_lbug(script)
Expand All @@ -255,7 +255,7 @@ def test_07_scan_rel_table_filter(self):
script = f"""
LOAD EXTENSION '{PG_CLIENT_EXT}';
ATTACH '{self.conn_str}' AS testdb (DBTYPE PG_CLIENT);
LOAD FROM testdb.rel_knows WHERE src_id = 1 RETURN dst_id, since ORDER BY dst_id;
LOAD FROM testdb.fkrel_knows WHERE src_id = 1 RETURN dst_id, since ORDER BY dst_id;
"""
stdout, stderr = run_lbug(script)
self.assertIn("2020-01-15", stdout, f"Rel filter failed: {stderr}")
Expand All @@ -270,7 +270,7 @@ def test_09_show_tables(self):
"""
stdout, stderr = run_lbug(script)
self.assertIn("node_person", stdout, f"SHOW_TABLES missing node_person: {stderr}")
self.assertIn("rel_knows", stdout, f"SHOW_TABLES missing rel_knows: {stderr}")
self.assertIn("fkrel_knows", stdout, f"SHOW_TABLES missing fkrel_knows: {stderr}")

def test_10_table_info(self):
"""Test TABLE_INFO on attached table."""
Expand Down
Loading