Skip to content

Support for weak binding views to allow dropping of underlying objects #585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: BABEL_5_X_DEV__PG_17_X
Choose a base branch
from

Conversation

R4hul04
Copy link
Contributor

@R4hul04 R4hul04 commented Jun 5, 2025

Description

This PR introduces improvements to Babelfish's view handling, focusing on weak binding views. The changes enable more SQL Server-compatible behavior for view dependencies.

This PR introduces the following changes:

  1. Added hooks in DROP codepath to allow dropping underlying objects (tables, functions, views) referenced by weak binding views without errors, while maintaining the view structure
  2. Implemented a view repair mechanism that automatically fixes broken views when their referenced objects are recreated, eliminating the need for manual view recreation
  3. Added validation to prevent creating schema-bound views that reference non-schema-bound views, matching SQL Server's behavior

Implementation Details

  1. Added a new flags BBF_VIEW_DEF_FLAG_IS_BROKEN to track views with missing dependencies and BBF_VIEW_DEF_FLAG_IS_WEAK_VIEW to identify view's schemabinding property in babelfish_view_def catalog
  2. Implemented hooks in object drop operations to detect and mark dependent views as broken
  3. Created a view repair mechanism that activates when a broken view is accessed during the rewriting
  4. Added recursive dependency tracking to handle chains of view dependencies

Issues Resolved

BABEL-1660

Check List

  • Commits are signed per the DCO using --signoff

By submitting this pull request, I confirm that my contribution is under the terms of the PostgreSQL license, and grant any person obtaining a copy of the contribution permission to relicense all or a portion of my contribution to the PostgreSQL License solely to contribute all or a portion of my contribution to the PostgreSQL open source project.

For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@@ -131,6 +133,20 @@ RemoveObjects(DropStmt *stmt)
table_close(relation, NoLock);

add_exact_object_address(&address, objects);

/* Check for strong views and handle weak views */
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not move the open table into the hook implement ?

@R4hul04 R4hul04 requested a review from kuntalghosh June 10, 2025 00:56
* This returns a new buffer which must be freed by the caller.
*/
static PQExpBuffer
createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not reuse the createDummyViewAsClause def from community ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We are reusing the same function. We are only moving it before createViewAsClause, since we need createDummyViewAsClause defined before the function call in createViewAsClause

Copy link
Contributor

Choose a reason for hiding this comment

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

We should not move this function's place in community code, since after moving, the follow up new commits diff from community will be lost for this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed.
Added the function declaration of createDummyViewAsClause at the top instead of moving the whole function body.

@@ -15833,6 +15877,7 @@ createViewAsClause(Archive *fout, const TableInfo *tbinfo)
{
PQExpBuffer query = createPQExpBuffer();
PQExpBuffer result = createPQExpBuffer();
PQExpBuffer dummyResult = createPQExpBuffer();
Copy link
Contributor

Choose a reason for hiding this comment

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

Dummyresult doesn't need to be created as createPQExpBuffer.

Copy link
Contributor Author

@R4hul04 R4hul04 Jun 17, 2025

Choose a reason for hiding this comment

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

Removed dummyResult variable instead using the existing variable result

@@ -15869,50 +15927,6 @@ createViewAsClause(Archive *fout, const TableInfo *tbinfo)
return result;
}

/*
* Create a dummy AS clause for a view. This is used when the real view
Copy link
Contributor

Choose a reason for hiding this comment

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

Why move the whole function definition ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added createDummyViewAsClause inside createViewAsClause. Hence we needed to define createDummyViewAsClause before createViewAsClause.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should find a alternative way rather than moving this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added the function declaration of createDummyViewAsClause at the top instead of moving the whole function body.

@@ -19,9 +19,11 @@
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_depend_d.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Why adding this .h ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed pg_depend_d.h

@R4hul04 R4hul04 marked this pull request as ready for review June 16, 2025 19:05
@R4hul04 R4hul04 requested a review from tanscorpio7 June 16, 2025 19:05
Rahul Parande added 2 commits June 17, 2025 17:49
instead added it's declaration at the top to access the function

Signed-off-by: Rahul Parande <[email protected]>
Comment on lines 4434 to 4441
* Step 0 (Babelfish extension)
*
* If this is a view with broken rules, try to repair it
* using the definition from babelfish_view_def
*/
if (view_repair_hook)
(*view_repair_hook)(parsetree);
/*
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we use a more generic name. pre_query_rewrite_hook

Copy link
Contributor

Choose a reason for hiding this comment

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

+1
Let's use the name pre_QueryRewrite_hook.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed the hook to pre_QueryRewrite_hook

Comment on lines 1680 to 1686

/* Check for strong views and handle weak views */
if ((drop->removeType == OBJECT_TABLE || drop->removeType == OBJECT_VIEW) && view_dependency_hook)
{
if (!(*view_dependency_hook)(&obj, NULL, NULL))
continue;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Let us move all comments and conditions inside the hook. Also would naming it pre_drop_object_hook make sense ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved implementation to InvokeObjectDropHook instead.

@@ -1676,6 +1677,13 @@ RemoveRelations(DropStmt *drop)
{
InvokeObjectDropHook(RelationRelationId,relOid,0);
}

/* Check for strong views and handle weak views */
if ((drop->removeType == OBJECT_TABLE || drop->removeType == OBJECT_VIEW) && view_dependency_hook)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move this part also into the hook : (drop->removeType == OBJECT_TABLE || drop->removeType == OBJECT_VIEW) ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's use InvokeObjectDropHook which already there in the function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved implementation to use InvokeObjectDropHook instead.

Comment on lines 137 to 141
if ((stmt->removeType == OBJECT_FUNCTION) && view_dependency_hook)
{
if (!(*view_dependency_hook)(&address, NULL, NULL))
continue;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's make the hook generic and add all TSQL related check within the hook function. We're not worried about hook overhead in a DDL execution. Also, there's already a hook for this task - InvokeObjectDropHook. Let's plugin this hook here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Used InvokeObjectDropHook instead of view_dependency_hook.

@@ -1676,6 +1677,13 @@ RemoveRelations(DropStmt *drop)
{
InvokeObjectDropHook(RelationRelationId,relOid,0);
}

/* Check for strong views and handle weak views */
if ((drop->removeType == OBJECT_TABLE || drop->removeType == OBJECT_VIEW) && view_dependency_hook)
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's use InvokeObjectDropHook which already there in the function.

Comment on lines 387 to 389
if (check_view_dependencies_hook)
(*check_view_dependencies_hook)(viewParse);

Copy link
Contributor

Choose a reason for hiding this comment

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

The function parse_analyze_fixedparams already has a hook inside it - post_parse_analyze_hook(). Let's use that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated post_parse_analyze_hook() with the required logic.

Comment on lines 4434 to 4441
* Step 0 (Babelfish extension)
*
* If this is a view with broken rules, try to repair it
* using the definition from babelfish_view_def
*/
if (view_repair_hook)
(*view_repair_hook)(parsetree);
/*
Copy link
Contributor

Choose a reason for hiding this comment

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

+1
Let's use the name pre_QueryRewrite_hook.

Comment on lines -15859 to +15871
pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
tbinfo->dobj.name);
{
/*
* Handle broken views (with empty definitions)
* Instead of failing, create a dummy SELECT that preserves the structure
*/
PQclear(res);
destroyPQExpBuffer(query);

/* Use createDummyViewAsClause to generate a compatible structure */
result = createDummyViewAsClause(fout, tbinfo);

return result;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Whenever we modify in pg_dump, we make sure that this is a Babelfish Database. Check the functions with bbf prefix. Ideally, the logic should be,

if (bbfShouldDumpViews(Archive *fout, const TableInfo *tbinfo))
{
    createTSQLViewAsClause()
    return;
}

Within createTSQLViewAsClause, we should do all the handlings.

Copy link
Contributor Author

@R4hul04 R4hul04 Jun 18, 2025

Choose a reason for hiding this comment

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

Do we need to create our own createTSQLViewAsClause function in dump_babel_utils.c which will be called specifically for babelfish database. This function will be called from createViewAsClause in pg_dump.c ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we will insert a dummy view query into pg_rewrite at the time of DROP which will avoid the need to alter dump restore. The dummy query can be prepared according to the columns.
We should avoid keeping empty string as rewrite rule since engine code assumes that is an impossible case.

Rahul Parande added 4 commits June 18, 2025 19:55
InvokeObjectDropHook.
2. Removed check_view_dependencies_hook from view.c and used existing
   post_parse_analyze_hook.
3. Renamed view_repair_hook to pre_QueryRewrite_hook

Signed-off-by: Rahul Parande <[email protected]>
as special call for dependency check during function drop

Signed-off-by: Rahul Parande <[email protected]>
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