Skip to content

bug: RDS Data API returns numberOfRecordsUpdated=0 for INSERT/UPDATE/DELETE operations #12985

@tsuga

Description

@tsuga

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

The RDS Data API (rds-data execute-statement) consistently returns numberOfRecordsUpdated: 0 for all DML operations (INSERT, UPDATE, DELETE), even when these operations successfully modify data in the database.

From the test output:

  1. CREATE TABLE: Returns numberOfRecordsUpdated: 0 (expected, as DDL operations don't update records)
  2. INSERT: Returns numberOfRecordsUpdated: 0 (should return 1)
  3. SELECT: Returns numberOfRecordsUpdated: 0 and shows the data was inserted correctly
  4. UPDATE: Returns numberOfRecordsUpdated: 0 (should return 1)
  5. DELETE: Returns numberOfRecordsUpdated: 0 (should return 1)

The operations themselves work correctly (data is inserted, updated, deleted), but the API response field numberOfRecordsUpdated is always 0.

Expected Behavior

According to the AWS RDS Data API documentation, the numberOfRecordsUpdated field should contain:

The number of records updated by the request.

Expected behavior:

  • INSERT INTO table VALUES (...): numberOfRecordsUpdated: 1
  • UPDATE table SET ... WHERE id = 1: numberOfRecordsUpdated: 1
  • DELETE FROM table WHERE id = 1: numberOfRecordsUpdated: 1
  • SELECT: numberOfRecordsUpdated: 0 (correct, as SELECT doesn't modify data)

How are you starting LocalStack?

With the localstack script

Steps To Reproduce

How are you starting localstack

SERVICES=rds,rds-data,secretsmanager localstack start

Client commands

# Set up environment
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
DATABASE_NAME="testdb"

# Create secret for database credentials
SECRET_CREATE_OUTPUT=$(awslocal secretsmanager create-secret \
    --name test-secret \
    --secret-string '{"username":"testuser","password":"testpass"}' \
    --region us-east-1)
SECRET_ARN=$(echo "$SECRET_CREATE_OUTPUT" | jq -r '.ARN')

# Create Aurora cluster
CLUSTER_CREATE_OUTPUT=$(awslocal rds create-db-cluster \
    --db-cluster-identifier test-cluster \
    --engine aurora-postgresql \
    --master-username testuser \
    --master-user-password testpass \
    --database-name "$DATABASE_NAME" \
    --region us-east-1)
CLUSTER_ARN=$(echo "$CLUSTER_CREATE_OUTPUT" | jq -r '.DBCluster.DBClusterArn')

# 1. CREATE TABLE (returns numberOfRecordsUpdated: 0 - correct)
awslocal rds-data execute-statement \
  --resource-arn "$CLUSTER_ARN" \
  --secret-arn "$SECRET_ARN" \
  --database "$DATABASE_NAME" \
  --sql "CREATE TABLE test_table (id SERIAL PRIMARY KEY, name VARCHAR(50));"

# 2. INSERT (BUG: returns numberOfRecordsUpdated: 0, should be 1)
awslocal rds-data execute-statement \
  --resource-arn "$CLUSTER_ARN" \
  --secret-arn "$SECRET_ARN" \
  --database "$DATABASE_NAME" \
  --sql "INSERT INTO test_table (name) VALUES ('initial_name');" \
  --output json

# 3. Verify INSERT worked - SELECT shows data exists
SELECT_RESULT=$(awslocal rds-data execute-statement \
  --resource-arn "$CLUSTER_ARN" \
  --secret-arn "$SECRET_ARN" \
  --database "$DATABASE_NAME" \
  --sql "SELECT id, name FROM test_table;" \
  --output json)
RECORD_ID=$(echo "$SELECT_RESULT" | jq -r '.records[0][0].longValue')

# 4. UPDATE (BUG: returns numberOfRecordsUpdated: 0, should be 1)
awslocal rds-data execute-statement \
  --resource-arn "$CLUSTER_ARN" \
  --secret-arn "$SECRET_ARN" \
  --database "$DATABASE_NAME" \
  --sql "UPDATE test_table SET name = 'updated_name' WHERE id = $RECORD_ID;" \
  --output json

# 5. DELETE (BUG: returns numberOfRecordsUpdated: 0, should be 1)
awslocal rds-data execute-statement \
  --resource-arn "$CLUSTER_ARN" \
  --secret-arn "$SECRET_ARN" \
  --database "$DATABASE_NAME" \
  --sql "DELETE FROM test_table WHERE id = $RECORD_ID;" \
  --output json

Actual Output

# CREATE TABLE response (correct)
{"numberOfRecordsUpdated": 0, "generatedFields": []}

# INSERT response (BUG: should be 1)
{"numberOfRecordsUpdated": 0, "generatedFields": []}

# SELECT response (shows INSERT worked)
{
  "records": [[{"longValue": 1}, {"stringValue": "initial_name"}]],
  "numberOfRecordsUpdated": 0
}

# UPDATE response (BUG: should be 1)
{"numberOfRecordsUpdated": 0, "generatedFields": []}

# DELETE response (BUG: should be 1)
{"numberOfRecordsUpdated": 0, "generatedFields": []}

Environment

- OS: Linux (Debian GNU/Linux 12 bookworm in dev container)
- LocalStack:
  - LocalStack version: 4.7.1.dev29
  - LocalStack build date: 2025-08-08
  - LocalStack build git hash: 2521cefc7

Anything else?

This bug breaks compatibility with SQLAlchemy and other ORMs that rely on numberOfRecordsUpdated for:

  • Optimistic concurrency control
  • Detecting successful operations
  • Batch operation result validation

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions