-
Notifications
You must be signed in to change notification settings - Fork 0
Add pg_get_subscription_ddl() function #1
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
vaibhavdalvi93
wants to merge
4
commits into
main
Choose a base branch
from
pg156-add_func_get_sub_ddl
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ | |
| #include "catalog/pg_partitioned_table.h" | ||
| #include "catalog/pg_proc.h" | ||
| #include "catalog/pg_statistic_ext.h" | ||
| #include "catalog/pg_subscription.h" | ||
| #include "catalog/pg_trigger.h" | ||
| #include "catalog/pg_type.h" | ||
| #include "commands/defrem.h" | ||
|
|
@@ -57,6 +58,7 @@ | |
| #include "rewrite/rewriteHandler.h" | ||
| #include "rewrite/rewriteManip.h" | ||
| #include "rewrite/rewriteSupport.h" | ||
| #include "utils/acl.h" | ||
| #include "utils/array.h" | ||
| #include "utils/builtins.h" | ||
| #include "utils/fmgroids.h" | ||
|
|
@@ -13715,3 +13717,178 @@ get_range_partbound_string(List *bound_datums) | |
|
|
||
| return buf->data; | ||
| } | ||
|
|
||
| /* | ||
| * pg_get_subscription_ddl | ||
| * Get CREATE SUBSCRIPTION statement for the given subscription | ||
| */ | ||
| Datum | ||
| pg_get_subscription_ddl(PG_FUNCTION_ARGS) | ||
| { | ||
| char *subname = text_to_cstring(PG_GETARG_TEXT_P(0)); | ||
| StringInfo pubnames; | ||
| StringInfoData buf; | ||
| HeapTuple tup; | ||
| char *conninfo; | ||
| List *publist; | ||
| Datum datum; | ||
| bool isnull; | ||
|
|
||
| /* | ||
| * To prevent unprivileged users from initiating unauthorized network | ||
| * connections, dumping subscription creation is restricted. A user must | ||
| * be specifically authorized (via the appropriate role privilege) to | ||
| * create subscriptions and/or to read all data. | ||
| */ | ||
| if (!(has_privs_of_role(GetUserId(), ROLE_PG_CREATE_SUBSCRIPTION) || | ||
| has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_DATA))) | ||
| ereport(ERROR, | ||
| (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | ||
| errmsg("permission denied to get the create subscription ddl"), | ||
| errdetail("Only roles with privileges of the \"%s\" and/or \"%s\" role may get ddl.", | ||
| "pg_create_subscription", "pg_read_all_data"))); | ||
|
|
||
| /* Look up the subscription in pg_subscription */ | ||
| tup = SearchSysCache2(SUBSCRIPTIONNAME, ObjectIdGetDatum(MyDatabaseId), | ||
| CStringGetDatum(subname)); | ||
| if (!HeapTupleIsValid(tup)) | ||
| ereport(ERROR, | ||
| (errcode(ERRCODE_UNDEFINED_OBJECT), | ||
| errmsg("subscription \"%s\" does not exist", subname))); | ||
|
|
||
| initStringInfo(&buf); | ||
|
|
||
| /* Build the CREATE SUBSCRIPTION statement */ | ||
| appendStringInfo(&buf, "CREATE SUBSCRIPTION %s ", | ||
| quote_identifier(subname)); | ||
|
|
||
| /* Get conninfo */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subconninfo); | ||
| conninfo = TextDatumGetCString(datum); | ||
|
|
||
| /* Append connection info to the CREATE SUBSCRIPTION statement */ | ||
| appendStringInfo(&buf, "CONNECTION \'%s\'", conninfo); | ||
vaibhavdalvi93 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /* Build list of quoted publications and append them to query */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subpublications); | ||
| publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); | ||
| pubnames = makeStringInfo(); | ||
| GetPublicationsStr(publist, pubnames, false); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should |
||
| appendStringInfo(&buf, " PUBLICATION %s", pubnames->data); | ||
|
|
||
| /* | ||
| * Add options using WITH clause. The 'connect' option value given at the | ||
| * time of subscription creation is not available in the catalog. When | ||
| * creating a subscription, the remote host is not reachable or in an | ||
| * unclear state, in that case, the subscription can be created using | ||
| * 'connect = false' option. This is what pg_dump uses. | ||
| * | ||
| * The status or value of the options 'create_slot' and 'copy_data' not | ||
| * available in the catalog table. We can use default values i.e. TRUE | ||
| * for both. This is what pg_dump uses. | ||
| */ | ||
| appendStringInfoString(&buf, " WITH (connect = false"); | ||
|
|
||
| /* Get slotname */ | ||
| datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subslotname, | ||
| &isnull); | ||
| if (!isnull) | ||
| appendStringInfo(&buf, ", slot_name = \'%s\'", | ||
| NameStr(*DatumGetName(datum))); | ||
| else | ||
| { | ||
| appendStringInfoString(&buf, ", slot_name = none"); | ||
| /* Setting slot_name to none must set create_slot to false */ | ||
| appendStringInfoString(&buf, ", create_slot = false"); | ||
| } | ||
|
|
||
| /* Get enabled option */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subenabled); | ||
| /* Setting 'slot_name' to none must set 'enabled' to false as well */ | ||
| if (!DatumGetBool(datum) || isnull) | ||
| appendStringInfoString(&buf, ", enabled = false"); | ||
| else | ||
| appendStringInfoString(&buf, ", enabled = true"); | ||
|
|
||
| /* Get binary option */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subbinary); | ||
| appendStringInfo(&buf, ", binary = %s", | ||
| DatumGetBool(datum) ? "true" : "false"); | ||
|
|
||
| /* Get streaming option */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_substream); | ||
| if (DatumGetChar(datum) == LOGICALREP_STREAM_OFF) | ||
| appendStringInfoString(&buf, ", streaming = off"); | ||
| else if (DatumGetChar(datum) == LOGICALREP_STREAM_ON) | ||
| appendStringInfoString(&buf, ", streaming = on"); | ||
| else | ||
| appendStringInfoString(&buf, ", streaming = parallel"); | ||
|
|
||
| /* Get sync commit option */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subsynccommit); | ||
| appendStringInfo(&buf, ", synchronous_commit = %s", | ||
| TextDatumGetCString(datum)); | ||
|
|
||
| /* Get two-phase commit option */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subtwophasestate); | ||
| if (DatumGetChar(datum) == LOGICALREP_TWOPHASE_STATE_DISABLED) | ||
| appendStringInfoString(&buf, ", two_phase = off"); | ||
| else | ||
| appendStringInfoString(&buf, ", two_phase = on"); | ||
|
|
||
| /* Disable on error? */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subdisableonerr); | ||
| appendStringInfo(&buf, ", disable_on_error = %s", | ||
| DatumGetBool(datum) ? "on" : "off"); | ||
|
|
||
| /* Password required? */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subpasswordrequired); | ||
| appendStringInfo(&buf, ", password_required = %s", | ||
| DatumGetBool(datum) ? "on" : "off"); | ||
|
|
||
| /* Run as owner? */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subrunasowner); | ||
| appendStringInfo(&buf, ", run_as_owner = %s", | ||
| DatumGetBool(datum) ? "on" : "off"); | ||
|
|
||
| /* Get origin */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_suborigin); | ||
| appendStringInfo(&buf, ", origin = %s", TextDatumGetCString(datum)); | ||
|
|
||
| /* Failover? */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subfailover); | ||
| appendStringInfo(&buf, ", failover = %s", | ||
| DatumGetBool(datum) ? "on" : "off"); | ||
|
|
||
| /* Retain dead tuples? */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_subretaindeadtuples); | ||
| appendStringInfo(&buf, ", retain_dead_tuples = %s", | ||
| DatumGetBool(datum) ? "on" : "off"); | ||
|
|
||
| /* Max retention duration */ | ||
| datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup, | ||
| Anum_pg_subscription_submaxretention); | ||
| appendStringInfo(&buf, ", max_retention_duration = %lu", | ||
| Int32GetDatum(datum)); | ||
|
|
||
| /* Finally close parenthesis and add semicolon to the statement */ | ||
| appendStringInfoString(&buf, ");"); | ||
|
|
||
| ReleaseSysCache(tup); | ||
|
|
||
| PG_RETURN_TEXT_P(string_to_text(buf.data)); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| -- | ||
| -- Get CREATE SUBSCRIPTION statement | ||
| -- | ||
| CREATE ROLE createsub_role LOGIN; | ||
| CREATE ROLE readalldata_role LOGIN; | ||
| -- Create subscription with minimal options | ||
| CREATE SUBSCRIPTION testsub1 CONNECTION 'dbname=db_doesnotexist' | ||
| PUBLICATION testpub1 WITH (connect=false); | ||
| WARNING: subscription was created, but is not connected | ||
| HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription. | ||
| -- Check that the subscription ddl is correctly created | ||
| SELECT pg_get_subscription_ddl('testsub1'); | ||
| pg_get_subscription_ddl | ||
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ||
| CREATE SUBSCRIPTION testsub1 CONNECTION 'dbname=db_doesnotexist' PUBLICATION "testpub1" WITH (connect = false, slot_name = 'testsub1', enabled = false, binary = false, streaming = parallel, synchronous_commit = off, two_phase = off, disable_on_error = off, password_required = on, run_as_owner = off, origin = any, failover = off, retain_dead_tuples = off, max_retention_duration = 0); | ||
| (1 row) | ||
|
|
||
| -- Create subscription with more options | ||
| CREATE SUBSCRIPTION "TestSubddL2" CONNECTION 'host=unknown user=dvd password=pass123' | ||
| PUBLICATION "testpub2", "TestPub3" WITH (connect=false, slot_name='slot1', | ||
| enabled=off); | ||
| WARNING: subscription was created, but is not connected | ||
| HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription. | ||
| SELECT pg_get_subscription_ddl('TestSubddL2'); | ||
| pg_get_subscription_ddl | ||
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ||
| CREATE SUBSCRIPTION "TestSubddL2" CONNECTION 'host=unknown user=dvd password=pass123' PUBLICATION "testpub2", "TestPub3" WITH (connect = false, slot_name = 'slot1', enabled = false, binary = false, streaming = parallel, synchronous_commit = off, two_phase = off, disable_on_error = off, password_required = on, run_as_owner = off, origin = any, failover = off, retain_dead_tuples = off, max_retention_duration = 0); | ||
| (1 row) | ||
|
|
||
| -- Create subscription with all options | ||
| CREATE SUBSCRIPTION testsub3 CONNECTION 'host=unknown user=dvd password=pass12' | ||
| PUBLICATION testpub4 WITH (connect=false, slot_name=none, enabled=false, | ||
| create_slot=false, copy_data=false, binary=true, streaming=off, | ||
| synchronous_commit=local, two_phase=true, disable_on_error=true, | ||
| password_required=false, run_as_owner=true, origin=none, failover=true, | ||
| retain_dead_tuples=false, max_retention_duration=100); | ||
| NOTICE: max_retention_duration is ineffective when retain_dead_tuples is disabled | ||
| WARNING: subscription was created, but is not connected | ||
| HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription. | ||
| SELECT pg_get_subscription_ddl('testsub3'); | ||
| pg_get_subscription_ddl | ||
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ||
| CREATE SUBSCRIPTION testsub3 CONNECTION 'host=unknown user=dvd password=pass12' PUBLICATION "testpub4" WITH (connect = false, slot_name = none, create_slot = false, enabled = false, binary = true, streaming = off, synchronous_commit = local, two_phase = on, disable_on_error = on, password_required = off, run_as_owner = on, origin = none, failover = on, retain_dead_tuples = off, max_retention_duration = 100); | ||
| (1 row) | ||
|
|
||
| -- Non-superusers and which don't have pg_create_subscription and/or | ||
| -- pg_read_all_data permission can't get ddl | ||
| SET SESSION AUTHORIZATION 'createsub_role'; | ||
| SELECT pg_get_subscription_ddl('TestSubddL2'); | ||
| ERROR: permission denied to get the create subscription ddl | ||
| DETAIL: Only roles with privileges of the "pg_create_subscription" and/or "pg_read_all_data" role may get ddl. | ||
| RESET SESSION AUTHORIZATION; | ||
| SET SESSION AUTHORIZATION 'readalldata_role'; | ||
| SELECT pg_get_subscription_ddl('TestSubddL2'); | ||
| ERROR: permission denied to get the create subscription ddl | ||
| DETAIL: Only roles with privileges of the "pg_create_subscription" and/or "pg_read_all_data" role may get ddl. | ||
| RESET SESSION AUTHORIZATION; | ||
| -- Administrators can change who can access this function | ||
| GRANT pg_create_subscription TO createsub_role; | ||
| GRANT pg_read_all_data TO readalldata_role; | ||
| SET SESSION AUTHORIZATION 'createsub_role'; | ||
| SELECT pg_get_subscription_ddl('TestSubddL2'); | ||
| pg_get_subscription_ddl | ||
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ||
| CREATE SUBSCRIPTION "TestSubddL2" CONNECTION 'host=unknown user=dvd password=pass123' PUBLICATION "testpub2", "TestPub3" WITH (connect = false, slot_name = 'slot1', enabled = false, binary = false, streaming = parallel, synchronous_commit = off, two_phase = off, disable_on_error = off, password_required = on, run_as_owner = off, origin = any, failover = off, retain_dead_tuples = off, max_retention_duration = 0); | ||
| (1 row) | ||
|
|
||
| RESET SESSION AUTHORIZATION; | ||
| SET SESSION AUTHORIZATION 'readalldata_role'; | ||
| SELECT pg_get_subscription_ddl('TestSubddL2'); | ||
| pg_get_subscription_ddl | ||
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ||
| CREATE SUBSCRIPTION "TestSubddL2" CONNECTION 'host=unknown user=dvd password=pass123' PUBLICATION "testpub2", "TestPub3" WITH (connect = false, slot_name = 'slot1', enabled = false, binary = false, streaming = parallel, synchronous_commit = off, two_phase = off, disable_on_error = off, password_required = on, run_as_owner = off, origin = any, failover = off, retain_dead_tuples = off, max_retention_duration = 0); | ||
| (1 row) | ||
|
|
||
| RESET SESSION AUTHORIZATION; | ||
| REVOKE pg_create_subscription FROM createsub_role; | ||
| REVOKE pg_read_all_data FROM readalldata_role; | ||
| ALTER SUBSCRIPTION testsub1 SET (slot_name=NONE); | ||
| DROP SUBSCRIPTION testsub1; | ||
| ALTER SUBSCRIPTION "TestSubddL2" SET (slot_name=NONE); | ||
| DROP SUBSCRIPTION "TestSubddL2"; | ||
| DROP SUBSCRIPTION testsub3; | ||
| DROP ROLE createsub_role; | ||
| DROP ROLE readalldata_role; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.