Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 60a96a9

Browse files
author
Rahul Parande
committedMar 15, 2025·
Add support for ALTER VIEW and CREATE OR ALTER VIEW syntax on schema bound views in Babelfish
This commit implements - ALTER VIEW and CREATE OR ALTER VIEW syntax support - Added createoralter flag to ViewStmt structure and grammar - Added comprehensive test cases: * Basic alter view functionality * Schema bound views * Various test scenarios and edge cases - Updated test files and expected outputs - Fixed failing tests and naming conflicts - Addressed review comments Signed-off-by: Rahul Parande <rparande@amazon.com>
1 parent 158b0d1 commit 60a96a9

36 files changed

+1672
-25
lines changed
 

‎contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-decl.y

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
%type <node> tsql_stmt
1616

17-
%type <node> tsql_AlterFunctionStmt tsql_CreatePartitionStmt
17+
%type <node> tsql_AlterFunctionStmt tsql_CreatePartitionStmt tsql_AlterViewStmt
1818
%type <node> tsql_CreateFunctionStmt tsql_VariableSetStmt tsql_CreateTrigStmt tsql_TransactionStmt tsql_UpdateStmt tsql_DeleteStmt tsql_IndexStmt
1919
%type <partspec> tsql_PartitionSpec
2020
%type <node> tsql_DropIndexStmt tsql_InsertStmt

‎contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-rule.y

+30
Original file line numberDiff line numberDiff line change
@@ -2476,6 +2476,7 @@ tsql_stmt :
24762476
| AlterTSDictionaryStmt
24772477
| AlterUserMappingStmt
24782478
| tsql_AlterUserStmt
2479+
| tsql_AlterViewStmt
24792480
| AnalyzeStmt
24802481
| CallStmt
24812482
| CheckPointStmt
@@ -4110,6 +4111,35 @@ tsql_AlterFunctionStmt:
41104111
}
41114112
;
41124113

4114+
tsql_AlterViewStmt:
4115+
TSQL_ALTER VIEW qualified_name opt_column_list opt_reloptions
4116+
AS SelectStmt opt_check_option
4117+
{
4118+
ViewStmt *n = makeNode(ViewStmt);
4119+
n->view = $3;
4120+
n->aliases = $4;
4121+
n->query = $7;
4122+
n->replace = true;
4123+
n->options = $5;
4124+
n->withCheckOption = $8;
4125+
n->createOrAlter = true;
4126+
$$ = (Node *) n;
4127+
}
4128+
| CREATE OR TSQL_ALTER VIEW qualified_name opt_column_list opt_reloptions
4129+
AS SelectStmt opt_check_option
4130+
{
4131+
ViewStmt *n = makeNode(ViewStmt);
4132+
n->view = $5;
4133+
n->aliases = $6;
4134+
n->query = $9;
4135+
n->replace = false;
4136+
n->options = $7;
4137+
n->withCheckOption = $10;
4138+
n->createOrAlter = true;
4139+
$$ = (Node *) n;
4140+
}
4141+
;
4142+
41134143
/*
41144144
* These rules define the WITH clause in a CREATE PROCEDURE
41154145
* or CREATE FUNCTION statement. This is very similar to

‎contrib/babelfishpg_tsql/src/backend_parser/parser.c

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ pgtsql_base_yylex(YYSTYPE *lvalp, YYLTYPE * llocp, core_yyscan_t yyscanner)
321321
case PROCEDURE:
322322
case TSQL_PROC:
323323
case FUNCTION:
324+
case VIEW:
324325
cur_token = TSQL_ALTER;
325326
break;
326327
}

‎contrib/babelfishpg_tsql/src/dbcmds.c

+8
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ gen_createdb_subcmds(const char *dbname, const char *owner)
9595
bool owner_is_sa_or_superuser = false;
9696
const char *db_datareader;
9797
const char *db_datawriter;
98+
int old_dialect;
9899

99100
schema = get_dbo_schema_name(dbname);
100101
dbo = get_dbo_role_name(dbname);
@@ -155,8 +156,15 @@ gen_createdb_subcmds(const char *dbname, const char *owner)
155156
if (guest)
156157
appendStringInfo(&query, "CREATE SCHEMA dummy AUTHORIZATION dummy; ");
157158

159+
old_dialect = sql_dialect;
160+
sql_dialect = SQL_DIALECT_PG;
161+
/* this query must be run in PG dialect or we will get a syntax error due to different
162+
* ALTER VIEW behavior between PG and TSQL
163+
*/
158164
res = raw_parser(query.data, RAW_PARSE_DEFAULT);
159165

166+
sql_dialect = old_dialect;
167+
160168
if (guest)
161169
{
162170
if (!owner_is_sa_or_superuser)

‎contrib/babelfishpg_tsql/src/pl_handler.c

+182
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "commands/tablecmds.h"
4040
#include "commands/trigger.h"
4141
#include "commands/user.h"
42+
#include "commands/view.h"
4243
#include "common/md5.h"
4344
#include "common/string.h"
4445
#include "funcapi.h"
@@ -63,6 +64,7 @@
6364
#include "utils/acl.h"
6465
#include "utils/builtins.h"
6566
#include "utils/guc_tables.h"
67+
#include "utils/inval.h"
6668
#include "utils/lsyscache.h"
6769
#include "utils/plancache.h"
6870
#include "utils/ps_status.h"
@@ -142,6 +144,8 @@ static void call_prev_ProcessUtility(PlannedStmt *pstmt,
142144
QueryCompletion *qc);
143145
static void set_pgtype_byval(List *name, bool byval);
144146
static void pltsql_proc_get_oid_proname_proacl(AlterFunctionStmt *stmt, ParseState *pstate, Oid *oid, Acl **acl, bool *isSameFunc, bool is_proc);
147+
static Acl *get_old_view_acl(Oid oldViewOid);
148+
static void pg_class_update_acl(Oid newViewOid, Acl *oldViewAcl);
145149
static bool pltsql_truncate_identifier(char *ident, int len, bool warn);
146150
static Name pltsql_cstr_to_name(char *s, int len);
147151
extern void pltsql_add_guc_plan(CachedPlanSource *plansource);
@@ -2766,6 +2770,117 @@ bbf_ProcessUtility(PlannedStmt *pstmt,
27662770
}
27672771
break;
27682772
}
2773+
2774+
/*
2775+
* This case handles the `ALTER VIEW` and `CREATE OR ALTER VIEW` statements in Babelfish.
2776+
*/
2777+
case T_ViewStmt:
2778+
{
2779+
ViewStmt *stmt = (ViewStmt *) parsetree;
2780+
2781+
/*
2782+
* We are using PostgreSQL's existing ViewStmt node which is shared between PostgreSQL's
2783+
* CREATE VIEW and T-SQL's ALTER VIEW/CREATE OR ALTER VIEW operations. To properly distinguish
2784+
* between these operations and not let CREATE VIEW inside this case we use createOrAlter flag
2785+
*
2786+
* Since both CREATE VIEW and CREATE OR ALTER VIEW set replace = false initially,
2787+
* we use the 'createOrAlter' flag to distinguish between them and implement the
2788+
* correct behavior when a view already exists
2789+
*/
2790+
2791+
if (sql_dialect == SQL_DIALECT_TSQL && (stmt->createOrAlter))
2792+
{
2793+
/*
2794+
* 1. Retrieve the OID of the old view using `RangeVarGetRelid()`.
2795+
* 2. If the old view does not exist, create the new view using `DefineView()` and increment the command counter.
2796+
* 3. If the old view exists:
2797+
* a. Save the ACL information of the current view.
2798+
* b. Drop the current view using `performDeletion()`.
2799+
* c. Create the new view using `DefineView()` and increment the command counter.
2800+
* f. Store the new view definition in the `bbf_view_def` catalog using `store_view_definition_hook()`.
2801+
* g. Update the ACL information for the new view using `pg_class_update_acl()`.
2802+
*/
2803+
ObjectAddress address, originalView;
2804+
Oid oldViewOid;
2805+
Acl *oldViewAcl = NULL;
2806+
bool isCompleteQuery = (context != PROCESS_UTILITY_SUBCOMMAND);
2807+
bool needCleanup;
2808+
2809+
if (!IS_TDS_CLIENT())
2810+
{
2811+
ereport(ERROR,
2812+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2813+
errmsg("TSQL ALTER VIEW is not supported from PostgreSQL endpoint.")));
2814+
}
2815+
2816+
needCleanup = isCompleteQuery && EventTriggerBeginCompleteQuery();
2817+
2818+
PG_TRY();
2819+
{
2820+
StartTransactionCommand();
2821+
2822+
if (isCompleteQuery)
2823+
EventTriggerDDLCommandStart(parsetree);
2824+
2825+
/* Get the old view's OID and verify it exists */
2826+
oldViewOid = RangeVarGetRelid(stmt->view, AccessExclusiveLock, true);
2827+
2828+
/* If the view does not exist, check if the stmt is CREATE OR ALTER VIEW / ALTER VIEW */
2829+
if (!OidIsValid(oldViewOid))
2830+
{
2831+
if(stmt->replace) /* we have set replace to false for CREATE OR ALTER VIEW to avoid view does not exist error */
2832+
{
2833+
ereport(ERROR,
2834+
(errcode(ERRCODE_UNDEFINED_TABLE),
2835+
errmsg("view \"%s\" does not exist",
2836+
stmt->view->relname)));
2837+
}
2838+
/* View doesn't exist - create it */
2839+
address = DefineView(stmt, queryString, pstmt->stmt_location, pstmt->stmt_len);
2840+
CommandCounterIncrement();
2841+
}
2842+
/* View exists */
2843+
else
2844+
{
2845+
/* Save ACL before dropping the view */
2846+
oldViewAcl = get_old_view_acl(oldViewOid);
2847+
CacheInvalidateRelcacheByRelid(oldViewOid);
2848+
2849+
/* Drop the old view */
2850+
originalView.objectId = oldViewOid;
2851+
originalView.classId = RelationRelationId;
2852+
originalView.objectSubId = 0;
2853+
performDeletion(&originalView, DROP_RESTRICT, 0);
2854+
CommandCounterIncrement();
2855+
2856+
/* Create new view */
2857+
stmt->replace = true;
2858+
address = DefineView(stmt, queryString, pstmt->stmt_location, pstmt->stmt_len);
2859+
CommandCounterIncrement();
2860+
2861+
/* Store the view definition in babelfish_view_def */
2862+
if(store_view_definition_hook)
2863+
store_view_definition_hook(queryString, address);
2864+
2865+
/* Update ACL info */
2866+
pg_class_update_acl(address.objectId, oldViewAcl);
2867+
pfree(oldViewAcl);
2868+
}
2869+
CommitTransactionCommand();
2870+
}
2871+
PG_FINALLY();
2872+
{
2873+
if (needCleanup)
2874+
EventTriggerEndCompleteQuery();
2875+
}
2876+
PG_END_TRY();
2877+
return;
2878+
}
2879+
/* check that no T-SQL ALTER VIEW operations reach this point because they should have been handled earlier in the code.*/
2880+
Assert(!(sql_dialect == SQL_DIALECT_TSQL && stmt->createOrAlter));
2881+
break;
2882+
}
2883+
27692884
case T_AlterTableStmt:
27702885
{
27712886
AlterTableStmt *atstmt = (AlterTableStmt *) parsetree;
@@ -4814,6 +4929,73 @@ pltsql_proc_get_oid_proname_proacl(AlterFunctionStmt *stmt, ParseState *pstate,
48144929
*isSameFunc = OidIsValid(funcOid);
48154930
}
48164931

4932+
/*
4933+
* Get the acl of view from pg_class given its oid
4934+
*/
4935+
static Acl *
4936+
get_old_view_acl(Oid oldViewOid)
4937+
{
4938+
HeapTuple tuple;
4939+
Datum aclDatum;
4940+
bool isNull;
4941+
Acl *oldViewAcl = NULL;
4942+
4943+
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(oldViewOid));
4944+
if (!HeapTupleIsValid(tuple))
4945+
ereport(ERROR,
4946+
(errcode(ERRCODE_UNDEFINED_TABLE),
4947+
errmsg("cache lookup failed for relation %u", oldViewOid)));
4948+
4949+
aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, &isNull);
4950+
if (!isNull)
4951+
oldViewAcl = DatumGetAclPCopy(aclDatum);
4952+
4953+
ReleaseSysCache(tuple);
4954+
4955+
return oldViewAcl;
4956+
}
4957+
4958+
/*
4959+
* Update the acl info of view in pg_class given its oid and acl info
4960+
*/
4961+
static void
4962+
pg_class_update_acl(Oid newViewOid, Acl *oldViewAcl)
4963+
{
4964+
Relation pg_class_rel;
4965+
HeapTuple classtup;
4966+
4967+
Datum values[Natts_pg_class];
4968+
bool nulls[Natts_pg_class];
4969+
bool replaces[Natts_pg_class];
4970+
HeapTuple newtup;
4971+
4972+
/* Get the tuple from syscache */
4973+
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(newViewOid));
4974+
if (!HeapTupleIsValid(classtup))
4975+
elog(ERROR, "cache lookup failed for relation %u", newViewOid);
4976+
4977+
pg_class_rel = table_open(RelationRelationId, RowExclusiveLock);
4978+
4979+
memset(values, 0, sizeof(values));
4980+
memset(nulls, false, sizeof(nulls));
4981+
memset(replaces, false, sizeof(replaces));
4982+
4983+
if (oldViewAcl != NULL)
4984+
values[Anum_pg_class_relacl - 1] = PointerGetDatum(oldViewAcl);
4985+
else
4986+
nulls[Anum_pg_class_relacl - 1] = true;
4987+
replaces[Anum_pg_class_relacl - 1] = true;
4988+
4989+
newtup = heap_modify_tuple(classtup, RelationGetDescr(pg_class_rel),
4990+
values, nulls, replaces);
4991+
4992+
CatalogTupleUpdate(pg_class_rel, &newtup->t_self, newtup);
4993+
4994+
ReleaseSysCache(classtup);
4995+
heap_freetuple(newtup);
4996+
table_close(pg_class_rel, RowExclusiveLock);
4997+
}
4998+
48174999
/*
48185000
* Update the pg_type catalog entry for the given name to have
48195001
* typbyval set to the given value.

‎contrib/babelfishpg_tsql/src/tsqlIface.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -4356,6 +4356,7 @@ storeOriginalQueryForBatchLevelStatement(TSqlParser::Batch_level_statementContex
43564356
{
43574357
int startIndex = -1;
43584358
int endIndex = -1;
4359+
int alterIndex = -1;
43594360
std::string originalQueryCopy = originalQuery;
43604361

43614362
if ((ctx->create_or_alter_procedure() && ctx->create_or_alter_procedure()->ALTER()))
@@ -4372,6 +4373,25 @@ storeOriginalQueryForBatchLevelStatement(TSqlParser::Batch_level_statementContex
43724373
originalQueryCopy.replace(startIndex, endIndex - startIndex, "CREATE");
43734374
return pstrdup(originalQueryCopy.c_str());
43744375
}
4376+
else if (ctx->create_or_alter_view() && ctx->create_or_alter_view()->ALTER())
4377+
{
4378+
startIndex = ctx->create_or_alter_view()->ALTER()->getSymbol()->getStartIndex();
4379+
endIndex = startIndex + 5;
4380+
/* if the statement is "ALTER VIEW" */
4381+
if (!ctx->create_or_alter_view()->CREATE())
4382+
{
4383+
originalQueryCopy.replace(startIndex, endIndex - startIndex, "CREATE");
4384+
}
4385+
/* if the statement is "CREATE OR ALTER VIEW" */
4386+
else
4387+
{
4388+
startIndex = ctx->create_or_alter_view()->CREATE()->getSymbol()->getStartIndex();
4389+
alterIndex = ctx->create_or_alter_view()->ALTER()->getSymbol()->getStartIndex();
4390+
endIndex = alterIndex + 5;
4391+
originalQueryCopy.replace(startIndex, endIndex - startIndex, "CREATE");
4392+
}
4393+
return pstrdup(originalQueryCopy.c_str());
4394+
}
43754395
else
43764396
return pstrdup(originalQueryCopy.c_str());
43774397
}

‎contrib/babelfishpg_tsql/src/tsqlUnsupportedFeatureHandler.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -485,9 +485,6 @@ antlrcpp::Any TsqlUnsupportedFeatureHandlerImpl::visitCreate_or_alter_trigger(TS
485485

486486
antlrcpp::Any TsqlUnsupportedFeatureHandlerImpl::visitCreate_or_alter_view(TSqlParser::Create_or_alter_viewContext *ctx)
487487
{
488-
if (ctx->ALTER())
489-
handle(INSTR_UNSUPPORTED_TSQL_ALTER_VIEW, "ALTER VIEW", getLineAndPos(ctx->ALTER()));
490-
491488
/* escape hatch of SCHEMABINDING option*/
492489
if (escape_hatch_schemabinding_view != EH_IGNORE)
493490
{

‎test/JDBC/expected/BABEL-UNSUPPORTED.out

-8
Original file line numberDiff line numberDiff line change
@@ -2414,16 +2414,8 @@ CREATE VIEW v_babel_2017 AS SELECT * FROM t_babel_2017;
24142414
GO
24152415
ALTER VIEW v_babel_2017 AS SELECT a FROM t_babel_2017;
24162416
GO
2417-
~~ERROR (Code: 33557097)~~
2418-
2419-
~~ERROR (Message: 'ALTER VIEW' is not currently supported in Babelfish)~~
2420-
24212417
CREATE OR ALTER VIEW v_babel_2017 AS SELECT b FROM t_babel_2017;
24222418
GO
2423-
~~ERROR (Code: 33557097)~~
2424-
2425-
~~ERROR (Message: 'ALTER VIEW' is not currently supported in Babelfish)~~
2426-
24272419
DROP VIEW v_babel_2017;
24282420
GO
24292421
DROP TABLE t_babel_2017;

0 commit comments

Comments
 (0)
Please sign in to comment.