From 9d17c5e7f2292699b91d5ebd2dbfe3ce2a3e0aad Mon Sep 17 00:00:00 2001 From: Fabiano Honorato Date: Thu, 22 Aug 2024 15:39:03 -0300 Subject: [PATCH 1/9] Add event trigger resource --- postgresql/provider.go | 4 +- .../resource_postgresql_event_trigger.go | 444 ++++++++++++++++++ 2 files changed, 447 insertions(+), 1 deletion(-) create mode 100644 postgresql/resource_postgresql_event_trigger.go diff --git a/postgresql/provider.go b/postgresql/provider.go index 7f15c92e..739985b4 100644 --- a/postgresql/provider.go +++ b/postgresql/provider.go @@ -3,9 +3,10 @@ package postgresql import ( "context" "fmt" + "os" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "os" "github.com/blang/semver" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -190,6 +191,7 @@ func Provider() *schema.Provider { "postgresql_extension": resourcePostgreSQLExtension(), "postgresql_grant": resourcePostgreSQLGrant(), "postgresql_grant_role": resourcePostgreSQLGrantRole(), + "postgresql_event_trigger": resourcePostgreSQLEventTrigger(), "postgresql_replication_slot": resourcePostgreSQLReplicationSlot(), "postgresql_publication": resourcePostgreSQLPublication(), "postgresql_subscription": resourcePostgreSQLSubscription(), diff --git a/postgresql/resource_postgresql_event_trigger.go b/postgresql/resource_postgresql_event_trigger.go new file mode 100644 index 00000000..70bb6605 --- /dev/null +++ b/postgresql/resource_postgresql_event_trigger.go @@ -0,0 +1,444 @@ +package postgresql + +import ( + "bytes" + "database/sql" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/lib/pq" +) + +const ( + eventTriggerNameAttr = "name" + eventTriggerOnAttr = "on" + eventTriggerFunctionAttr = "function" + eventTriggerFilterAttr = "filter" + eventTriggerFilterVariableAttr = "variable" + eventTriggerFilterValueAttr = "values" + eventTriggerDatabaseAttr = "database" + eventTriggerSchemaAttr = "schema" + eventTriggerOwnerAttr = "owner" + eventTriggerEnabledAttr = "enabled" +) + +func resourcePostgreSQLEventTrigger() *schema.Resource { + return &schema.Resource{ + Create: PGResourceFunc(resourcePostgreSQLEventTriggerCreate), + Read: PGResourceFunc(resourcePostgreSQLEventTriggerRead), + Update: PGResourceFunc(resourcePostgreSQLEventTriggerUpdate), + Delete: PGResourceFunc(resourcePostgreSQLEventTriggerDelete), + Exists: PGResourceExistsFunc(resourcePostgreSQLEventTriggerExists), + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + eventTriggerNameAttr: { + Type: schema.TypeString, + Required: true, + Description: "The name of the event trigger to create", + }, + eventTriggerOnAttr: { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The event the trigger will listen to", + ValidateFunc: validation.StringInSlice([]string{ + "ddl_command_start", + "ddl_command_end", + "sql_drop", + "table_rewrite", + }, false), + }, + eventTriggerFunctionAttr: { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "A function that is declared as taking no argument and returning type event_trigger", + }, + eventTriggerFilterAttr: { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + eventTriggerFilterVariableAttr: { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The name of a variable used to filter events. Currently the only supported value is TAG", + ValidateFunc: validation.StringInSlice([]string{ + "TAG", + }, false), + }, + + eventTriggerFilterValueAttr: { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Description: "A list of values for the associated filter_variable for which the trigger should fire", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + eventTriggerDatabaseAttr: { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + Description: "The database where the event trigger is located. If not specified, the provider default database is used.", + }, + eventTriggerSchemaAttr: { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + Description: "Schema where the function is located. If not specified, the provider default schema is used.", + }, + eventTriggerEnabledAttr: { + Type: schema.TypeString, + Optional: true, + Default: "enable", + Description: "These configure the firing of event triggers. A disabled trigger is still known to the system, but is not executed when its triggering event occurs", + ValidateFunc: validation.StringInSlice([]string{ + "disable", + "enable", + "enable_replica", + "enable_always", + }, false), + }, + eventTriggerOwnerAttr: { + Type: schema.TypeString, + Required: true, + Description: "The user name of the owner of the event trigger. You can't use 'current_role', 'current_user' or 'session_user' in order to avoid drifts", + ValidateFunc: validation.StringNotInSlice([]string{ + "current_role", + "current_user", + "session_user", + }, true), + }, + }, + } +} + +func resourcePostgreSQLEventTriggerCreate(db *DBConnection, d *schema.ResourceData) error { + if err := createEventTrigger(db, d); err != nil { + return err + } + + d.SetId(d.Get(eventTriggerNameAttr).(string)) + + return nil +} + +func resourcePostgreSQLEventTriggerUpdate(db *DBConnection, d *schema.ResourceData) error { + if err := updateEventTrigger(db, d); err != nil { + return err + } + + d.SetId(d.Get(eventTriggerNameAttr).(string)) + + return nil +} + +func resourcePostgreSQLEventTriggerDelete(db *DBConnection, d *schema.ResourceData) error { + if err := deleteEventTrigger(db, d); err != nil { + return err + } + + d.SetId(d.Get(eventTriggerNameAttr).(string)) + + return nil +} + +func resourcePostgreSQLEventTriggerRead(db *DBConnection, d *schema.ResourceData) error { + return readEventTrigger(db, d) +} + +func resourcePostgreSQLEventTriggerExists(db *DBConnection, d *schema.ResourceData) (bool, error) { + database, eventTriggerName, err := getDBEventTriggerName(d, db.client.databaseName) + if err != nil { + return false, err + } + + // Check if the database exists + exists, err := dbExists(db, database) + if err != nil || !exists { + return false, err + } + + txn, err := startTransaction(db.client, database) + if err != nil { + return false, err + } + defer deferredRollback(txn) + + err = txn.QueryRow("SELECT evtname FROM pg_event_trigger WHERE evtname=$1", eventTriggerName).Scan(&eventTriggerName) + switch { + case err == sql.ErrNoRows: + return false, nil + case err != nil: + return false, fmt.Errorf("Error reading schema: %w", err) + } + + return true, nil +} + +func getDBEventTriggerName(d *schema.ResourceData, databaseName string) (string, string, error) { + database := getDatabase(d, databaseName) + eventTriggerName := d.Get(eventTriggerNameAttr).(string) + + // When importing, we have to parse the ID to find event trigger and database names. + if eventTriggerName == "" { + parsed := strings.Split(d.Id(), ".") + if len(parsed) != 2 { + return "", "", fmt.Errorf("schema ID %s has not the expected format 'database.event_trigger': %v", d.Id(), parsed) + } + database = parsed[0] + eventTriggerName = parsed[1] + } + + return database, eventTriggerName, nil +} + +func createEventTrigger(db *DBConnection, d *schema.ResourceData) error { + eventTriggerName := d.Get(eventTriggerNameAttr).(string) + b := bytes.NewBufferString("CREATE EVENT TRIGGER ") + fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) + + eventTriggerOn := d.Get(eventTriggerOnAttr).(string) + fmt.Fprint(b, " ON ", eventTriggerOn) + + if filters, ok := d.GetOk(eventTriggerFilterAttr); ok { + filters := filters.([]interface{}) + + for i, filter := range filters { + filter := filter.(map[string]interface{}) + + if variable, ok := filter[eventTriggerFilterVariableAttr]; ok { + if i == 0 { + fmt.Fprint(b, " WHEN ", variable) + } else { + fmt.Fprint(b, " AND ", variable) + } + } + + if values, ok := filter[eventTriggerFilterValueAttr]; ok { + var new_values []string + + for _, value := range values.([]interface{}) { + new_values = append(new_values, pq.QuoteLiteral(value.(string))) + } + + fmt.Fprint(b, " IN (", strings.Join(new_values, ","), ")") + } + } + } + + eventTriggerFunction := d.Get(eventTriggerFunctionAttr).(string) + eventTriggerSchema := d.Get(eventTriggerSchemaAttr).(string) + fmt.Fprint(b, " EXECUTE FUNCTION ", pq.QuoteIdentifier(eventTriggerSchema), ".", eventTriggerFunction, "()") + + createSql := b.String() + + // Enable or disable the event trigger + b = bytes.NewBufferString("ALTER EVENT TRIGGER ") + fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) + + eventTriggerEnabled := d.Get(eventTriggerEnabledAttr).(string) + fmt.Fprint(b, " ", eventTriggerEnabled) + + statusSql := b.String() + + // Table owner + b = bytes.NewBufferString("ALTER EVENT TRIGGER ") + eventTriggerOwner := d.Get(eventTriggerOwnerAttr).(string) + fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName), " OWNER TO ", eventTriggerOwner) + + ownerSql := b.String() + + // Start transaction + txn, err := startTransaction(db.client, d.Get(eventTriggerDatabaseAttr).(string)) + if err != nil { + return err + } + defer deferredRollback(txn) + + if _, err := txn.Exec(createSql); err != nil { + return err + } + + if _, err := txn.Exec(statusSql); err != nil { + return err + } + + if _, err := txn.Exec(ownerSql); err != nil { + return err + } + + if err := txn.Commit(); err != nil { + return err + } + + return nil +} + +func updateEventTrigger(db *DBConnection, d *schema.ResourceData) error { + eventTriggerName := d.Get(eventTriggerNameAttr).(string) + + // Enable or disable the event trigger + b := bytes.NewBufferString("ALTER EVENT TRIGGER ") + fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) + + eventTriggerEnabled := d.Get(eventTriggerEnabledAttr).(string) + fmt.Fprint(b, " ", eventTriggerEnabled) + + statusSql := b.String() + + // Table owner + b = bytes.NewBufferString("ALTER EVENT TRIGGER ") + eventTriggerOwner := d.Get(eventTriggerOwnerAttr).(string) + fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName), " OWNER TO ", eventTriggerOwner) + + ownerSql := b.String() + + txn, err := startTransaction(db.client, d.Get(eventTriggerDatabaseAttr).(string)) + if err != nil { + return err + } + defer deferredRollback(txn) + + if _, err := txn.Exec(statusSql); err != nil { + return err + } + + if _, err := txn.Exec(ownerSql); err != nil { + return err + } + + if err := txn.Commit(); err != nil { + return err + } + + return nil +} + +func deleteEventTrigger(db *DBConnection, d *schema.ResourceData) error { + eventTriggerName := d.Get(eventTriggerNameAttr).(string) + b := bytes.NewBufferString("DROP EVENT TRIGGER ") + fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) + + sql := b.String() + + txn, err := startTransaction(db.client, d.Get(eventTriggerDatabaseAttr).(string)) + if err != nil { + return err + } + defer deferredRollback(txn) + + if _, err := txn.Exec(sql); err != nil { + return err + } + + if err := txn.Commit(); err != nil { + return err + } + + return nil +} + +func readEventTrigger(db *DBConnection, d *schema.ResourceData) error { + database, eventTriggerName, err := getDBEventTriggerName(d, db.client.databaseName) + if err != nil { + return err + } + + query := `SELECT evtname, evtevent, proname, nspname, evtenabled, evttags, usename ` + + `FROM pg_catalog.pg_event_trigger ` + + `JOIN pg_catalog.pg_user on pg_catalog.pg_event_trigger.evtowner = pg_catalog.pg_user.usesysid ` + + `JOIN pg_catalog.pg_proc on pg_catalog.pg_event_trigger.evtfoid = pg_catalog.pg_proc.oid ` + + `JOIN pg_catalog.pg_namespace on pg_catalog.pg_proc.pronamespace = pg_catalog.pg_namespace.oid ` + + `WHERE evtname=$1` + + var name, on, owner, function, schema string + var enabled string + var tags []string + + values := []interface{}{ + &name, + &on, + &function, + &schema, + &enabled, + (*pq.StringArray)(&tags), + &owner, + } + + txn, err := startTransaction(db.client, database) + if err != nil { + return err + } + defer deferredRollback(txn) + + err = txn.QueryRow(query, eventTriggerName).Scan(values...) + switch { + case err == sql.ErrNoRows: + d.SetId("") + return nil + case err != nil: + return fmt.Errorf("error reading event trigger: %w", err) + } + + if err := txn.Commit(); err != nil { + return err + } + + d.SetId(name) + d.Set("name", name) + d.Set("on", on) + d.Set("function", function) + d.Set("owner", owner) + d.Set("database", database) + d.Set("schema", schema) + + switch enabled { + case "D": + d.Set("enabled", "disable") + case "O": + d.Set("enabled", "enable") + case "R": + d.Set("enabled", "enable_replica") + case "A": + d.Set("enabled", "enable_always") + } + + // TODO: maybe it's better to add a struct + // with types instead of an interface{}? + var filters []interface{} + + if len(tags) > 0 { + var values []string + + for _, tag := range tags { + values = append(values, tag) + } + + filter := map[string]interface{}{ + "variable": "TAG", + "values": values, + } + + filters = append(filters, filter) + } + + d.Set("filter", filters) + + return nil +} From a2644bcb3142e88d608aede5ec5729a1bacd08f2 Mon Sep 17 00:00:00 2001 From: Fabiano Honorato Date: Fri, 23 Aug 2024 16:05:00 -0300 Subject: [PATCH 2/9] Init event trigger tests --- .../resource_postgresql_event_trigger_test.go | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 postgresql/resource_postgresql_event_trigger_test.go diff --git a/postgresql/resource_postgresql_event_trigger_test.go b/postgresql/resource_postgresql_event_trigger_test.go new file mode 100644 index 00000000..1f926112 --- /dev/null +++ b/postgresql/resource_postgresql_event_trigger_test.go @@ -0,0 +1,152 @@ +package postgresql + +import ( + "database/sql" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccPostgresqlEventTrigger_Basic(t *testing.T) { + // Create the database outside of resource.Test + // because we need to create test schemas. + dbSuffix, teardown := setupTestDatabase(t, true, true) + defer teardown() + + schemas := []string{"test_schema1"} + createTestSchemas(t, dbSuffix, schemas, "") + + dbName, _ := getTestDBNames(dbSuffix) + + testAccPostgresqlDataSourceTablesEventTriggerConfig := fmt.Sprintf(testAccPostgreSQLEventTriggerConfig, dbName, schemas[0]) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPostgresqlEventTriggerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccPostgresqlDataSourceTablesEventTriggerConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckPostgresqlEventTriggerExists("postgresql_event_trigger.event_trigger", dbName), + ), + }, + }, + }) +} + +func testAccCheckPostgresqlEventTriggerExists(n string, database string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + client := testAccProvider.Meta().(*Client) + txn, err := startTransaction(client, database) + if err != nil { + return err + } + defer deferredRollback(txn) + + exists, err := checkEventTriggerExists(txn, rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error checking event trigger %s", err) + } + + if !exists { + return fmt.Errorf("Event trigger not found") + } + + return nil + } +} + +func testAccCheckPostgresqlEventTriggerDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "postgresql_event_trigger" { + continue + } + + var database string + for k, v := range rs.Primary.Attributes { + if k == "database" { + database = v + } + } + + txn, err := startTransaction(client, database) + if err != nil { + return err + } + defer deferredRollback(txn) + + exists, err := checkEventTriggerExists(txn, rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error checking event trigger %s", err) + } + + if exists { + return fmt.Errorf("Event trigger still exists after destroy") + } + } + + return nil +} + +func checkEventTriggerExists(txn *sql.Tx, signature string) (bool, error) { + var _rez string + err := txn.QueryRow("SELECT oid FROM pg_catalog.pg_event_trigger WHERE evtname=$1", signature).Scan(&_rez) + switch { + case err == sql.ErrNoRows: + return false, nil + case err != nil: + return false, fmt.Errorf("Error reading info about event trigger: %s", err) + } + + return true, nil +} + +var testAccPostgreSQLEventTriggerConfig = ` +resource "postgresql_function" "function" { + name = "test_function" + database = "%[1]s" + schema = "%[2]s" + + returns = "event_trigger" + language = "plpgsql" + body = <<-EOF + BEGIN + RAISE EXCEPTION 'command % is disabled', tg_tag; + END; + EOF +} + +resource "postgresql_event_trigger" "event_trigger" { + name = "event_trigger_test" + database = "%[1]s" + schema = "%[2]s" + function = postgresql_function.function.name + on = "ddl_command_end" + owner = "postgres" + enabled = "enable" + + filter { + variable = "TAG" + values = [ + "CREATE TABLE", + "ALTER TABLE", + ] + } +} +` From 36669d3d1a2041935aa3670990c48383a9eb2d35 Mon Sep 17 00:00:00 2001 From: Fabiano Honorato Date: Tue, 27 Aug 2024 12:43:40 -0300 Subject: [PATCH 3/9] Tidying and add docs --- .../resource_postgresql_event_trigger.go | 181 +++++++----------- .../resource_postgresql_event_trigger_test.go | 2 +- .../r/postgresql_event_trigger.html.markdown | 74 +++++++ 3 files changed, 148 insertions(+), 109 deletions(-) create mode 100644 website/docs/r/postgresql_event_trigger.html.markdown diff --git a/postgresql/resource_postgresql_event_trigger.go b/postgresql/resource_postgresql_event_trigger.go index 70bb6605..14ae259c 100644 --- a/postgresql/resource_postgresql_event_trigger.go +++ b/postgresql/resource_postgresql_event_trigger.go @@ -21,7 +21,7 @@ const ( eventTriggerDatabaseAttr = "database" eventTriggerSchemaAttr = "schema" eventTriggerOwnerAttr = "owner" - eventTriggerEnabledAttr = "enabled" + eventTriggerStatusAttr = "status" ) func resourcePostgreSQLEventTrigger() *schema.Resource { @@ -102,7 +102,7 @@ func resourcePostgreSQLEventTrigger() *schema.Resource { ForceNew: true, Description: "Schema where the function is located. If not specified, the provider default schema is used.", }, - eventTriggerEnabledAttr: { + eventTriggerStatusAttr: { Type: schema.TypeString, Optional: true, Default: "enable", @@ -129,87 +129,9 @@ func resourcePostgreSQLEventTrigger() *schema.Resource { } func resourcePostgreSQLEventTriggerCreate(db *DBConnection, d *schema.ResourceData) error { - if err := createEventTrigger(db, d); err != nil { - return err - } - - d.SetId(d.Get(eventTriggerNameAttr).(string)) - - return nil -} - -func resourcePostgreSQLEventTriggerUpdate(db *DBConnection, d *schema.ResourceData) error { - if err := updateEventTrigger(db, d); err != nil { - return err - } - - d.SetId(d.Get(eventTriggerNameAttr).(string)) - - return nil -} - -func resourcePostgreSQLEventTriggerDelete(db *DBConnection, d *schema.ResourceData) error { - if err := deleteEventTrigger(db, d); err != nil { - return err - } - - d.SetId(d.Get(eventTriggerNameAttr).(string)) - - return nil -} - -func resourcePostgreSQLEventTriggerRead(db *DBConnection, d *schema.ResourceData) error { - return readEventTrigger(db, d) -} - -func resourcePostgreSQLEventTriggerExists(db *DBConnection, d *schema.ResourceData) (bool, error) { - database, eventTriggerName, err := getDBEventTriggerName(d, db.client.databaseName) - if err != nil { - return false, err - } - - // Check if the database exists - exists, err := dbExists(db, database) - if err != nil || !exists { - return false, err - } - - txn, err := startTransaction(db.client, database) - if err != nil { - return false, err - } - defer deferredRollback(txn) - - err = txn.QueryRow("SELECT evtname FROM pg_event_trigger WHERE evtname=$1", eventTriggerName).Scan(&eventTriggerName) - switch { - case err == sql.ErrNoRows: - return false, nil - case err != nil: - return false, fmt.Errorf("Error reading schema: %w", err) - } - - return true, nil -} - -func getDBEventTriggerName(d *schema.ResourceData, databaseName string) (string, string, error) { - database := getDatabase(d, databaseName) eventTriggerName := d.Get(eventTriggerNameAttr).(string) + d.SetId(eventTriggerName) - // When importing, we have to parse the ID to find event trigger and database names. - if eventTriggerName == "" { - parsed := strings.Split(d.Id(), ".") - if len(parsed) != 2 { - return "", "", fmt.Errorf("schema ID %s has not the expected format 'database.event_trigger': %v", d.Id(), parsed) - } - database = parsed[0] - eventTriggerName = parsed[1] - } - - return database, eventTriggerName, nil -} - -func createEventTrigger(db *DBConnection, d *schema.ResourceData) error { - eventTriggerName := d.Get(eventTriggerNameAttr).(string) b := bytes.NewBufferString("CREATE EVENT TRIGGER ") fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) @@ -252,7 +174,7 @@ func createEventTrigger(db *DBConnection, d *schema.ResourceData) error { b = bytes.NewBufferString("ALTER EVENT TRIGGER ") fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) - eventTriggerEnabled := d.Get(eventTriggerEnabledAttr).(string) + eventTriggerEnabled := d.Get(eventTriggerStatusAttr).(string) fmt.Fprint(b, " ", eventTriggerEnabled) statusSql := b.String() @@ -290,14 +212,15 @@ func createEventTrigger(db *DBConnection, d *schema.ResourceData) error { return nil } -func updateEventTrigger(db *DBConnection, d *schema.ResourceData) error { +func resourcePostgreSQLEventTriggerUpdate(db *DBConnection, d *schema.ResourceData) error { eventTriggerName := d.Get(eventTriggerNameAttr).(string) + d.SetId(eventTriggerName) // Enable or disable the event trigger b := bytes.NewBufferString("ALTER EVENT TRIGGER ") fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) - eventTriggerEnabled := d.Get(eventTriggerEnabledAttr).(string) + eventTriggerEnabled := d.Get(eventTriggerStatusAttr).(string) fmt.Fprint(b, " ", eventTriggerEnabled) statusSql := b.String() @@ -330,8 +253,10 @@ func updateEventTrigger(db *DBConnection, d *schema.ResourceData) error { return nil } -func deleteEventTrigger(db *DBConnection, d *schema.ResourceData) error { +func resourcePostgreSQLEventTriggerDelete(db *DBConnection, d *schema.ResourceData) error { eventTriggerName := d.Get(eventTriggerNameAttr).(string) + d.SetId(eventTriggerName) + b := bytes.NewBufferString("DROP EVENT TRIGGER ") fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) @@ -354,7 +279,7 @@ func deleteEventTrigger(db *DBConnection, d *schema.ResourceData) error { return nil } -func readEventTrigger(db *DBConnection, d *schema.ResourceData) error { +func resourcePostgreSQLEventTriggerRead(db *DBConnection, d *schema.ResourceData) error { database, eventTriggerName, err := getDBEventTriggerName(d, db.client.databaseName) if err != nil { return err @@ -367,8 +292,7 @@ func readEventTrigger(db *DBConnection, d *schema.ResourceData) error { `JOIN pg_catalog.pg_namespace on pg_catalog.pg_proc.pronamespace = pg_catalog.pg_namespace.oid ` + `WHERE evtname=$1` - var name, on, owner, function, schema string - var enabled string + var name, on, owner, function, schema, status string var tags []string values := []interface{}{ @@ -376,7 +300,7 @@ func readEventTrigger(db *DBConnection, d *schema.ResourceData) error { &on, &function, &schema, - &enabled, + &status, (*pq.StringArray)(&tags), &owner, } @@ -401,34 +325,29 @@ func readEventTrigger(db *DBConnection, d *schema.ResourceData) error { } d.SetId(name) - d.Set("name", name) - d.Set("on", on) - d.Set("function", function) - d.Set("owner", owner) - d.Set("database", database) - d.Set("schema", schema) - - switch enabled { + d.Set(eventTriggerNameAttr, name) + d.Set(eventTriggerOnAttr, on) + d.Set(eventTriggerFunctionAttr, function) + d.Set(eventTriggerOwnerAttr, owner) + d.Set(eventTriggerDatabaseAttr, database) + d.Set(eventTriggerSchemaAttr, schema) + + switch status { case "D": - d.Set("enabled", "disable") + d.Set(eventTriggerStatusAttr, "disable") case "O": - d.Set("enabled", "enable") + d.Set(eventTriggerStatusAttr, "enable") case "R": - d.Set("enabled", "enable_replica") + d.Set(eventTriggerStatusAttr, "enable_replica") case "A": - d.Set("enabled", "enable_always") + d.Set(eventTriggerStatusAttr, "enable_always") } - // TODO: maybe it's better to add a struct - // with types instead of an interface{}? var filters []interface{} if len(tags) > 0 { var values []string - - for _, tag := range tags { - values = append(values, tag) - } + values = append(values, tags...) filter := map[string]interface{}{ "variable": "TAG", @@ -438,7 +357,53 @@ func readEventTrigger(db *DBConnection, d *schema.ResourceData) error { filters = append(filters, filter) } - d.Set("filter", filters) + d.Set(eventTriggerFilterAttr, filters) return nil } + +func resourcePostgreSQLEventTriggerExists(db *DBConnection, d *schema.ResourceData) (bool, error) { + database, eventTriggerName, err := getDBEventTriggerName(d, db.client.databaseName) + if err != nil { + return false, err + } + + // Check if the database exists + exists, err := dbExists(db, database) + if err != nil || !exists { + return false, err + } + + txn, err := startTransaction(db.client, database) + if err != nil { + return false, err + } + defer deferredRollback(txn) + + err = txn.QueryRow("SELECT evtname FROM pg_event_trigger WHERE evtname=$1", eventTriggerName).Scan(&eventTriggerName) + switch { + case err == sql.ErrNoRows: + return false, nil + case err != nil: + return false, fmt.Errorf("error reading schema: %w", err) + } + + return true, nil +} + +func getDBEventTriggerName(d *schema.ResourceData, databaseName string) (string, string, error) { + database := getDatabase(d, databaseName) + eventTriggerName := d.Get(eventTriggerNameAttr).(string) + + // When importing, we have to parse the ID to find event trigger and database names. + if eventTriggerName == "" { + parsed := strings.Split(d.Id(), ".") + if len(parsed) != 2 { + return "", "", fmt.Errorf("schema ID %s has not the expected format 'database.event_trigger': %v", d.Id(), parsed) + } + database = parsed[0] + eventTriggerName = parsed[1] + } + + return database, eventTriggerName, nil +} diff --git a/postgresql/resource_postgresql_event_trigger_test.go b/postgresql/resource_postgresql_event_trigger_test.go index 1f926112..a74ebe3e 100644 --- a/postgresql/resource_postgresql_event_trigger_test.go +++ b/postgresql/resource_postgresql_event_trigger_test.go @@ -139,7 +139,7 @@ resource "postgresql_event_trigger" "event_trigger" { function = postgresql_function.function.name on = "ddl_command_end" owner = "postgres" - enabled = "enable" + status = "enable" filter { variable = "TAG" diff --git a/website/docs/r/postgresql_event_trigger.html.markdown b/website/docs/r/postgresql_event_trigger.html.markdown new file mode 100644 index 00000000..0b7c5970 --- /dev/null +++ b/website/docs/r/postgresql_event_trigger.html.markdown @@ -0,0 +1,74 @@ +--- +layout: "postgresql" +page_title: "PostgreSQL: postgresql_event_trigger" +sidebar_current: "docs-postgresql-resource-postgresql_event_trigger" +description: |- + Creates and manages an event trigger on a PostgreSQL server. +--- + +# postgresql\_event_trigger + +The ``postgresql_event_trigger`` resource creates and manages [event trigger +objects](https://www.postgresql.org/docs/current/static/event-triggers.html) +within a PostgreSQL server instance. + +## Usage + +```hcl +resource "postgresql_function" "function" { + name = "test_function" + + returns = "event_trigger" + language = "plpgsql" + body = <<-EOF + BEGIN + RAISE EXCEPTION 'command % is disabled', tg_tag; + END; + EOF +} + +resource "postgresql_event_trigger" "event_trigger" { + name = "event_trigger_test" + function = postgresql_function.function.name + on = "ddl_command_start" + owner = "postgres" + + filter { + variable = "TAG" + values = [ + "DROP TABLE" + ] + } +} +``` + +## Argument Reference + +* `name` - (Required) The name of the event trigger. + +* `on` - (Required) The name of the on event the trigger will listen to. The allowed names are "ddl_command_start", "ddl_command_end", "sql_drop" or "table_rewrite". + +* `function` - (Required) A function that is declared as taking no argument and returning type event_trigger. + +* `filter` - (Optional) Lists of filter variables to restrict the firing of the trigger. Currently the only supported filter_variable is TAG. + * `variable` - (Required) The name of a variable used to filter events. Currently the only supported value is TAG. + * `values` - (Required) The name of the filter variable name. For TAG, this means a list of command tags (e.g., 'DROP FUNCTION'). + +* `database` - (Optional) The database where the event trigger is located. + If not specified, the function is created in the current database. + +* `schema` - (Optional) Schema where the function is located. + If not specified, the function is created in the current schema. + +* `status` - (Optional) These configure the firing of event triggers. The allowed names are "disable", "enable", "enable_replica" or "enable_always". Default is "enable". + +* `owner` - (Required) The user name of the owner of the event trigger. You can't use 'current_role', 'current_user' or 'session_user' in order to avoid drifts. + +## Import Example + +It is possible to import a `postgresql_event_trigger` resource with the following +command: + +``` +$ terraform import postgresql_event_trigger.event_trigger_test "database.event_trigger" +``` From 9dc8dff5ddd2e8b0b5804c58b26b543b241376f0 Mon Sep 17 00:00:00 2001 From: Fabiano Honorato Date: Mon, 25 Nov 2024 10:51:33 -0300 Subject: [PATCH 4/9] Use function_schema instead of schema and fix tests --- postgresql/resource_postgresql_event_trigger.go | 13 ++++++------- .../resource_postgresql_event_trigger_test.go | 5 ++++- .../r/postgresql_event_trigger.html.markdown | 16 ++++++++-------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/postgresql/resource_postgresql_event_trigger.go b/postgresql/resource_postgresql_event_trigger.go index 14ae259c..08c93f09 100644 --- a/postgresql/resource_postgresql_event_trigger.go +++ b/postgresql/resource_postgresql_event_trigger.go @@ -15,11 +15,11 @@ const ( eventTriggerNameAttr = "name" eventTriggerOnAttr = "on" eventTriggerFunctionAttr = "function" + eventTriggerFunctionSchemaAttr = "function_schema" eventTriggerFilterAttr = "filter" eventTriggerFilterVariableAttr = "variable" eventTriggerFilterValueAttr = "values" eventTriggerDatabaseAttr = "database" - eventTriggerSchemaAttr = "schema" eventTriggerOwnerAttr = "owner" eventTriggerStatusAttr = "status" ) @@ -95,12 +95,11 @@ func resourcePostgreSQLEventTrigger() *schema.Resource { ForceNew: true, Description: "The database where the event trigger is located. If not specified, the provider default database is used.", }, - eventTriggerSchemaAttr: { + eventTriggerFunctionSchemaAttr: { Type: schema.TypeString, - Optional: true, - Computed: true, + Required: true, ForceNew: true, - Description: "Schema where the function is located. If not specified, the provider default schema is used.", + Description: "Schema where the function is located.", }, eventTriggerStatusAttr: { Type: schema.TypeString, @@ -165,7 +164,7 @@ func resourcePostgreSQLEventTriggerCreate(db *DBConnection, d *schema.ResourceDa } eventTriggerFunction := d.Get(eventTriggerFunctionAttr).(string) - eventTriggerSchema := d.Get(eventTriggerSchemaAttr).(string) + eventTriggerSchema := d.Get(eventTriggerFunctionSchemaAttr).(string) fmt.Fprint(b, " EXECUTE FUNCTION ", pq.QuoteIdentifier(eventTriggerSchema), ".", eventTriggerFunction, "()") createSql := b.String() @@ -330,7 +329,7 @@ func resourcePostgreSQLEventTriggerRead(db *DBConnection, d *schema.ResourceData d.Set(eventTriggerFunctionAttr, function) d.Set(eventTriggerOwnerAttr, owner) d.Set(eventTriggerDatabaseAttr, database) - d.Set(eventTriggerSchemaAttr, schema) + d.Set(eventTriggerFunctionSchemaAttr, schema) switch status { case "D": diff --git a/postgresql/resource_postgresql_event_trigger_test.go b/postgresql/resource_postgresql_event_trigger_test.go index a74ebe3e..30e3d94a 100644 --- a/postgresql/resource_postgresql_event_trigger_test.go +++ b/postgresql/resource_postgresql_event_trigger_test.go @@ -10,6 +10,9 @@ import ( ) func TestAccPostgresqlEventTrigger_Basic(t *testing.T) { + skipIfNotAcc(t) + testSuperuserPreCheck(t) + // Create the database outside of resource.Test // because we need to create test schemas. dbSuffix, teardown := setupTestDatabase(t, true, true) @@ -135,8 +138,8 @@ resource "postgresql_function" "function" { resource "postgresql_event_trigger" "event_trigger" { name = "event_trigger_test" database = "%[1]s" - schema = "%[2]s" function = postgresql_function.function.name + function_schema = postgresql_function.function.schema on = "ddl_command_end" owner = "postgres" status = "enable" diff --git a/website/docs/r/postgresql_event_trigger.html.markdown b/website/docs/r/postgresql_event_trigger.html.markdown index 0b7c5970..f736be62 100644 --- a/website/docs/r/postgresql_event_trigger.html.markdown +++ b/website/docs/r/postgresql_event_trigger.html.markdown @@ -28,10 +28,11 @@ resource "postgresql_function" "function" { } resource "postgresql_event_trigger" "event_trigger" { - name = "event_trigger_test" - function = postgresql_function.function.name - on = "ddl_command_start" - owner = "postgres" + name = "event_trigger_test" + function = postgresql_function.function.name + function_schema = postgresql_function.function.schema + on = "ddl_command_start" + owner = "postgres" filter { variable = "TAG" @@ -50,6 +51,8 @@ resource "postgresql_event_trigger" "event_trigger" { * `function` - (Required) A function that is declared as taking no argument and returning type event_trigger. +* `function_schema` - (Required) Schema where the function is located. + * `filter` - (Optional) Lists of filter variables to restrict the firing of the trigger. Currently the only supported filter_variable is TAG. * `variable` - (Required) The name of a variable used to filter events. Currently the only supported value is TAG. * `values` - (Required) The name of the filter variable name. For TAG, this means a list of command tags (e.g., 'DROP FUNCTION'). @@ -57,9 +60,6 @@ resource "postgresql_event_trigger" "event_trigger" { * `database` - (Optional) The database where the event trigger is located. If not specified, the function is created in the current database. -* `schema` - (Optional) Schema where the function is located. - If not specified, the function is created in the current schema. - * `status` - (Optional) These configure the firing of event triggers. The allowed names are "disable", "enable", "enable_replica" or "enable_always". Default is "enable". * `owner` - (Required) The user name of the owner of the event trigger. You can't use 'current_role', 'current_user' or 'session_user' in order to avoid drifts. @@ -71,4 +71,4 @@ command: ``` $ terraform import postgresql_event_trigger.event_trigger_test "database.event_trigger" -``` +``` \ No newline at end of file From 43a9021650310ac56eaeb8f6b76266ab91e0a099 Mon Sep 17 00:00:00 2001 From: Fabiano Soares Honorato Date: Mon, 25 Nov 2024 10:57:04 -0300 Subject: [PATCH 5/9] Apply suggestions from code review Co-authored-by: Cyril Gaudin --- .../resource_postgresql_event_trigger.go | 43 ++----------------- 1 file changed, 4 insertions(+), 39 deletions(-) diff --git a/postgresql/resource_postgresql_event_trigger.go b/postgresql/resource_postgresql_event_trigger.go index 08c93f09..e2722e17 100644 --- a/postgresql/resource_postgresql_event_trigger.go +++ b/postgresql/resource_postgresql_event_trigger.go @@ -30,7 +30,6 @@ func resourcePostgreSQLEventTrigger() *schema.Resource { Read: PGResourceFunc(resourcePostgreSQLEventTriggerRead), Update: PGResourceFunc(resourcePostgreSQLEventTriggerUpdate), Delete: PGResourceFunc(resourcePostgreSQLEventTriggerDelete), - Exists: PGResourceExistsFunc(resourcePostgreSQLEventTriggerExists), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -134,8 +133,7 @@ func resourcePostgreSQLEventTriggerCreate(db *DBConnection, d *schema.ResourceDa b := bytes.NewBufferString("CREATE EVENT TRIGGER ") fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) - eventTriggerOn := d.Get(eventTriggerOnAttr).(string) - fmt.Fprint(b, " ON ", eventTriggerOn) + fmt.Fprint(b, " ON ", d.Get(eventTriggerOnAttr).(string)) if filters, ok := d.GetOk(eventTriggerFilterAttr); ok { filters := filters.([]interface{}) @@ -181,7 +179,7 @@ func resourcePostgreSQLEventTriggerCreate(db *DBConnection, d *schema.ResourceDa // Table owner b = bytes.NewBufferString("ALTER EVENT TRIGGER ") eventTriggerOwner := d.Get(eventTriggerOwnerAttr).(string) - fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName), " OWNER TO ", eventTriggerOwner) + fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName), " OWNER TO ", pq.QuoteIdentifier(eventTriggerOwner)) ownerSql := b.String() @@ -256,10 +254,7 @@ func resourcePostgreSQLEventTriggerDelete(db *DBConnection, d *schema.ResourceDa eventTriggerName := d.Get(eventTriggerNameAttr).(string) d.SetId(eventTriggerName) - b := bytes.NewBufferString("DROP EVENT TRIGGER ") - fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) - - sql := b.String() + sql := fmt.Sprintf("DROP EVENT TRIGGER %s", pq.QuoteIdentifier(eventTriggerName)) txn, err := startTransaction(db.client, d.Get(eventTriggerDatabaseAttr).(string)) if err != nil { @@ -284,9 +279,8 @@ func resourcePostgreSQLEventTriggerRead(db *DBConnection, d *schema.ResourceData return err } - query := `SELECT evtname, evtevent, proname, nspname, evtenabled, evttags, usename ` + + query := `SELECT evtname, evtevent, proname, nspname, evtenabled, evttags, pg_get_userbyid(evtowner) ` + `FROM pg_catalog.pg_event_trigger ` + - `JOIN pg_catalog.pg_user on pg_catalog.pg_event_trigger.evtowner = pg_catalog.pg_user.usesysid ` + `JOIN pg_catalog.pg_proc on pg_catalog.pg_event_trigger.evtfoid = pg_catalog.pg_proc.oid ` + `JOIN pg_catalog.pg_namespace on pg_catalog.pg_proc.pronamespace = pg_catalog.pg_namespace.oid ` + `WHERE evtname=$1` @@ -361,35 +355,6 @@ func resourcePostgreSQLEventTriggerRead(db *DBConnection, d *schema.ResourceData return nil } -func resourcePostgreSQLEventTriggerExists(db *DBConnection, d *schema.ResourceData) (bool, error) { - database, eventTriggerName, err := getDBEventTriggerName(d, db.client.databaseName) - if err != nil { - return false, err - } - - // Check if the database exists - exists, err := dbExists(db, database) - if err != nil || !exists { - return false, err - } - - txn, err := startTransaction(db.client, database) - if err != nil { - return false, err - } - defer deferredRollback(txn) - - err = txn.QueryRow("SELECT evtname FROM pg_event_trigger WHERE evtname=$1", eventTriggerName).Scan(&eventTriggerName) - switch { - case err == sql.ErrNoRows: - return false, nil - case err != nil: - return false, fmt.Errorf("error reading schema: %w", err) - } - - return true, nil -} - func getDBEventTriggerName(d *schema.ResourceData, databaseName string) (string, string, error) { database := getDatabase(d, databaseName) eventTriggerName := d.Get(eventTriggerNameAttr).(string) From 80cf48e309a2a97d32e85fa3032b3514faef7fe9 Mon Sep 17 00:00:00 2001 From: Fabiano Honorato Date: Mon, 25 Nov 2024 11:13:27 -0300 Subject: [PATCH 6/9] Fix SetId usage --- postgresql/resource_postgresql_event_trigger.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/postgresql/resource_postgresql_event_trigger.go b/postgresql/resource_postgresql_event_trigger.go index e2722e17..11df1cd1 100644 --- a/postgresql/resource_postgresql_event_trigger.go +++ b/postgresql/resource_postgresql_event_trigger.go @@ -128,7 +128,9 @@ func resourcePostgreSQLEventTrigger() *schema.Resource { func resourcePostgreSQLEventTriggerCreate(db *DBConnection, d *schema.ResourceData) error { eventTriggerName := d.Get(eventTriggerNameAttr).(string) - d.SetId(eventTriggerName) + database := getDatabase(d, db.client.databaseName) + + d.SetId(generateSchemaID(d, database)) b := bytes.NewBufferString("CREATE EVENT TRIGGER ") fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) @@ -211,7 +213,9 @@ func resourcePostgreSQLEventTriggerCreate(db *DBConnection, d *schema.ResourceDa func resourcePostgreSQLEventTriggerUpdate(db *DBConnection, d *schema.ResourceData) error { eventTriggerName := d.Get(eventTriggerNameAttr).(string) - d.SetId(eventTriggerName) + database := getDatabase(d, db.client.databaseName) + + d.SetId(generateSchemaID(d, database)) // Enable or disable the event trigger b := bytes.NewBufferString("ALTER EVENT TRIGGER ") @@ -252,7 +256,9 @@ func resourcePostgreSQLEventTriggerUpdate(db *DBConnection, d *schema.ResourceDa func resourcePostgreSQLEventTriggerDelete(db *DBConnection, d *schema.ResourceData) error { eventTriggerName := d.Get(eventTriggerNameAttr).(string) - d.SetId(eventTriggerName) + database := getDatabase(d, db.client.databaseName) + + d.SetId(generateSchemaID(d, database)) sql := fmt.Sprintf("DROP EVENT TRIGGER %s", pq.QuoteIdentifier(eventTriggerName)) @@ -317,7 +323,7 @@ func resourcePostgreSQLEventTriggerRead(db *DBConnection, d *schema.ResourceData return err } - d.SetId(name) + d.SetId(generateSchemaID(d, database)) d.Set(eventTriggerNameAttr, name) d.Set(eventTriggerOnAttr, on) d.Set(eventTriggerFunctionAttr, function) From c8467b9220d90606d765387f9451e0a9336d44bb Mon Sep 17 00:00:00 2001 From: Fabiano Honorato Date: Mon, 25 Nov 2024 11:15:01 -0300 Subject: [PATCH 7/9] Quote eventTriggerFunction --- postgresql/resource_postgresql_event_trigger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgresql/resource_postgresql_event_trigger.go b/postgresql/resource_postgresql_event_trigger.go index 11df1cd1..d19c02e0 100644 --- a/postgresql/resource_postgresql_event_trigger.go +++ b/postgresql/resource_postgresql_event_trigger.go @@ -165,7 +165,7 @@ func resourcePostgreSQLEventTriggerCreate(db *DBConnection, d *schema.ResourceDa eventTriggerFunction := d.Get(eventTriggerFunctionAttr).(string) eventTriggerSchema := d.Get(eventTriggerFunctionSchemaAttr).(string) - fmt.Fprint(b, " EXECUTE FUNCTION ", pq.QuoteIdentifier(eventTriggerSchema), ".", eventTriggerFunction, "()") + fmt.Fprint(b, " EXECUTE FUNCTION ", pq.QuoteIdentifier(eventTriggerSchema), ".", pq.QuoteIdentifier(eventTriggerFunction), "()") createSql := b.String() From 23ad8c26280a6e89276af3cc4905388fffb08bfd Mon Sep 17 00:00:00 2001 From: Fabiano Honorato Date: Tue, 26 Nov 2024 16:46:35 -0300 Subject: [PATCH 8/9] Add rename and fix event trigger status --- .../resource_postgresql_event_trigger.go | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/postgresql/resource_postgresql_event_trigger.go b/postgresql/resource_postgresql_event_trigger.go index d19c02e0..080a39e7 100644 --- a/postgresql/resource_postgresql_event_trigger.go +++ b/postgresql/resource_postgresql_event_trigger.go @@ -24,6 +24,13 @@ const ( eventTriggerStatusAttr = "status" ) +var eventTriggerStatusMap = map[string]string{ + "disable": "DISABLE", + "enable": "ENABLE", + "enable_replica": "ENABLE REPLICA", + "enable_always": "ENABLE ALWAYS", +} + func resourcePostgreSQLEventTrigger() *schema.Resource { return &schema.Resource{ Create: PGResourceFunc(resourcePostgreSQLEventTriggerCreate), @@ -174,7 +181,7 @@ func resourcePostgreSQLEventTriggerCreate(db *DBConnection, d *schema.ResourceDa fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) eventTriggerEnabled := d.Get(eventTriggerStatusAttr).(string) - fmt.Fprint(b, " ", eventTriggerEnabled) + fmt.Fprint(b, " ", eventTriggerStatusMap[eventTriggerEnabled]) statusSql := b.String() @@ -212,7 +219,16 @@ func resourcePostgreSQLEventTriggerCreate(db *DBConnection, d *schema.ResourceDa } func resourcePostgreSQLEventTriggerUpdate(db *DBConnection, d *schema.ResourceData) error { - eventTriggerName := d.Get(eventTriggerNameAttr).(string) + var eventTriggerName, eventTriggerNameNew string + + if d.HasChange(eventTriggerNameAttr) { + old, new := d.GetChange(eventTriggerNameAttr) + eventTriggerName = old.(string) + eventTriggerNameNew = new.(string) + } else { + eventTriggerName = d.Get(eventTriggerNameAttr).(string) + } + database := getDatabase(d, db.client.databaseName) d.SetId(generateSchemaID(d, database)) @@ -222,7 +238,7 @@ func resourcePostgreSQLEventTriggerUpdate(db *DBConnection, d *schema.ResourceDa fmt.Fprint(b, pq.QuoteIdentifier(eventTriggerName)) eventTriggerEnabled := d.Get(eventTriggerStatusAttr).(string) - fmt.Fprint(b, " ", eventTriggerEnabled) + fmt.Fprint(b, " ", eventTriggerStatusMap[eventTriggerEnabled]) statusSql := b.String() @@ -247,6 +263,14 @@ func resourcePostgreSQLEventTriggerUpdate(db *DBConnection, d *schema.ResourceDa return err } + if eventTriggerNameNew != "" { + if _, err := txn.Exec( + fmt.Sprintf("ALTER EVENT TRIGGER %s RENAME TO %s", pq.QuoteIdentifier(eventTriggerName), pq.QuoteIdentifier(eventTriggerNameNew)), + ); err != nil { + return err + } + } + if err := txn.Commit(); err != nil { return err } From bde91d093cc59949fb5a324337d00770dbc5760e Mon Sep 17 00:00:00 2001 From: Fabiano Honorato Date: Tue, 26 Nov 2024 16:46:41 -0300 Subject: [PATCH 9/9] Fix tests --- .../resource_postgresql_event_trigger_test.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/postgresql/resource_postgresql_event_trigger_test.go b/postgresql/resource_postgresql_event_trigger_test.go index 30e3d94a..3bb427f6 100644 --- a/postgresql/resource_postgresql_event_trigger_test.go +++ b/postgresql/resource_postgresql_event_trigger_test.go @@ -23,15 +23,19 @@ func TestAccPostgresqlEventTrigger_Basic(t *testing.T) { dbName, _ := getTestDBNames(dbSuffix) - testAccPostgresqlDataSourceTablesEventTriggerConfig := fmt.Sprintf(testAccPostgreSQLEventTriggerConfig, dbName, schemas[0]) - resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckPostgresqlEventTriggerDestroy, Steps: []resource.TestStep{ { - Config: testAccPostgresqlDataSourceTablesEventTriggerConfig, + Config: fmt.Sprintf(testAccPostgreSQLEventTriggerConfig, dbName, schemas[0], "test_event"), + Check: resource.ComposeTestCheckFunc( + testAccCheckPostgresqlEventTriggerExists("postgresql_event_trigger.event_trigger", dbName), + ), + }, + { + Config: fmt.Sprintf(testAccPostgreSQLEventTriggerConfig, dbName, schemas[0], "test_event_renamed"), Check: resource.ComposeTestCheckFunc( testAccCheckPostgresqlEventTriggerExists("postgresql_event_trigger.event_trigger", dbName), ), @@ -58,7 +62,7 @@ func testAccCheckPostgresqlEventTriggerExists(n string, database string) resourc } defer deferredRollback(txn) - exists, err := checkEventTriggerExists(txn, rs.Primary.ID) + exists, err := checkEventTriggerExists(txn, rs.Primary.Attributes["name"]) if err != nil { return fmt.Errorf("Error checking event trigger %s", err) @@ -93,7 +97,7 @@ func testAccCheckPostgresqlEventTriggerDestroy(s *terraform.State) error { } defer deferredRollback(txn) - exists, err := checkEventTriggerExists(txn, rs.Primary.ID) + exists, err := checkEventTriggerExists(txn, rs.Primary.Attributes["name"]) if err != nil { return fmt.Errorf("Error checking event trigger %s", err) @@ -136,7 +140,7 @@ resource "postgresql_function" "function" { } resource "postgresql_event_trigger" "event_trigger" { - name = "event_trigger_test" + name = "%[3]s" database = "%[1]s" function = postgresql_function.function.name function_schema = postgresql_function.function.schema