package tests import ( "context" "fmt" "strconv" "testing" "time" "floci-sdk-test-go/internal/testutil" awsV2 "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/rds" "github.com/aws/aws-sdk-go-v2/service/rds/types" "github.com/aws/aws-sdk-go-v2/service/rdsdata" rdsdatatypes "github.com/aws/aws-sdk-go-v2/service/rdsdata/types" "github.com/aws/aws-sdk-go-v2/service/secretsmanager" awsV1 "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/rdsdataservice" "github.com/aws/smithy-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestRdsDataApiGoSdkV1(t *testing.T) { ctx := context.Background() rdsSvc := testutil.RDSClient() secretsSvc := testutil.SecretsManagerClient() dataSvc := rdsDataV1Client(t) clusterID := "go-rds-data-mysql-" + strconv.FormatInt(time.Now().UnixNano(), 36) database := "app" username := "admin" password := "secret123" secretName := "go-rds-data/" + clusterID var secretArn string var resourceArn string t.Cleanup(func() { if secretArn != "" { _, _ = secretsSvc.DeleteSecret(ctx, &secretsmanager.DeleteSecretInput{ SecretId: awsV2.String(secretArn), ForceDeleteWithoutRecovery: awsV2.Bool(true), }) } if resourceArn != "" { _, _ = rdsSvc.DeleteDBCluster(ctx, &rds.DeleteDBClusterInput{ DBClusterIdentifier: awsV2.String(clusterID), SkipFinalSnapshot: awsV2.Bool(true), }) } }) create, err := rdsSvc.CreateDBCluster(ctx, &rds.CreateDBClusterInput{ DBClusterIdentifier: awsV2.String(clusterID), Engine: awsV2.String("aurora-mysql"), EngineVersion: awsV2.String("8.0.mysql_aurora.3.08.0"), MasterUsername: awsV2.String(username), MasterUserPassword: awsV2.String(password), DatabaseName: awsV2.String(database), EngineMode: awsV2.String("provisioned"), Tags: []types.Tag{{Key: awsV2.String("test"), Value: awsV2.String("rds-data")}}, }) if err != nil { t.Skipf("RDS MySQL cluster unavailable in this environment: %v", err) } require.NotNil(t, create.DBCluster) resourceArn = awsV2.ToString(create.DBCluster.DBClusterArn) require.NotEmpty(t, resourceArn) secret, err := secretsSvc.CreateSecret(ctx, &secretsmanager.CreateSecretInput{ Name: awsV2.String(secretName), SecretString: awsV2.String(fmt.Sprintf(`{"username":%q,"password":%q}`, username, password)), }) require.NoError(t, err) secretArn = awsV2.ToString(secret.ARN) require.NotEmpty(t, secretArn) executeEventually(t, dataSvc, &rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String(`create table if not exists data_api_items ( id varchar(64) primary key, title varchar(255), score bigint, payload blob null, active boolean null, ratio double null, json_text text null, rfc3339_text varchar(64) null, observed_at datetime(6) null, stamped_at timestamp(6) null, due_date date null, due_time time null )`), }) tx, err := dataSvc.BeginTransaction(&rdsdataservice.BeginTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), }) require.NoError(t, err) require.NotEmpty(t, awsV1.StringValue(tx.TransactionId)) insertOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: tx.TransactionId, Sql: awsV1.String(`insert into data_api_items (id, title, score, payload, active, ratio, json_text, rfc3339_text, observed_at, stamped_at, due_date, due_time) values ( 'commit-1', 'first item', 7, UNHEX('010203'), true, 1.5, '{"ok":true}', '2021-03-04T05:06:07Z', '2021-03-04 05:06:07.891000', '2021-03-04 05:06:07.891000', '2021-03-04', '05:06:07' )`), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(insertOut.NumberOfRecordsUpdated)) assert.Nil(t, insertOut.Records, "AWS omits records for a statement that returns no result set") inTxRead, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: tx.TransactionId, Sql: awsV1.String("select count(*) as count from data_api_items where id = 'commit-1'"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(inTxRead.Records[0][0].LongValue)) _, err = dataSvc.CommitTransaction(&rdsdataservice.CommitTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), TransactionId: tx.TransactionId, }) require.NoError(t, err) _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: tx.TransactionId, Sql: awsV1.String("select 1"), }) assertAwsErrorCode(t, err, "TransactionNotFoundException") selectOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select title, score, payload, null as nothing, observed_at, stamped_at, due_date, due_time, active, ratio, json_text, rfc3339_text from data_api_items where id = 'commit-1'"), IncludeResultMetadata: awsV1.Bool(true), }) require.NoError(t, err) require.Len(t, selectOut.ColumnMetadata, 12) assert.Equal(t, "title", awsV1.StringValue(selectOut.ColumnMetadata[0].Name)) assert.Equal(t, "title", awsV1.StringValue(selectOut.ColumnMetadata[0].Label)) assert.NotEmpty(t, awsV1.StringValue(selectOut.ColumnMetadata[0].TypeName)) assert.Equal(t, "nothing", awsV1.StringValue(selectOut.ColumnMetadata[3].Label)) assert.Equal(t, "data_api_items", awsV1.StringValue(selectOut.ColumnMetadata[1].TableName)) require.Len(t, selectOut.Records, 1) require.Len(t, selectOut.Records[0], 12) assert.Equal(t, "first item", awsV1.StringValue(selectOut.Records[0][0].StringValue)) assert.Equal(t, int64(7), awsV1.Int64Value(selectOut.Records[0][1].LongValue)) assert.Equal(t, []byte{1, 2, 3}, selectOut.Records[0][2].BlobValue) assert.True(t, awsV1.BoolValue(selectOut.Records[0][3].IsNull)) assert.Equal(t, "2021-03-04 05:06:07.891", awsV1.StringValue(selectOut.Records[0][4].StringValue)) assert.Equal(t, "2021-03-04 05:06:07.891", awsV1.StringValue(selectOut.Records[0][5].StringValue)) assert.Equal(t, "2021-03-04", awsV1.StringValue(selectOut.Records[0][6].StringValue)) assert.Equal(t, "05:06:07", awsV1.StringValue(selectOut.Records[0][7].StringValue)) assert.True(t, awsV1.BoolValue(selectOut.Records[0][8].BooleanValue)) assert.Equal(t, 1.5, awsV1.Float64Value(selectOut.Records[0][9].DoubleValue)) assert.Equal(t, `{"ok":true}`, awsV1.StringValue(selectOut.Records[0][10].StringValue)) assert.Equal(t, "2021-03-04T05:06:07Z", awsV1.StringValue(selectOut.Records[0][11].StringValue)) updateOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("update data_api_items set score = 8 where id = 'commit-1' and score = 7"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(updateOut.NumberOfRecordsUpdated)) staleUpdateOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("update data_api_items set score = 9 where id = 'commit-1' and score = 7"), }) require.NoError(t, err) assert.Equal(t, int64(0), awsV1.Int64Value(staleUpdateOut.NumberOfRecordsUpdated)) upsertOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score, payload) values ('commit-1', 'upserted', 11, UNHEX('0A0B')) on duplicate key update title = values(title), score = values(score), payload = values(payload)"), }) require.NoError(t, err) assert.GreaterOrEqual(t, awsV1.Int64Value(upsertOut.NumberOfRecordsUpdated), int64(1)) _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values ('page-1', 'alpha', 1), ('page-2', 'beta', 2), ('page-3', 'alphabet', 3)"), }) require.NoError(t, err) predicateOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select id from data_api_items where (title like 'alpha%' or id in (select id from data_api_items where score = 11)) order by score desc limit 2 offset 0"), IncludeResultMetadata: awsV1.Bool(true), }) require.NoError(t, err) require.Len(t, predicateOut.Records, 2) assert.Equal(t, "commit-1", awsV1.StringValue(predicateOut.Records[0][0].StringValue)) assert.Equal(t, "page-3", awsV1.StringValue(predicateOut.Records[1][0].StringValue)) lessThanAndNotInOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select count(*) as count from data_api_items where score < 3 and id not in ('commit-1')"), IncludeResultMetadata: awsV1.Bool(true), }) require.NoError(t, err) assert.Equal(t, "count", awsV1.StringValue(lessThanAndNotInOut.ColumnMetadata[0].Name)) assert.Equal(t, int64(2), awsV1.Int64Value(lessThanAndNotInOut.Records[0][0].LongValue)) emptyOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select id from data_api_items where id = 'missing'"), IncludeResultMetadata: awsV1.Bool(true), }) require.NoError(t, err) assert.Equal(t, "id", awsV1.StringValue(emptyOut.ColumnMetadata[0].Name)) assert.Empty(t, emptyOut.Records) assert.Equal(t, int64(0), awsV1.Int64Value(emptyOut.NumberOfRecordsUpdated)) replaceTx, err := dataSvc.BeginTransaction(&rdsdataservice.BeginTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), }) require.NoError(t, err) deleteOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: replaceTx.TransactionId, Sql: awsV1.String("delete from data_api_items where id = 'page-2'"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(deleteOut.NumberOfRecordsUpdated)) replaceInsertOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: replaceTx.TransactionId, Sql: awsV1.String("insert into data_api_items (id, title, score) values ('page-4', 'replacement', 4)"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(replaceInsertOut.NumberOfRecordsUpdated)) _, err = dataSvc.CommitTransaction(&rdsdataservice.CommitTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), TransactionId: replaceTx.TransactionId, }) require.NoError(t, err) replaceCountOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select count(*) as count from data_api_items where id in ('page-2', 'page-4')"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(replaceCountOut.Records[0][0].LongValue)) rollbackTx, err := dataSvc.BeginTransaction(&rdsdataservice.BeginTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), }) require.NoError(t, err) rollbackInsertOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: rollbackTx.TransactionId, Sql: awsV1.String("insert into data_api_items (id, title, score) values ('rollback-1', 'rolled back', 9)"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(rollbackInsertOut.NumberOfRecordsUpdated)) _, err = dataSvc.RollbackTransaction(&rdsdataservice.RollbackTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), TransactionId: rollbackTx.TransactionId, }) require.NoError(t, err) _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: rollbackTx.TransactionId, Sql: awsV1.String("select 1"), }) assertAwsErrorCode(t, err, "TransactionNotFoundException") countOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select count(*) as count from data_api_items where id in ('commit-1', 'rollback-1')"), IncludeResultMetadata: awsV1.Bool(true), }) require.NoError(t, err) assert.Equal(t, "count", awsV1.StringValue(countOut.ColumnMetadata[0].Name)) assert.Equal(t, int64(1), awsV1.Int64Value(countOut.Records[0][0].LongValue)) batchOut, err := dataSvc.BatchExecuteStatement(&rdsdataservice.BatchExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values (:id, :title, :score)"), ParameterSets: [][]*rdsdataservice.SqlParameter{ { {Name: awsV1.String("id"), Value: &rdsdataservice.Field{StringValue: awsV1.String("batch-1")}}, {Name: awsV1.String("title"), Value: &rdsdataservice.Field{StringValue: awsV1.String("batch one")}}, {Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(1)}}, }, { {Name: awsV1.String("id"), Value: &rdsdataservice.Field{StringValue: awsV1.String("batch-2")}}, {Name: awsV1.String("title"), Value: &rdsdataservice.Field{StringValue: awsV1.String("batch two")}}, {Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(2)}}, }, }, }) require.NoError(t, err) require.Len(t, batchOut.UpdateResults, 2) assert.Empty(t, batchOut.UpdateResults[0].GeneratedFields) batchCountOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select count(*) as count from data_api_items where id in ('batch-1', 'batch-2')"), }) require.NoError(t, err) assert.Equal(t, int64(2), awsV1.Int64Value(batchCountOut.Records[0][0].LongValue)) _, err = dataSvc.BatchExecuteStatement(&rdsdataservice.BatchExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select id from data_api_items where score = :score"), ParameterSets: [][]*rdsdataservice.SqlParameter{ {{Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(1)}}}, {{Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(2)}}}, }, }) assertAwsErrorCode(t, err, "BadRequestException") noSetsOut, err := dataSvc.BatchExecuteStatement(&rdsdataservice.BatchExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values ('batch-no-sets', 'no sets', 99)"), }) require.NoError(t, err) assert.Empty(t, noSetsOut.UpdateResults) noSetsCountOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select count(*) as count from data_api_items where id = 'batch-no-sets'"), }) require.NoError(t, err) assert.Equal(t, int64(0), awsV1.Int64Value(noSetsCountOut.Records[0][0].LongValue)) _, err = dataSvc.BatchExecuteStatement(&rdsdataservice.BatchExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values (:id, :title, :score)"), ParameterSets: [][]*rdsdataservice.SqlParameter{{{Name: awsV1.String("id")}}}, }) assertAwsErrorCode(t, err, "BadRequestException") _, err = dataSvc.ExecuteSql(&rdsdataservice.ExecuteSqlInput{ DbClusterOrInstanceArn: awsV1.String(resourceArn), AwsSecretStoreArn: awsV1.String(secretArn), Database: awsV1.String(database), SqlStatements: awsV1.String("select 1"), }) assertAwsErrorCode(t, err, "BadRequestException") _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values (:id, :title, :score)"), Parameters: []*rdsdataservice.SqlParameter{ {Name: awsV1.String("id"), Value: &rdsdataservice.Field{StringValue: awsV1.String("param-1")}}, {Name: awsV1.String("title"), Value: &rdsdataservice.Field{StringValue: awsV1.String("parameterized")}}, {Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(42)}}, }, }) require.NoError(t, err) paramOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select title, score from data_api_items where id = :id and score = :score"), IncludeResultMetadata: awsV1.Bool(true), Parameters: []*rdsdataservice.SqlParameter{ {Name: awsV1.String("id"), Value: &rdsdataservice.Field{StringValue: awsV1.String("param-1")}}, {Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(42)}}, }, }) require.NoError(t, err) require.Len(t, paramOut.Records, 1) assert.Equal(t, "parameterized", awsV1.StringValue(paramOut.Records[0][0].StringValue)) assert.Equal(t, int64(42), awsV1.Int64Value(paramOut.Records[0][1].LongValue)) _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values ('commit-1', 'duplicate', 1)"), }) assertAwsErrorCode(t, err, "DatabaseErrorException") _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select from data_api_items"), }) assertAwsErrorCode(t, err, "DatabaseErrorException") } // TestRdsDataApiGoSdkPostgresV1 mirrors TestRdsDataApiGoSdkV1 against an // aurora-postgresql cluster to verify RDS Data API parity across engines // (transactions, result field mapping, upsert, and error codes). func TestRdsDataApiGoSdkPostgresV1(t *testing.T) { ctx := context.Background() rdsSvc := testutil.RDSClient() secretsSvc := testutil.SecretsManagerClient() dataSvc := rdsDataV1Client(t) clusterID := "go-rds-data-postgres-" + strconv.FormatInt(time.Now().UnixNano(), 36) database := "app" username := "admin" password := "secret123" secretName := "go-rds-data-postgres/" + clusterID var secretArn string var resourceArn string t.Cleanup(func() { if secretArn != "" { _, _ = secretsSvc.DeleteSecret(ctx, &secretsmanager.DeleteSecretInput{ SecretId: awsV2.String(secretArn), ForceDeleteWithoutRecovery: awsV2.Bool(true), }) } if resourceArn != "" { _, _ = rdsSvc.DeleteDBCluster(ctx, &rds.DeleteDBClusterInput{ DBClusterIdentifier: awsV2.String(clusterID), SkipFinalSnapshot: awsV2.Bool(true), }) } }) create, err := rdsSvc.CreateDBCluster(ctx, &rds.CreateDBClusterInput{ DBClusterIdentifier: awsV2.String(clusterID), Engine: awsV2.String("aurora-postgresql"), MasterUsername: awsV2.String(username), MasterUserPassword: awsV2.String(password), DatabaseName: awsV2.String(database), EngineMode: awsV2.String("provisioned"), Tags: []types.Tag{{Key: awsV2.String("test"), Value: awsV2.String("rds-data-postgres")}}, }) if err != nil { t.Skipf("RDS PostgreSQL cluster unavailable in this environment: %v", err) } require.NotNil(t, create.DBCluster) resourceArn = awsV2.ToString(create.DBCluster.DBClusterArn) require.NotEmpty(t, resourceArn) secret, err := secretsSvc.CreateSecret(ctx, &secretsmanager.CreateSecretInput{ Name: awsV2.String(secretName), SecretString: awsV2.String(fmt.Sprintf(`{"username":%q,"password":%q}`, username, password)), }) require.NoError(t, err) secretArn = awsV2.ToString(secret.ARN) require.NotEmpty(t, secretArn) executeEventually(t, dataSvc, &rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String(`create table if not exists data_api_items ( id varchar(64) primary key, title varchar(255), score bigint, payload bytea null, active boolean null, ratio double precision null, json_text text null, rfc3339_text varchar(64) null, observed_at timestamp(6) null, stamped_at timestamp(6) null, due_date date null, due_time time null )`), }) tx, err := dataSvc.BeginTransaction(&rdsdataservice.BeginTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), }) require.NoError(t, err) require.NotEmpty(t, awsV1.StringValue(tx.TransactionId)) insertOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: tx.TransactionId, Sql: awsV1.String(`insert into data_api_items (id, title, score, payload, active, ratio, json_text, rfc3339_text, observed_at, stamped_at, due_date, due_time) values ( 'commit-1', 'first item', 7, decode('010203', 'hex'), true, 1.5, '{"ok":true}', '2021-03-04T05:06:07Z', '2021-03-04 05:06:07.891000', '2021-03-04 05:06:07.891000', '2021-03-04', '05:06:07' )`), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(insertOut.NumberOfRecordsUpdated)) assert.Nil(t, insertOut.Records, "AWS omits records for a statement that returns no result set") inTxRead, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: tx.TransactionId, Sql: awsV1.String("select count(*) as count from data_api_items where id = 'commit-1'"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(inTxRead.Records[0][0].LongValue)) _, err = dataSvc.CommitTransaction(&rdsdataservice.CommitTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), TransactionId: tx.TransactionId, }) require.NoError(t, err) _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: tx.TransactionId, Sql: awsV1.String("select 1"), }) assertAwsErrorCode(t, err, "TransactionNotFoundException") selectOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select title, score, payload, null as nothing, observed_at, stamped_at, due_date, due_time, active, ratio, json_text, rfc3339_text from data_api_items where id = 'commit-1'"), IncludeResultMetadata: awsV1.Bool(true), }) require.NoError(t, err) require.Len(t, selectOut.ColumnMetadata, 12) assert.Equal(t, "title", awsV1.StringValue(selectOut.ColumnMetadata[0].Name)) assert.Equal(t, "title", awsV1.StringValue(selectOut.ColumnMetadata[0].Label)) assert.NotEmpty(t, awsV1.StringValue(selectOut.ColumnMetadata[0].TypeName)) assert.Equal(t, "nothing", awsV1.StringValue(selectOut.ColumnMetadata[3].Label)) assert.Equal(t, "data_api_items", awsV1.StringValue(selectOut.ColumnMetadata[1].TableName)) require.Len(t, selectOut.Records, 1) require.Len(t, selectOut.Records[0], 12) assert.Equal(t, "first item", awsV1.StringValue(selectOut.Records[0][0].StringValue)) assert.Equal(t, int64(7), awsV1.Int64Value(selectOut.Records[0][1].LongValue)) assert.Equal(t, []byte{1, 2, 3}, selectOut.Records[0][2].BlobValue) assert.True(t, awsV1.BoolValue(selectOut.Records[0][3].IsNull)) assert.Equal(t, "2021-03-04 05:06:07.891", awsV1.StringValue(selectOut.Records[0][4].StringValue)) assert.Equal(t, "2021-03-04 05:06:07.891", awsV1.StringValue(selectOut.Records[0][5].StringValue)) assert.Equal(t, "2021-03-04", awsV1.StringValue(selectOut.Records[0][6].StringValue)) assert.Equal(t, "05:06:07", awsV1.StringValue(selectOut.Records[0][7].StringValue)) assert.True(t, awsV1.BoolValue(selectOut.Records[0][8].BooleanValue)) assert.Equal(t, 1.5, awsV1.Float64Value(selectOut.Records[0][9].DoubleValue)) assert.Equal(t, `{"ok":true}`, awsV1.StringValue(selectOut.Records[0][10].StringValue)) assert.Equal(t, "2021-03-04T05:06:07Z", awsV1.StringValue(selectOut.Records[0][11].StringValue)) // Error code and message checked against Aurora PostgreSQL 17.7 on 2026-09-13. for _, unsupported := range []struct{ sql, typeName string }{ {"select point(1, 2)", "POINT"}, {"select interval '1 day 2 hours'", "INTERVAL"}, {"select 12.34::money", "MONEY"}, {"select box '((0,0),(1,1))'", "BOX"}, {"select circle '<(0,0),1>'", "CIRCLE"}, {"select line '{1,2,3}'", "LINE"}, {"select lseg '[(0,0),(1,1)]'", "LSEG"}, {"select path '[(0,0),(1,1)]'", "PATH"}, {"select polygon '((0,0),(1,1),(1,0))'", "POLYGON"}, } { _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String(unsupported.sql), }) assertAwsErrorCode(t, err, "UnsupportedResultException") var unsupportedErr awserr.Error require.ErrorAs(t, err, &unsupportedErr) assert.Equal(t, "The result contains the unsupported data type "+unsupported.typeName+".", unsupportedErr.Message()) } textCastOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select point(1, 2)::text as p"), }) require.NoError(t, err) assert.Equal(t, "(1,2)", awsV1.StringValue(textCastOut.Records[0][0].StringValue)) updateOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("update data_api_items set score = 8 where id = 'commit-1' and score = 7"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(updateOut.NumberOfRecordsUpdated)) staleUpdateOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("update data_api_items set score = 9 where id = 'commit-1' and score = 7"), }) require.NoError(t, err) assert.Equal(t, int64(0), awsV1.Int64Value(staleUpdateOut.NumberOfRecordsUpdated)) upsertOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String(`insert into data_api_items (id, title, score, payload) values ('commit-1', 'upserted', 11, decode('0A0B', 'hex')) on conflict (id) do update set title = excluded.title, score = excluded.score, payload = excluded.payload`), }) require.NoError(t, err) assert.GreaterOrEqual(t, awsV1.Int64Value(upsertOut.NumberOfRecordsUpdated), int64(1)) _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values ('page-1', 'alpha', 1), ('page-2', 'beta', 2), ('page-3', 'alphabet', 3)"), }) require.NoError(t, err) predicateOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select id from data_api_items where (title like 'alpha%' or id in (select id from data_api_items where score = 11)) order by score desc limit 2 offset 0"), IncludeResultMetadata: awsV1.Bool(true), }) require.NoError(t, err) require.Len(t, predicateOut.Records, 2) assert.Equal(t, "commit-1", awsV1.StringValue(predicateOut.Records[0][0].StringValue)) assert.Equal(t, "page-3", awsV1.StringValue(predicateOut.Records[1][0].StringValue)) lessThanAndNotInOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select count(*) as count from data_api_items where score < 3 and id not in ('commit-1')"), IncludeResultMetadata: awsV1.Bool(true), }) require.NoError(t, err) assert.Equal(t, "count", awsV1.StringValue(lessThanAndNotInOut.ColumnMetadata[0].Name)) assert.Equal(t, int64(2), awsV1.Int64Value(lessThanAndNotInOut.Records[0][0].LongValue)) emptyOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select id from data_api_items where id = 'missing'"), IncludeResultMetadata: awsV1.Bool(true), }) require.NoError(t, err) assert.Equal(t, "id", awsV1.StringValue(emptyOut.ColumnMetadata[0].Name)) assert.Empty(t, emptyOut.Records) assert.Equal(t, int64(0), awsV1.Int64Value(emptyOut.NumberOfRecordsUpdated)) replaceTx, err := dataSvc.BeginTransaction(&rdsdataservice.BeginTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), }) require.NoError(t, err) deleteOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: replaceTx.TransactionId, Sql: awsV1.String("delete from data_api_items where id = 'page-2'"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(deleteOut.NumberOfRecordsUpdated)) replaceInsertOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: replaceTx.TransactionId, Sql: awsV1.String("insert into data_api_items (id, title, score) values ('page-4', 'replacement', 4)"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(replaceInsertOut.NumberOfRecordsUpdated)) _, err = dataSvc.CommitTransaction(&rdsdataservice.CommitTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), TransactionId: replaceTx.TransactionId, }) require.NoError(t, err) replaceCountOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select count(*) as count from data_api_items where id in ('page-2', 'page-4')"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(replaceCountOut.Records[0][0].LongValue)) rollbackTx, err := dataSvc.BeginTransaction(&rdsdataservice.BeginTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), }) require.NoError(t, err) rollbackInsertOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: rollbackTx.TransactionId, Sql: awsV1.String("insert into data_api_items (id, title, score) values ('rollback-1', 'rolled back', 9)"), }) require.NoError(t, err) assert.Equal(t, int64(1), awsV1.Int64Value(rollbackInsertOut.NumberOfRecordsUpdated)) _, err = dataSvc.RollbackTransaction(&rdsdataservice.RollbackTransactionInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), TransactionId: rollbackTx.TransactionId, }) require.NoError(t, err) _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), TransactionId: rollbackTx.TransactionId, Sql: awsV1.String("select 1"), }) assertAwsErrorCode(t, err, "TransactionNotFoundException") countOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select count(*) as count from data_api_items where id in ('commit-1', 'rollback-1')"), IncludeResultMetadata: awsV1.Bool(true), }) require.NoError(t, err) assert.Equal(t, "count", awsV1.StringValue(countOut.ColumnMetadata[0].Name)) assert.Equal(t, int64(1), awsV1.Int64Value(countOut.Records[0][0].LongValue)) batchOut, err := dataSvc.BatchExecuteStatement(&rdsdataservice.BatchExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values (:id, :title, :score)"), ParameterSets: [][]*rdsdataservice.SqlParameter{ { {Name: awsV1.String("id"), Value: &rdsdataservice.Field{StringValue: awsV1.String("batch-1")}}, {Name: awsV1.String("title"), Value: &rdsdataservice.Field{StringValue: awsV1.String("batch one")}}, {Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(1)}}, }, { {Name: awsV1.String("id"), Value: &rdsdataservice.Field{StringValue: awsV1.String("batch-2")}}, {Name: awsV1.String("title"), Value: &rdsdataservice.Field{StringValue: awsV1.String("batch two")}}, {Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(2)}}, }, }, }) require.NoError(t, err) require.Len(t, batchOut.UpdateResults, 2) assert.Empty(t, batchOut.UpdateResults[0].GeneratedFields) batchCountOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select count(*) as count from data_api_items where id in ('batch-1', 'batch-2')"), }) require.NoError(t, err) assert.Equal(t, int64(2), awsV1.Int64Value(batchCountOut.Records[0][0].LongValue)) _, err = dataSvc.BatchExecuteStatement(&rdsdataservice.BatchExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select id from data_api_items where score = :score"), ParameterSets: [][]*rdsdataservice.SqlParameter{ {{Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(1)}}}, {{Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(2)}}}, }, }) assertAwsErrorCode(t, err, "BadRequestException") noSetsOut, err := dataSvc.BatchExecuteStatement(&rdsdataservice.BatchExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values ('batch-no-sets', 'no sets', 99)"), }) require.NoError(t, err) assert.Empty(t, noSetsOut.UpdateResults) noSetsCountOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select count(*) as count from data_api_items where id = 'batch-no-sets'"), }) require.NoError(t, err) assert.Equal(t, int64(0), awsV1.Int64Value(noSetsCountOut.Records[0][0].LongValue)) _, err = dataSvc.BatchExecuteStatement(&rdsdataservice.BatchExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values (:id, :title, :score)"), ParameterSets: [][]*rdsdataservice.SqlParameter{{{Name: awsV1.String("id")}}}, }) assertAwsErrorCode(t, err, "BadRequestException") _, err = dataSvc.ExecuteSql(&rdsdataservice.ExecuteSqlInput{ DbClusterOrInstanceArn: awsV1.String(resourceArn), AwsSecretStoreArn: awsV1.String(secretArn), Database: awsV1.String(database), SqlStatements: awsV1.String("select 1"), }) assertAwsErrorCode(t, err, "BadRequestException") _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values (:id, :title, :score)"), Parameters: []*rdsdataservice.SqlParameter{ {Name: awsV1.String("id"), Value: &rdsdataservice.Field{StringValue: awsV1.String("param-1")}}, {Name: awsV1.String("title"), Value: &rdsdataservice.Field{StringValue: awsV1.String("parameterized")}}, {Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(42)}}, }, }) require.NoError(t, err) paramOut, err := dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("select title, score from data_api_items where id = :id and score = :score"), IncludeResultMetadata: awsV1.Bool(true), Parameters: []*rdsdataservice.SqlParameter{ {Name: awsV1.String("id"), Value: &rdsdataservice.Field{StringValue: awsV1.String("param-1")}}, {Name: awsV1.String("score"), Value: &rdsdataservice.Field{LongValue: awsV1.Int64(42)}}, }, }) require.NoError(t, err) require.Len(t, paramOut.Records, 1) assert.Equal(t, "parameterized", awsV1.StringValue(paramOut.Records[0][0].StringValue)) assert.Equal(t, int64(42), awsV1.Int64Value(paramOut.Records[0][1].LongValue)) _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), Sql: awsV1.String("insert into data_api_items (id, title, score) values ('commit-1', 'duplicate', 1)"), }) assertAwsErrorCode(t, err, "DatabaseErrorException") _, err = dataSvc.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{ ResourceArn: awsV1.String(resourceArn), SecretArn: awsV1.String(secretArn), Database: awsV1.String(database), // Unlike MySQL, PostgreSQL accepts "select from t" as a valid zero-column // query, so use a statement that is malformed under any SQL dialect. Sql: awsV1.String("select * from data_api_items where"), }) assertAwsErrorCode(t, err, "DatabaseErrorException") } func TestRdsDataApiGoSdkV2(t *testing.T) { ctx := context.Background() rdsSvc := testutil.RDSClient() secretsSvc := testutil.SecretsManagerClient() dataSvc := rdsDataV2Client() clusterID := "go-rds-data-v2-mysql-" + strconv.FormatInt(time.Now().UnixNano(), 36) database := "app" username := "admin" password := "secret123" secretName := "go-rds-data-v2/" + clusterID var secretArn string var resourceArn string t.Cleanup(func() { if secretArn != "" { _, _ = secretsSvc.DeleteSecret(ctx, &secretsmanager.DeleteSecretInput{ SecretId: awsV2.String(secretArn), ForceDeleteWithoutRecovery: awsV2.Bool(true), }) } if resourceArn != "" { _, _ = rdsSvc.DeleteDBCluster(ctx, &rds.DeleteDBClusterInput{ DBClusterIdentifier: awsV2.String(clusterID), SkipFinalSnapshot: awsV2.Bool(true), }) } }) create, err := rdsSvc.CreateDBCluster(ctx, &rds.CreateDBClusterInput{ DBClusterIdentifier: awsV2.String(clusterID), Engine: awsV2.String("aurora-mysql"), EngineVersion: awsV2.String("8.0.mysql_aurora.3.08.0"), MasterUsername: awsV2.String(username), MasterUserPassword: awsV2.String(password), DatabaseName: awsV2.String(database), EngineMode: awsV2.String("provisioned"), Tags: []types.Tag{{Key: awsV2.String("test"), Value: awsV2.String("rds-data-v2")}}, }) if err != nil { t.Skipf("RDS MySQL cluster unavailable in this environment: %v", err) } require.NotNil(t, create.DBCluster) resourceArn = awsV2.ToString(create.DBCluster.DBClusterArn) require.NotEmpty(t, resourceArn) secret, err := secretsSvc.CreateSecret(ctx, &secretsmanager.CreateSecretInput{ Name: awsV2.String(secretName), SecretString: awsV2.String(fmt.Sprintf(`{"username":%q,"password":%q}`, username, password)), }) require.NoError(t, err) secretArn = awsV2.ToString(secret.ARN) require.NotEmpty(t, secretArn) executeEventuallyV2(t, ctx, dataSvc, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String(`create table if not exists data_api_v2_items ( id varchar(64) primary key, name varchar(255), qty bigint, payload blob null, active boolean null, ratio double null )`), }) tx, err := dataSvc.BeginTransaction(ctx, &rdsdata.BeginTransactionInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), }) require.NoError(t, err) require.NotEmpty(t, awsV2.ToString(tx.TransactionId)) insertOut, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), TransactionId: tx.TransactionId, Sql: awsV2.String("insert into data_api_v2_items (id, name, qty, payload, active, ratio) values ('v2-1', 'second client', 12, UNHEX('0A0B'), true, 2.5)"), }) require.NoError(t, err) assert.Equal(t, int64(1), insertOut.NumberOfRecordsUpdated) assert.Nil(t, insertOut.Records, "AWS omits records for a statement that returns no result set") readInTx, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), TransactionId: tx.TransactionId, Sql: awsV2.String("select count(*) as count from data_api_v2_items where id = 'v2-1'"), }) require.NoError(t, err) assert.Equal(t, int64(1), fieldLongValue(t, readInTx.Records[0][0])) commitOut, err := dataSvc.CommitTransaction(ctx, &rdsdata.CommitTransactionInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), TransactionId: tx.TransactionId, }) require.NoError(t, err) assert.NotEmpty(t, awsV2.ToString(commitOut.TransactionStatus)) selectOut, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("select name, qty, payload, active, ratio from data_api_v2_items where id = 'v2-1'"), IncludeResultMetadata: true, }) require.NoError(t, err) require.Len(t, selectOut.ColumnMetadata, 5) assert.Equal(t, "name", awsV2.ToString(selectOut.ColumnMetadata[0].Name)) assert.Equal(t, "name", awsV2.ToString(selectOut.ColumnMetadata[0].Label)) assert.NotEmpty(t, awsV2.ToString(selectOut.ColumnMetadata[0].TypeName)) assert.Equal(t, "data_api_v2_items", awsV2.ToString(selectOut.ColumnMetadata[0].TableName)) require.Len(t, selectOut.Records, 1) assert.Equal(t, "second client", fieldStringValue(t, selectOut.Records[0][0])) assert.Equal(t, int64(12), fieldLongValue(t, selectOut.Records[0][1])) assert.Equal(t, []byte{10, 11}, fieldBlobValue(t, selectOut.Records[0][2])) assert.True(t, fieldBooleanValue(t, selectOut.Records[0][3])) assert.Equal(t, 2.5, fieldDoubleValue(t, selectOut.Records[0][4])) rollbackTx, err := dataSvc.BeginTransaction(ctx, &rdsdata.BeginTransactionInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), }) require.NoError(t, err) _, err = dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), TransactionId: rollbackTx.TransactionId, Sql: awsV2.String("insert into data_api_v2_items (id, name, qty) values ('v2-rollback', 'gone', 1)"), }) require.NoError(t, err) _, err = dataSvc.RollbackTransaction(ctx, &rdsdata.RollbackTransactionInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), TransactionId: rollbackTx.TransactionId, }) require.NoError(t, err) _, err = dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), TransactionId: rollbackTx.TransactionId, Sql: awsV2.String("select 1"), }) assertSmithyErrorCode(t, err, "TransactionNotFoundException") _, err = dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("insert into data_api_v2_items (id, name, qty) values (:id, :name, :qty)"), Parameters: []rdsdatatypes.SqlParameter{ {Name: awsV2.String("id"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "v2-param"}}, {Name: awsV2.String("name"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "parameterized"}}, {Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 42}}, }, }) require.NoError(t, err) paramOut, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("select name, qty from data_api_v2_items where id = :id and qty = :qty"), IncludeResultMetadata: true, Parameters: []rdsdatatypes.SqlParameter{ {Name: awsV2.String("id"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "v2-param"}}, {Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 42}}, }, }) require.NoError(t, err) require.Len(t, paramOut.Records, 1) assert.Equal(t, "parameterized", fieldStringValue(t, paramOut.Records[0][0])) assert.Equal(t, int64(42), fieldLongValue(t, paramOut.Records[0][1])) batchOut, err := dataSvc.BatchExecuteStatement(ctx, &rdsdata.BatchExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("insert into data_api_v2_items (id, name, qty) values (:id, :name, :qty)"), ParameterSets: [][]rdsdatatypes.SqlParameter{ { {Name: awsV2.String("id"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "v2-batch-1"}}, {Name: awsV2.String("name"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "batch one"}}, {Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 1}}, }, { {Name: awsV2.String("id"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "v2-batch-2"}}, {Name: awsV2.String("name"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "batch two"}}, {Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 2}}, }, }, }) require.NoError(t, err) require.Len(t, batchOut.UpdateResults, 2) batchCountOut, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("select count(*) as count from data_api_v2_items where id in ('v2-batch-1', 'v2-batch-2')"), }) require.NoError(t, err) assert.Equal(t, int64(2), fieldLongValue(t, batchCountOut.Records[0][0])) _, err = dataSvc.BatchExecuteStatement(ctx, &rdsdata.BatchExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("select id from data_api_v2_items where qty = :qty"), ParameterSets: [][]rdsdatatypes.SqlParameter{ {{Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 1}}}, {{Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 2}}}, }, }) assertSmithyErrorCode(t, err, "BadRequestException") noSetsOut, err := dataSvc.BatchExecuteStatement(ctx, &rdsdata.BatchExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("insert into data_api_v2_items (id, name, qty) values ('v2-batch-no-sets', 'no sets', 99)"), }) require.NoError(t, err) assert.Empty(t, noSetsOut.UpdateResults) noSetsCountOut, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("select count(*) as count from data_api_v2_items where id = 'v2-batch-no-sets'"), }) require.NoError(t, err) assert.Equal(t, int64(0), fieldLongValue(t, noSetsCountOut.Records[0][0])) _, err = dataSvc.ExecuteSql(ctx, &rdsdata.ExecuteSqlInput{ DbClusterOrInstanceArn: awsV2.String(resourceArn), AwsSecretStoreArn: awsV2.String(secretArn), Database: awsV2.String(database), SqlStatements: awsV2.String("select 1"), }) assertSmithyErrorCode(t, err, "BadRequestException") } // TestRdsDataApiGoSdkPostgresV2 mirrors TestRdsDataApiGoSdkV2 against an // aurora-postgresql cluster to verify RDS Data API parity across engines. func TestRdsDataApiGoSdkPostgresV2(t *testing.T) { ctx := context.Background() rdsSvc := testutil.RDSClient() secretsSvc := testutil.SecretsManagerClient() dataSvc := rdsDataV2Client() clusterID := "go-rds-data-v2-postgres-" + strconv.FormatInt(time.Now().UnixNano(), 36) database := "app" username := "admin" password := "secret123" secretName := "go-rds-data-v2-postgres/" + clusterID var secretArn string var resourceArn string t.Cleanup(func() { if secretArn != "" { _, _ = secretsSvc.DeleteSecret(ctx, &secretsmanager.DeleteSecretInput{ SecretId: awsV2.String(secretArn), ForceDeleteWithoutRecovery: awsV2.Bool(true), }) } if resourceArn != "" { _, _ = rdsSvc.DeleteDBCluster(ctx, &rds.DeleteDBClusterInput{ DBClusterIdentifier: awsV2.String(clusterID), SkipFinalSnapshot: awsV2.Bool(true), }) } }) create, err := rdsSvc.CreateDBCluster(ctx, &rds.CreateDBClusterInput{ DBClusterIdentifier: awsV2.String(clusterID), Engine: awsV2.String("aurora-postgresql"), MasterUsername: awsV2.String(username), MasterUserPassword: awsV2.String(password), DatabaseName: awsV2.String(database), EngineMode: awsV2.String("provisioned"), Tags: []types.Tag{{Key: awsV2.String("test"), Value: awsV2.String("rds-data-v2-postgres")}}, }) if err != nil { t.Skipf("RDS PostgreSQL cluster unavailable in this environment: %v", err) } require.NotNil(t, create.DBCluster) resourceArn = awsV2.ToString(create.DBCluster.DBClusterArn) require.NotEmpty(t, resourceArn) secret, err := secretsSvc.CreateSecret(ctx, &secretsmanager.CreateSecretInput{ Name: awsV2.String(secretName), SecretString: awsV2.String(fmt.Sprintf(`{"username":%q,"password":%q}`, username, password)), }) require.NoError(t, err) secretArn = awsV2.ToString(secret.ARN) require.NotEmpty(t, secretArn) executeEventuallyV2(t, ctx, dataSvc, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String(`create table if not exists data_api_v2_items ( id varchar(64) primary key, name varchar(255), qty bigint, payload bytea null, active boolean null, ratio double precision null )`), }) tx, err := dataSvc.BeginTransaction(ctx, &rdsdata.BeginTransactionInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), }) require.NoError(t, err) require.NotEmpty(t, awsV2.ToString(tx.TransactionId)) insertOut, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), TransactionId: tx.TransactionId, Sql: awsV2.String("insert into data_api_v2_items (id, name, qty, payload, active, ratio) values ('v2-1', 'second client', 12, decode('0A0B', 'hex'), true, 2.5)"), }) require.NoError(t, err) assert.Equal(t, int64(1), insertOut.NumberOfRecordsUpdated) assert.Nil(t, insertOut.Records, "AWS omits records for a statement that returns no result set") readInTx, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), TransactionId: tx.TransactionId, Sql: awsV2.String("select count(*) as count from data_api_v2_items where id = 'v2-1'"), }) require.NoError(t, err) assert.Equal(t, int64(1), fieldLongValue(t, readInTx.Records[0][0])) commitOut, err := dataSvc.CommitTransaction(ctx, &rdsdata.CommitTransactionInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), TransactionId: tx.TransactionId, }) require.NoError(t, err) assert.NotEmpty(t, awsV2.ToString(commitOut.TransactionStatus)) selectOut, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("select name, qty, payload, active, ratio from data_api_v2_items where id = 'v2-1'"), IncludeResultMetadata: true, }) require.NoError(t, err) require.Len(t, selectOut.ColumnMetadata, 5) assert.Equal(t, "name", awsV2.ToString(selectOut.ColumnMetadata[0].Name)) assert.Equal(t, "name", awsV2.ToString(selectOut.ColumnMetadata[0].Label)) assert.NotEmpty(t, awsV2.ToString(selectOut.ColumnMetadata[0].TypeName)) assert.Equal(t, "data_api_v2_items", awsV2.ToString(selectOut.ColumnMetadata[0].TableName)) require.Len(t, selectOut.Records, 1) assert.Equal(t, "second client", fieldStringValue(t, selectOut.Records[0][0])) assert.Equal(t, int64(12), fieldLongValue(t, selectOut.Records[0][1])) assert.Equal(t, []byte{10, 11}, fieldBlobValue(t, selectOut.Records[0][2])) assert.True(t, fieldBooleanValue(t, selectOut.Records[0][3])) assert.Equal(t, 2.5, fieldDoubleValue(t, selectOut.Records[0][4])) rollbackTx, err := dataSvc.BeginTransaction(ctx, &rdsdata.BeginTransactionInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), }) require.NoError(t, err) _, err = dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), TransactionId: rollbackTx.TransactionId, Sql: awsV2.String("insert into data_api_v2_items (id, name, qty) values ('v2-rollback', 'gone', 1)"), }) require.NoError(t, err) _, err = dataSvc.RollbackTransaction(ctx, &rdsdata.RollbackTransactionInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), TransactionId: rollbackTx.TransactionId, }) require.NoError(t, err) _, err = dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), TransactionId: rollbackTx.TransactionId, Sql: awsV2.String("select 1"), }) assertSmithyErrorCode(t, err, "TransactionNotFoundException") _, err = dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("insert into data_api_v2_items (id, name, qty) values (:id, :name, :qty)"), Parameters: []rdsdatatypes.SqlParameter{ {Name: awsV2.String("id"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "v2-param"}}, {Name: awsV2.String("name"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "parameterized"}}, {Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 42}}, }, }) require.NoError(t, err) paramOut, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("select name, qty from data_api_v2_items where id = :id and qty = :qty"), IncludeResultMetadata: true, Parameters: []rdsdatatypes.SqlParameter{ {Name: awsV2.String("id"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "v2-param"}}, {Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 42}}, }, }) require.NoError(t, err) require.Len(t, paramOut.Records, 1) assert.Equal(t, "parameterized", fieldStringValue(t, paramOut.Records[0][0])) assert.Equal(t, int64(42), fieldLongValue(t, paramOut.Records[0][1])) batchOut, err := dataSvc.BatchExecuteStatement(ctx, &rdsdata.BatchExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("insert into data_api_v2_items (id, name, qty) values (:id, :name, :qty)"), ParameterSets: [][]rdsdatatypes.SqlParameter{ { {Name: awsV2.String("id"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "v2-batch-1"}}, {Name: awsV2.String("name"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "batch one"}}, {Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 1}}, }, { {Name: awsV2.String("id"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "v2-batch-2"}}, {Name: awsV2.String("name"), Value: &rdsdatatypes.FieldMemberStringValue{Value: "batch two"}}, {Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 2}}, }, }, }) require.NoError(t, err) require.Len(t, batchOut.UpdateResults, 2) batchCountOut, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("select count(*) as count from data_api_v2_items where id in ('v2-batch-1', 'v2-batch-2')"), }) require.NoError(t, err) assert.Equal(t, int64(2), fieldLongValue(t, batchCountOut.Records[0][0])) _, err = dataSvc.BatchExecuteStatement(ctx, &rdsdata.BatchExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("select id from data_api_v2_items where qty = :qty"), ParameterSets: [][]rdsdatatypes.SqlParameter{ {{Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 1}}}, {{Name: awsV2.String("qty"), Value: &rdsdatatypes.FieldMemberLongValue{Value: 2}}}, }, }) assertSmithyErrorCode(t, err, "BadRequestException") noSetsOut, err := dataSvc.BatchExecuteStatement(ctx, &rdsdata.BatchExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("insert into data_api_v2_items (id, name, qty) values ('v2-batch-no-sets', 'no sets', 99)"), }) require.NoError(t, err) assert.Empty(t, noSetsOut.UpdateResults) noSetsCountOut, err := dataSvc.ExecuteStatement(ctx, &rdsdata.ExecuteStatementInput{ ResourceArn: awsV2.String(resourceArn), SecretArn: awsV2.String(secretArn), Database: awsV2.String(database), Sql: awsV2.String("select count(*) as count from data_api_v2_items where id = 'v2-batch-no-sets'"), }) require.NoError(t, err) assert.Equal(t, int64(0), fieldLongValue(t, noSetsCountOut.Records[0][0])) _, err = dataSvc.ExecuteSql(ctx, &rdsdata.ExecuteSqlInput{ DbClusterOrInstanceArn: awsV2.String(resourceArn), AwsSecretStoreArn: awsV2.String(secretArn), Database: awsV2.String(database), SqlStatements: awsV2.String("select 1"), }) assertSmithyErrorCode(t, err, "BadRequestException") } func rdsDataV1Client(t *testing.T) *rdsdataservice.RDSDataService { t.Helper() sess, err := session.NewSession(&awsV1.Config{ Region: awsV1.String("us-east-1"), Endpoint: awsV1.String(testutil.Endpoint()), DisableSSL: awsV1.Bool(true), Credentials: credentials.NewStaticCredentials("test", "test", ""), }) require.NoError(t, err) return rdsdataservice.New(sess) } func rdsDataV2Client() *rdsdata.Client { return rdsdata.NewFromConfig(testutil.Config()) } func executeEventually(t *testing.T, svc *rdsdataservice.RDSDataService, input *rdsdataservice.ExecuteStatementInput) { t.Helper() require.Eventually(t, func() bool { _, err := svc.ExecuteStatement(input) return err == nil }, 90*time.Second, time.Second) } func executeEventuallyV2(t *testing.T, ctx context.Context, svc *rdsdata.Client, input *rdsdata.ExecuteStatementInput) { t.Helper() require.Eventually(t, func() bool { _, err := svc.ExecuteStatement(ctx, input) return err == nil }, 90*time.Second, time.Second) } func assertAwsErrorCode(t *testing.T, err error, code string) { t.Helper() require.Error(t, err) var awsErr awserr.Error require.ErrorAs(t, err, &awsErr) assert.Equal(t, code, awsErr.Code()) } func assertSmithyErrorCode(t *testing.T, err error, code string) { t.Helper() require.Error(t, err) var apiErr smithy.APIError require.ErrorAs(t, err, &apiErr) assert.Equal(t, code, apiErr.ErrorCode()) } func fieldStringValue(t *testing.T, field rdsdatatypes.Field) string { t.Helper() value, ok := field.(*rdsdatatypes.FieldMemberStringValue) require.Truef(t, ok, "expected stringValue field, got %T", field) return value.Value } func fieldLongValue(t *testing.T, field rdsdatatypes.Field) int64 { t.Helper() value, ok := field.(*rdsdatatypes.FieldMemberLongValue) require.Truef(t, ok, "expected longValue field, got %T", field) return value.Value } func fieldBlobValue(t *testing.T, field rdsdatatypes.Field) []byte { t.Helper() value, ok := field.(*rdsdatatypes.FieldMemberBlobValue) require.Truef(t, ok, "expected blobValue field, got %T", field) return value.Value } func fieldBooleanValue(t *testing.T, field rdsdatatypes.Field) bool { t.Helper() value, ok := field.(*rdsdatatypes.FieldMemberBooleanValue) require.Truef(t, ok, "expected booleanValue field, got %T", field) return value.Value } func fieldDoubleValue(t *testing.T, field rdsdatatypes.Field) float64 { t.Helper() value, ok := field.(*rdsdatatypes.FieldMemberDoubleValue) require.Truef(t, ok, "expected doubleValue field, got %T", field) return value.Value }