From a660d1c3a0b9fed8be99501aa9a8dc38bee11178 Mon Sep 17 00:00:00 2001 From: CharlesDelannoy Date: Tue, 21 Oct 2025 18:39:49 +0200 Subject: [PATCH 1/9] feat: Allow to precise a database to connect to in a script --- postgresql/resource_postgresql_script.go | 45 ++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/postgresql/resource_postgresql_script.go b/postgresql/resource_postgresql_script.go index af3348ee..6ab2f108 100644 --- a/postgresql/resource_postgresql_script.go +++ b/postgresql/resource_postgresql_script.go @@ -14,6 +14,7 @@ import ( const ( scriptCommandsAttr = "commands" + scriptDatabaseAttr = "database" scriptTriesAttr = "tries" scriptBackoffDelayAttr = "backoff_delay" scriptTimeoutAttr = "timeout" @@ -28,6 +29,12 @@ func resourcePostgreSQLScript() *schema.Resource { Delete: PGResourceFunc(resourcePostgreSQLScriptDelete), Schema: map[string]*schema.Schema{ + scriptDatabaseAttr: { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "The database to execute commands in (defaults to provider's configured database)", + }, scriptCommandsAttr: { Type: schema.TypeList, Required: true, @@ -77,9 +84,24 @@ func resourcePostgreSQLScriptCreateOrUpdate(ctx context.Context, db *DBConnectio }} } - sum := shasumCommands(commands) + // Get the target database connection + database := getDatabase(d, db.client.databaseName) - if err := executeCommands(ctx, db, commands, tries, backoffDelay, timeout); err != nil { + targetDB := db + if database != "" && database != db.client.databaseName { + client := db.client.config.NewClient(database) + newDB, err := client.Connect() + if err != nil { + return diag.Diagnostics{diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to connect to database", + Detail: err.Error(), + }} + } + targetDB = newDB + } + + if err := executeCommands(ctx, targetDB, commands, tries, backoffDelay, timeout); err != nil { return diag.Diagnostics{diag.Diagnostic{ Severity: diag.Error, Summary: "Commands execution failed", @@ -87,18 +109,35 @@ func resourcePostgreSQLScriptCreateOrUpdate(ctx context.Context, db *DBConnectio }} } - d.Set(scriptShasumAttr, sum) + sum := shasumCommands(commands) d.SetId(sum) + + if err := resourcePostgreSQLScriptReadImpl(db, d); err != nil { + return diag.Diagnostics{diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to read script state", + Detail: err.Error(), + }} + } + return nil } func resourcePostgreSQLScriptRead(db *DBConnection, d *schema.ResourceData) error { + return resourcePostgreSQLScriptReadImpl(db, d) +} + +func resourcePostgreSQLScriptReadImpl(db *DBConnection, d *schema.ResourceData) error { commands, err := toStringArray(d.Get(scriptCommandsAttr).([]any)) if err != nil { return err } newSum := shasumCommands(commands) + + database := getDatabase(d, db.client.databaseName) + d.Set(scriptShasumAttr, newSum) + d.Set(scriptDatabaseAttr, database) return nil } From c9baa78c37d56a46fb72c955558247c04c2f1734 Mon Sep 17 00:00:00 2001 From: CharlesDelannoy Date: Wed, 22 Oct 2025 10:51:30 +0200 Subject: [PATCH 2/9] Always create the new connection and close it after the commands --- postgresql/resource_postgresql_script.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/postgresql/resource_postgresql_script.go b/postgresql/resource_postgresql_script.go index 6ab2f108..ce396924 100644 --- a/postgresql/resource_postgresql_script.go +++ b/postgresql/resource_postgresql_script.go @@ -87,19 +87,16 @@ func resourcePostgreSQLScriptCreateOrUpdate(ctx context.Context, db *DBConnectio // Get the target database connection database := getDatabase(d, db.client.databaseName) - targetDB := db - if database != "" && database != db.client.databaseName { - client := db.client.config.NewClient(database) - newDB, err := client.Connect() - if err != nil { - return diag.Diagnostics{diag.Diagnostic{ - Severity: diag.Error, - Summary: "Failed to connect to database", - Detail: err.Error(), - }} - } - targetDB = newDB + client := db.client.config.NewClient(database) + newDB, err := client.Connect() + if err != nil { + return diag.Diagnostics{diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to connect to database", + Detail: err.Error(), + }} } + targetDB := newDB if err := executeCommands(ctx, targetDB, commands, tries, backoffDelay, timeout); err != nil { return diag.Diagnostics{diag.Diagnostic{ @@ -109,6 +106,8 @@ func resourcePostgreSQLScriptCreateOrUpdate(ctx context.Context, db *DBConnectio }} } + defer targetDB.Close() + sum := shasumCommands(commands) d.SetId(sum) From 0c82d615952c1439c3d37a2858427853a8ce7486 Mon Sep 17 00:00:00 2001 From: CharlesDelannoy Date: Wed, 22 Oct 2025 11:27:27 +0200 Subject: [PATCH 3/9] use specific function to retrieve the database arg --- postgresql/resource_postgresql_script.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/postgresql/resource_postgresql_script.go b/postgresql/resource_postgresql_script.go index ce396924..c44ad023 100644 --- a/postgresql/resource_postgresql_script.go +++ b/postgresql/resource_postgresql_script.go @@ -85,7 +85,7 @@ func resourcePostgreSQLScriptCreateOrUpdate(ctx context.Context, db *DBConnectio } // Get the target database connection - database := getDatabase(d, db.client.databaseName) + database := getDatabaseForScript(d, db.client.databaseName) client := db.client.config.NewClient(database) newDB, err := client.Connect() @@ -122,6 +122,14 @@ func resourcePostgreSQLScriptCreateOrUpdate(ctx context.Context, db *DBConnectio return nil } +func getDatabaseForScript(d *schema.ResourceData, databaseName string) string { + if v, ok := d.GetOk(scriptDatabaseAttr); ok { + databaseName = v.(string) + } + + return databaseName +} + func resourcePostgreSQLScriptRead(db *DBConnection, d *schema.ResourceData) error { return resourcePostgreSQLScriptReadImpl(db, d) } @@ -133,7 +141,7 @@ func resourcePostgreSQLScriptReadImpl(db *DBConnection, d *schema.ResourceData) } newSum := shasumCommands(commands) - database := getDatabase(d, db.client.databaseName) + database := getDatabaseForScript(d, db.client.databaseName) d.Set(scriptShasumAttr, newSum) d.Set(scriptDatabaseAttr, database) From 5eba463e6086fce1c4c4ce5f728e6380444a4fdf Mon Sep 17 00:00:00 2001 From: CharlesDelannoy Date: Wed, 22 Oct 2025 12:10:17 +0200 Subject: [PATCH 4/9] Add default database value --- postgresql/resource_postgresql_script.go | 1 + 1 file changed, 1 insertion(+) diff --git a/postgresql/resource_postgresql_script.go b/postgresql/resource_postgresql_script.go index c44ad023..367b7de5 100644 --- a/postgresql/resource_postgresql_script.go +++ b/postgresql/resource_postgresql_script.go @@ -33,6 +33,7 @@ func resourcePostgreSQLScript() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, + Default: "postgres", Description: "The database to execute commands in (defaults to provider's configured database)", }, scriptCommandsAttr: { From 1307c2b861ad345673336137143e92dffe1d3c49 Mon Sep 17 00:00:00 2001 From: CharlesDelannoy Date: Wed, 22 Oct 2025 12:22:26 +0200 Subject: [PATCH 5/9] ForceNew --- postgresql/resource_postgresql_script.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgresql/resource_postgresql_script.go b/postgresql/resource_postgresql_script.go index 367b7de5..f64298b8 100644 --- a/postgresql/resource_postgresql_script.go +++ b/postgresql/resource_postgresql_script.go @@ -33,7 +33,7 @@ func resourcePostgreSQLScript() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - Default: "postgres", + ForceNew: true, Description: "The database to execute commands in (defaults to provider's configured database)", }, scriptCommandsAttr: { From cfbe786a562f6e3cdb84c5d716b034c1ce4037ac Mon Sep 17 00:00:00 2001 From: CharlesDelannoy Date: Wed, 22 Oct 2025 12:27:17 +0200 Subject: [PATCH 6/9] rm defer to test --- postgresql/resource_postgresql_script.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/postgresql/resource_postgresql_script.go b/postgresql/resource_postgresql_script.go index f64298b8..3d3faa0a 100644 --- a/postgresql/resource_postgresql_script.go +++ b/postgresql/resource_postgresql_script.go @@ -107,8 +107,6 @@ func resourcePostgreSQLScriptCreateOrUpdate(ctx context.Context, db *DBConnectio }} } - defer targetDB.Close() - sum := shasumCommands(commands) d.SetId(sum) From b00f211ae54003a49b5d387abe905ff87bc7af32 Mon Sep 17 00:00:00 2001 From: CharlesDelannoy Date: Wed, 22 Oct 2025 13:26:22 +0200 Subject: [PATCH 7/9] add a test --- postgresql/resource_postgresql_script_test.go | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/postgresql/resource_postgresql_script_test.go b/postgresql/resource_postgresql_script_test.go index 8e624237..73ed47be 100644 --- a/postgresql/resource_postgresql_script_test.go +++ b/postgresql/resource_postgresql_script_test.go @@ -1,10 +1,12 @@ package postgresql import ( + "fmt" "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccPostgresqlScript_basic(t *testing.T) { @@ -227,3 +229,85 @@ func TestAccPostgresqlScript_timeout(t *testing.T) { }, }) } + +func TestAccPostgresqlScript_withDatabase(t *testing.T) { + config := ` + resource "postgresql_database" "test_db" { + name = "test_script_db" + } + + resource "postgresql_script" "test" { + database = postgresql_database.test_db.name + commands = [ + "CREATE TABLE test_table (id INT);", + "INSERT INTO test_table VALUES (1);" + ] + depends_on = [postgresql_database.test_db] + } + ` + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("postgresql_script.test", "database", "test_script_db"), + resource.TestCheckResourceAttr("postgresql_script.test", "commands.0", "CREATE TABLE test_table (id INT);"), + resource.TestCheckResourceAttr("postgresql_script.test", "commands.1", "INSERT INTO test_table VALUES (1);"), + testAccCheckTableExistsInDatabase("test_script_db", "test_table"), + testAccCheckTableHasRecords("test_script_db", "test_table", 1), + ), + }, + }, + }) +} + +func testAccCheckTableExistsInDatabase(dbName, tableName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*Client) + dbClient := client.config.NewClient(dbName) + db, err := dbClient.Connect() + if err != nil { + return fmt.Errorf("Error connecting to database %s: %s", dbName, err) + } + + var exists bool + query := "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = $1)" + err = db.QueryRow(query, tableName).Scan(&exists) + if err != nil { + return fmt.Errorf("Error checking if table %s exists: %s", tableName, err) + } + + if !exists { + return fmt.Errorf("Table %s does not exist in database %s", tableName, dbName) + } + + return nil + } +} + +func testAccCheckTableHasRecords(dbName, tableName string, expectedCount int) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*Client) + dbClient := client.config.NewClient(dbName) + db, err := dbClient.Connect() + if err != nil { + return fmt.Errorf("Error connecting to database %s: %s", dbName, err) + } + + var count int + query := fmt.Sprintf("SELECT COUNT(*) FROM %s", tableName) + err = db.QueryRow(query).Scan(&count) + if err != nil { + return fmt.Errorf("Error counting records in table %s: %s", tableName, err) + } + + if count != expectedCount { + return fmt.Errorf("Expected %d records but got %d in table %s", expectedCount, count, tableName) + } + + return nil + } +} From 74d7c235d86a5bdde8a54bb9807118126a5cff89 Mon Sep 17 00:00:00 2001 From: CharlesDelannoy Date: Wed, 22 Oct 2025 14:13:57 +0200 Subject: [PATCH 8/9] update test to ensure it can handle with or without explicit database. + script to cleanup tables --- postgresql/resource_postgresql_script_test.go | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/postgresql/resource_postgresql_script_test.go b/postgresql/resource_postgresql_script_test.go index 73ed47be..4edf1753 100644 --- a/postgresql/resource_postgresql_script_test.go +++ b/postgresql/resource_postgresql_script_test.go @@ -244,11 +244,21 @@ func TestAccPostgresqlScript_withDatabase(t *testing.T) { ] depends_on = [postgresql_database.test_db] } + + resource "postgresql_script" "test_default" { + commands = [ + "CREATE TABLE default_db_table (id INT);", + "INSERT INTO default_db_table VALUES (1);", + "INSERT INTO default_db_table VALUES (2);" + ] + depends_on = [postgresql_database.test_db] + } ` resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckScriptTablesDestroyed, Steps: []resource.TestStep{ { Config: config, @@ -256,14 +266,45 @@ func TestAccPostgresqlScript_withDatabase(t *testing.T) { resource.TestCheckResourceAttr("postgresql_script.test", "database", "test_script_db"), resource.TestCheckResourceAttr("postgresql_script.test", "commands.0", "CREATE TABLE test_table (id INT);"), resource.TestCheckResourceAttr("postgresql_script.test", "commands.1", "INSERT INTO test_table VALUES (1);"), + resource.TestCheckResourceAttr("postgresql_script.test_default", "database", "postgres"), + resource.TestCheckResourceAttr("postgresql_script.test_default", "commands.0", "CREATE TABLE default_db_table (id INT);"), + resource.TestCheckResourceAttr("postgresql_script.test_default", "commands.1", "INSERT INTO default_db_table VALUES (1);"), + resource.TestCheckResourceAttr("postgresql_script.test_default", "commands.2", "INSERT INTO default_db_table VALUES (2);"), testAccCheckTableExistsInDatabase("test_script_db", "test_table"), testAccCheckTableHasRecords("test_script_db", "test_table", 1), + testAccCheckTableExistsInDatabase("postgres", "default_db_table"), + testAccCheckTableHasRecords("postgres", "default_db_table", 2), ), }, }, }) } +func testAccCheckScriptTablesDestroyed(s *terraform.State) error { + return testAccDropTables(map[string][]string{ + "test_script_db": {"test_table"}, + "postgres": {"default_db_table"}, + }) +} + +func testAccDropTables(tablesToDrop map[string][]string) error { + client := testAccProvider.Meta().(*Client) + + for dbName, tables := range tablesToDrop { + dbClient := client.config.NewClient(dbName) + db, err := dbClient.Connect() + if err != nil { + continue // Skip if we can't connect to the database + } + + for _, tableName := range tables { + _, _ = db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", tableName)) + } + } + + return nil +} + func testAccCheckTableExistsInDatabase(dbName, tableName string) resource.TestCheckFunc { return func(s *terraform.State) error { client := testAccProvider.Meta().(*Client) From caf578cb98241fdc8f74fb5e6735c746b0ec649a Mon Sep 17 00:00:00 2001 From: CharlesDelannoy Date: Wed, 22 Oct 2025 16:55:28 +0200 Subject: [PATCH 9/9] rm unused variable and rename explicitely metho --- postgresql/resource_postgresql_script.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/postgresql/resource_postgresql_script.go b/postgresql/resource_postgresql_script.go index 3d3faa0a..1d10cfca 100644 --- a/postgresql/resource_postgresql_script.go +++ b/postgresql/resource_postgresql_script.go @@ -86,7 +86,7 @@ func resourcePostgreSQLScriptCreateOrUpdate(ctx context.Context, db *DBConnectio } // Get the target database connection - database := getDatabaseForScript(d, db.client.databaseName) + database := getDatabaseAttrOrDefault(d, db.client.databaseName) client := db.client.config.NewClient(database) newDB, err := client.Connect() @@ -97,9 +97,8 @@ func resourcePostgreSQLScriptCreateOrUpdate(ctx context.Context, db *DBConnectio Detail: err.Error(), }} } - targetDB := newDB - if err := executeCommands(ctx, targetDB, commands, tries, backoffDelay, timeout); err != nil { + if err := executeCommands(ctx, newDB, commands, tries, backoffDelay, timeout); err != nil { return diag.Diagnostics{diag.Diagnostic{ Severity: diag.Error, Summary: "Commands execution failed", @@ -121,7 +120,7 @@ func resourcePostgreSQLScriptCreateOrUpdate(ctx context.Context, db *DBConnectio return nil } -func getDatabaseForScript(d *schema.ResourceData, databaseName string) string { +func getDatabaseAttrOrDefault(d *schema.ResourceData, databaseName string) string { if v, ok := d.GetOk(scriptDatabaseAttr); ok { databaseName = v.(string) } @@ -140,7 +139,7 @@ func resourcePostgreSQLScriptReadImpl(db *DBConnection, d *schema.ResourceData) } newSum := shasumCommands(commands) - database := getDatabaseForScript(d, db.client.databaseName) + database := getDatabaseAttrOrDefault(d, db.client.databaseName) d.Set(scriptShasumAttr, newSum) d.Set(scriptDatabaseAttr, database)