@@ -90,6 +90,8 @@ typedef struct ExtensionControlFile
90
90
* MODULE_PATHNAME */
91
91
char * comment ; /* comment, if any */
92
92
char * schema ; /* target schema (allowed if !relocatable) */
93
+ bool owned_schema ; /* if the schema should be owned by the
94
+ * extension */
93
95
bool relocatable ; /* is ALTER EXTENSION SET SCHEMA supported? */
94
96
bool superuser ; /* must be superuser to install? */
95
97
bool trusted ; /* allow becoming superuser on the fly? */
@@ -611,6 +613,14 @@ parse_extension_control_file(ExtensionControlFile *control,
611
613
{
612
614
control -> schema = pstrdup (item -> value );
613
615
}
616
+ else if (strcmp (item -> name , "owned_schema" ) == 0 )
617
+ {
618
+ if (!parse_bool (item -> value , & control -> owned_schema ))
619
+ ereport (ERROR ,
620
+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
621
+ errmsg ("parameter \"%s\" requires a Boolean value" ,
622
+ item -> name )));
623
+ }
614
624
else if (strcmp (item -> name , "relocatable" ) == 0 )
615
625
{
616
626
if (!parse_bool (item -> value , & control -> relocatable ))
@@ -1744,8 +1754,11 @@ CreateExtensionInternal(char *extensionName,
1744
1754
*/
1745
1755
if (schemaName )
1746
1756
{
1747
- /* If the user is giving us the schema name, it must exist already. */
1748
- schemaOid = get_namespace_oid (schemaName , false);
1757
+ /*
1758
+ * If the user is giving us the schema name, it must exist already if
1759
+ * the extension does not want to own the schema
1760
+ */
1761
+ schemaOid = get_namespace_oid (schemaName , control -> owned_schema );
1749
1762
}
1750
1763
1751
1764
if (control -> schema != NULL )
@@ -1767,7 +1780,10 @@ CreateExtensionInternal(char *extensionName,
1767
1780
1768
1781
/* Always use the schema from control file for current extension. */
1769
1782
schemaName = control -> schema ;
1783
+ }
1770
1784
1785
+ if (schemaName )
1786
+ {
1771
1787
/* Find or create the schema in case it does not exist. */
1772
1788
schemaOid = get_namespace_oid (schemaName , true);
1773
1789
@@ -1788,8 +1804,22 @@ CreateExtensionInternal(char *extensionName,
1788
1804
*/
1789
1805
schemaOid = get_namespace_oid (schemaName , false);
1790
1806
}
1807
+ else if (control -> owned_schema )
1808
+ {
1809
+ ereport (ERROR ,
1810
+ (errcode (ERRCODE_DUPLICATE_SCHEMA ),
1811
+ errmsg ("schema \"%s\" already exists" ,
1812
+ schemaName )));
1813
+ }
1814
+
1791
1815
}
1792
- else if (!OidIsValid (schemaOid ))
1816
+ else if (control -> owned_schema )
1817
+ {
1818
+ ereport (ERROR ,
1819
+ (errcode (ERRCODE_UNDEFINED_SCHEMA ),
1820
+ errmsg ("no schema has been selected to create in" )));
1821
+ }
1822
+ else
1793
1823
{
1794
1824
/*
1795
1825
* Neither user nor author of the extension specified schema; use the
@@ -1856,6 +1886,7 @@ CreateExtensionInternal(char *extensionName,
1856
1886
*/
1857
1887
address = InsertExtensionTuple (control -> name , extowner ,
1858
1888
schemaOid , control -> relocatable ,
1889
+ control -> owned_schema ,
1859
1890
versionName ,
1860
1891
PointerGetDatum (NULL ),
1861
1892
PointerGetDatum (NULL ),
@@ -2061,7 +2092,8 @@ CreateExtension(ParseState *pstate, CreateExtensionStmt *stmt)
2061
2092
*/
2062
2093
ObjectAddress
2063
2094
InsertExtensionTuple (const char * extName , Oid extOwner ,
2064
- Oid schemaOid , bool relocatable , const char * extVersion ,
2095
+ Oid schemaOid , bool relocatable , bool ownedSchema ,
2096
+ const char * extVersion ,
2065
2097
Datum extConfig , Datum extCondition ,
2066
2098
List * requiredExtensions )
2067
2099
{
@@ -2091,6 +2123,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
2091
2123
values [Anum_pg_extension_extowner - 1 ] = ObjectIdGetDatum (extOwner );
2092
2124
values [Anum_pg_extension_extnamespace - 1 ] = ObjectIdGetDatum (schemaOid );
2093
2125
values [Anum_pg_extension_extrelocatable - 1 ] = BoolGetDatum (relocatable );
2126
+ values [Anum_pg_extension_extownedschema - 1 ] = BoolGetDatum (ownedSchema );
2094
2127
values [Anum_pg_extension_extversion - 1 ] = CStringGetTextDatum (extVersion );
2095
2128
2096
2129
if (extConfig == PointerGetDatum (NULL ))
@@ -2135,6 +2168,17 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
2135
2168
record_object_address_dependencies (& myself , refobjs , DEPENDENCY_NORMAL );
2136
2169
free_object_addresses (refobjs );
2137
2170
2171
+ if (ownedSchema )
2172
+ {
2173
+ ObjectAddress schemaAddress = {
2174
+ .classId = NamespaceRelationId ,
2175
+ .objectId = schemaOid ,
2176
+ };
2177
+
2178
+ recordDependencyOn (& schemaAddress , & myself , DEPENDENCY_EXTENSION );
2179
+ }
2180
+
2181
+
2138
2182
/* Post creation hook for new extension */
2139
2183
InvokeObjectPostCreateHook (ExtensionRelationId , extensionOid , 0 );
2140
2184
@@ -3053,11 +3097,10 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
3053
3097
HeapTuple depTup ;
3054
3098
ObjectAddresses * objsMoved ;
3055
3099
ObjectAddress extAddr ;
3100
+ bool ownedSchema ;
3056
3101
3057
3102
extensionOid = get_extension_oid (extensionName , false);
3058
3103
3059
- nspOid = LookupCreationNamespace (newschema );
3060
-
3061
3104
/*
3062
3105
* Permission check: must own extension. Note that we don't bother to
3063
3106
* check ownership of the individual member objects ...
@@ -3066,22 +3109,6 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
3066
3109
aclcheck_error (ACLCHECK_NOT_OWNER , OBJECT_EXTENSION ,
3067
3110
extensionName );
3068
3111
3069
- /* Permission check: must have creation rights in target namespace */
3070
- aclresult = object_aclcheck (NamespaceRelationId , nspOid , GetUserId (), ACL_CREATE );
3071
- if (aclresult != ACLCHECK_OK )
3072
- aclcheck_error (aclresult , OBJECT_SCHEMA , newschema );
3073
-
3074
- /*
3075
- * If the schema is currently a member of the extension, disallow moving
3076
- * the extension into the schema. That would create a dependency loop.
3077
- */
3078
- if (getExtensionOfObject (NamespaceRelationId , nspOid ) == extensionOid )
3079
- ereport (ERROR ,
3080
- (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
3081
- errmsg ("cannot move extension \"%s\" into schema \"%s\" "
3082
- "because the extension contains the schema" ,
3083
- extensionName , newschema )));
3084
-
3085
3112
/* Locate the pg_extension tuple */
3086
3113
extRel = table_open (ExtensionRelationId , RowExclusiveLock );
3087
3114
@@ -3105,14 +3132,38 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
3105
3132
3106
3133
systable_endscan (extScan );
3107
3134
3108
- /*
3109
- * If the extension is already in the target schema, just silently do
3110
- * nothing.
3111
- */
3112
- if (extForm -> extnamespace == nspOid )
3135
+ ownedSchema = extForm -> extownedschema ;
3136
+
3137
+ if (!ownedSchema )
3113
3138
{
3114
- table_close (extRel , RowExclusiveLock );
3115
- return InvalidObjectAddress ;
3139
+ nspOid = LookupCreationNamespace (newschema );
3140
+
3141
+ /* Permission check: must have creation rights in target namespace */
3142
+ aclresult = object_aclcheck (NamespaceRelationId , nspOid , GetUserId (), ACL_CREATE );
3143
+ if (aclresult != ACLCHECK_OK )
3144
+ aclcheck_error (aclresult , OBJECT_SCHEMA , newschema );
3145
+
3146
+ /*
3147
+ * If the schema is currently a member of the extension, disallow
3148
+ * moving the extension into the schema. That would create a
3149
+ * dependency loop.
3150
+ */
3151
+ if (getExtensionOfObject (NamespaceRelationId , nspOid ) == extensionOid )
3152
+ ereport (ERROR ,
3153
+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
3154
+ errmsg ("cannot move extension \"%s\" into schema \"%s\" "
3155
+ "because the extension contains the schema" ,
3156
+ extensionName , newschema )));
3157
+
3158
+ /*
3159
+ * If the extension is already in the target schema, just silently do
3160
+ * nothing.
3161
+ */
3162
+ if (extForm -> extnamespace == nspOid )
3163
+ {
3164
+ table_close (extRel , RowExclusiveLock );
3165
+ return InvalidObjectAddress ;
3166
+ }
3116
3167
}
3117
3168
3118
3169
/* Check extension is supposed to be relocatable */
@@ -3185,6 +3236,13 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
3185
3236
}
3186
3237
}
3187
3238
3239
+ /*
3240
+ * We don't actually have to move any objects anything for owned
3241
+ * schemas, because we simply rename the schema.
3242
+ */
3243
+ if (ownedSchema )
3244
+ continue ;
3245
+
3188
3246
/*
3189
3247
* Otherwise, ignore non-membership dependencies. (Currently, the
3190
3248
* only other case we could see here is a normal dependency from
@@ -3228,18 +3286,26 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
3228
3286
3229
3287
relation_close (depRel , AccessShareLock );
3230
3288
3231
- /* Now adjust pg_extension.extnamespace */
3232
- extForm -> extnamespace = nspOid ;
3289
+ if (ownedSchema )
3290
+ {
3291
+ RenameSchema (get_namespace_name (oldNspOid ), newschema );
3292
+ table_close (extRel , RowExclusiveLock );
3293
+ }
3294
+ else
3295
+ {
3296
+ /* Now adjust pg_extension.extnamespace */
3297
+ extForm -> extnamespace = nspOid ;
3233
3298
3234
- CatalogTupleUpdate (extRel , & extTup -> t_self , extTup );
3299
+ CatalogTupleUpdate (extRel , & extTup -> t_self , extTup );
3235
3300
3236
- table_close (extRel , RowExclusiveLock );
3301
+ table_close (extRel , RowExclusiveLock );
3237
3302
3238
- /* update dependency to point to the new schema */
3239
- if (changeDependencyFor (ExtensionRelationId , extensionOid ,
3240
- NamespaceRelationId , oldNspOid , nspOid ) != 1 )
3241
- elog (ERROR , "could not change schema dependency for extension %s" ,
3242
- NameStr (extForm -> extname ));
3303
+ /* update dependency to point to the new schema */
3304
+ if (changeDependencyFor (ExtensionRelationId , extensionOid ,
3305
+ NamespaceRelationId , oldNspOid , nspOid ) != 1 )
3306
+ elog (ERROR , "could not change schema dependency for extension %s" ,
3307
+ NameStr (extForm -> extname ));
3308
+ }
3243
3309
3244
3310
InvokeObjectPostAlterHook (ExtensionRelationId , extensionOid , 0 );
3245
3311
0 commit comments