|
39 | 39 | #include "commands/tablecmds.h"
|
40 | 40 | #include "commands/trigger.h"
|
41 | 41 | #include "commands/user.h"
|
| 42 | +#include "commands/view.h" |
42 | 43 | #include "common/md5.h"
|
43 | 44 | #include "common/string.h"
|
44 | 45 | #include "funcapi.h"
|
|
63 | 64 | #include "utils/acl.h"
|
64 | 65 | #include "utils/builtins.h"
|
65 | 66 | #include "utils/guc_tables.h"
|
| 67 | +#include "utils/inval.h" |
66 | 68 | #include "utils/lsyscache.h"
|
67 | 69 | #include "utils/plancache.h"
|
68 | 70 | #include "utils/ps_status.h"
|
@@ -142,6 +144,8 @@ static void call_prev_ProcessUtility(PlannedStmt *pstmt,
|
142 | 144 | QueryCompletion *qc);
|
143 | 145 | static void set_pgtype_byval(List *name, bool byval);
|
144 | 146 | 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); |
145 | 149 | static bool pltsql_truncate_identifier(char *ident, int len, bool warn);
|
146 | 150 | static Name pltsql_cstr_to_name(char *s, int len);
|
147 | 151 | extern void pltsql_add_guc_plan(CachedPlanSource *plansource);
|
@@ -2766,6 +2770,117 @@ bbf_ProcessUtility(PlannedStmt *pstmt,
|
2766 | 2770 | }
|
2767 | 2771 | break;
|
2768 | 2772 | }
|
| 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 | + |
2769 | 2884 | case T_AlterTableStmt:
|
2770 | 2885 | {
|
2771 | 2886 | AlterTableStmt *atstmt = (AlterTableStmt *) parsetree;
|
@@ -4814,6 +4929,73 @@ pltsql_proc_get_oid_proname_proacl(AlterFunctionStmt *stmt, ParseState *pstate,
|
4814 | 4929 | *isSameFunc = OidIsValid(funcOid);
|
4815 | 4930 | }
|
4816 | 4931 |
|
| 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 | + |
4817 | 4999 | /*
|
4818 | 5000 | * Update the pg_type catalog entry for the given name to have
|
4819 | 5001 | * typbyval set to the given value.
|
|
0 commit comments