Skip to content

Add extension for view logging - #434

Open
MutableFire wants to merge 2 commits into
open-gpdb:OPENGPDB_STABLEfrom
MutableFire:view_log
Open

MutableFire wants to merge 2 commits into
open-gpdb:OPENGPDB_STABLEfrom
MutableFire:view_log

Conversation

@MutableFire

Copy link
Copy Markdown
Contributor

Add view_log extension that logs all references to database views during
query analysis. It hooks into post_parse_analyze_hook to inspect the range
table and subqueries, writing a CSV entry with timestamp, user, session ID,
and schema-qualified view name to pg_log/view.log on the master node.
Logging is presence-based (before view expansion) and does not guarantee
data was actually fetched.

@MutableFire
MutableFire force-pushed the view_log branch 2 times, most recently from d9165fa to f5c37b9 Compare September 30, 2026 13:23
@MutableFire
MutableFire marked this pull request as ready for review October 1, 2026 10:00
Comment thread gpcontrib/view_log/sql/view_log.sql Resolved
Comment thread gpcontrib/view_log/sql/view_log.sql Resolved
Comment thread gpcontrib/view_log/sql/view_log.sql Resolved
static void
view_log_post_parse_analyze_hook(ParseState *pstate, Query *query)
{
log_views_from_rtable(query);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using a standard walker function from PostgreSQL core?

@MutableFire MutableFire Nov 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

query_tree_walker crashes with unrecognized node type: 705 when it encounters specific fields inside the Query struct. The core walker in nodeFuncs.c does not know how to descend into these specific node types and aborts the query with an error, which breaks CREATE VIEW and every subsequent test. Therefore we cannot use query_tree_walker here. We have to enumerate the relevant standard fields manually (targetList, jointree, havingQual, limitCount, limitOffset, cteList, rtable) and walk each of them with expression_tree_walker, which only touches standard expression nodes and never hits the specific ones.

@MutableFire
MutableFire force-pushed the view_log branch 2 times, most recently from 9542127 to 9d96e05 Compare October 2, 2026 13:49
Comment thread gpcontrib/view_log/view_log.c Resolved
Comment thread gpcontrib/view_log/view_log.c Resolved
Comment thread gpcontrib/view_log/view_log.c Outdated
}
ReleaseSysCache(tp);
}
if (query->targetList)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also check cteList, JoinExpr conditions, limitCount, and other fields. For example both queries below execute without logging the reference to v:

WITH c AS (SELECT * FROM v) SELECT * FROM c;
SELECT 1 LIMIT (SELECT id FROM v);

parse_sub_analyze() does not invoke the hook separately, maybe you should use query_tree_walker() to cover the relevant fields instead of enumerating them manually?

@MutableFire MutableFire Nov 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

query_tree_walker crashes with unrecognized node type: 705 when it encounters specific fields inside the Query struct. The core walker in nodeFuncs.c does not know how to descend into these specific node types and aborts the query with an error, which breaks CREATE VIEW and every subsequent test. Therefore we cannot use query_tree_walker here. We have to enumerate the relevant standard fields manually (targetList, jointree, havingQual, limitCount, limitOffset, cteList, rtable) and walk each of them with expression_tree_walker, which only touches standard expression nodes and never hits the specific ones.

Comment thread gpcontrib/view_log/view_log.c Outdated
if (node == NULL)
return false;

if (IsA(node, SubLink))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about this case?
SELECT (SELECT id FROM v) IN (SELECT 1);
SubLink handling skips its testexpr. The walker returns immediately after traversing subselect, skipping the left-hand expression of IN/ANY/ALL.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread gpcontrib/view_log/view_log.c Outdated
nspName = nspNameBuf;
}

strlcat(buf, nspName, sizeof(buf));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this case?

postgres=# CREATE OR REPLACE VIEW "comma,view" AS SELECT 1;
CREATE VIEW
postgres=# SELECT * FROM "comma,view";
 ?column? 
----------
        1
(1 row)

A valid view name such as "comma,view" produces ...,public.comma,view, resulting in five fields instead of four.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@MutableFire
MutableFire force-pushed the view_log branch 7 times, most recently from c5e0f20 to 83f7505 Compare November 3, 2026 12:37
Comment thread gpcontrib/view_log/view_log.c Outdated
errmsg("could not open file " LOG_FILE_NAME ": %m")));
return;
}
write(f, str, strlen(str));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread gpcontrib/view_log/view_log.c Resolved
Comment thread gpcontrib/view_log/view_log.c Outdated
continue;
}

if (rte->rtekind != RTE_RELATION)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider all variants subqueries?

For example what happen if subqueries in functions?

Comment thread gpcontrib/view_log/sql/view_log.sql Outdated
Comment on lines +120 to +122
-- start_ignore
SELECT pg_file_unlink('pg_log/view.log');
-- end_ignore

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove ignore?

Suggested change
-- start_ignore
SELECT pg_file_unlink('pg_log/view.log');
-- end_ignore
SELECT pg_file_unlink('pg_log/view.log');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this code?

Suggested change
-- start_ignore
SELECT pg_file_unlink('pg_log/view.log');
-- end_ignore

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we do. At the end of a test we should bring DB in the same state as before the test. So the file should be deleted to avoid impact on other tests

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants