Hello dynamodb-shell

ddbsh is an interactive shell for AWS DynamoDB.

DynamoDB Shell (ddbsh) is an interactive command line interface for Amazon DynamoDB. ddbsh is available for download at https://github.com/awslabs/dynamodb-shell.

ddbsh is provided for your use on an AS-IS basis. It can delete, and update table data, as well as drop tables. These operations are irreversible. It can perform scans and queries against your data and these can cost you significant money.

The quickest way to understand ddbsh is through a simple interactive session. First download the software and build the binary.

% ddbsh
ddbsh - version 0.1
us-east-1>

You are now at an interactive prompt where you can execute commands. The prompt shows that you are connected to us-east-1 (this is the default). You can override that if you so desire (commands in ~/.ddbsh_config will be automatically executed when you launch ddbsh). You can also dynamically reconnect to another region.

us-east-1> connect ap-south-1;
CONNECT
ap-south-1> 

That’s all there is to it. Now let’s get back to us-east-1 and take ddbsh for a spin. Let’s make a table. Commands are terminated with the ‘;’ character.

ap-south-1> connect us-east-1;
CONNECT
us-east-1> 
us-east-1> create table ddbsh_demo ( id number ) 
us-east-1> primary key ( id hash );
CREATE
us-east-1>

The CREATE TABLE command (by default) will wait till the table is created. You can have it submit the request and return with the NOWAIT option (see HELP CREATE TABLE for complete options).

By default it creates a table that is On-Demand (you can also create a table with provisioned billing mode, more about that later).

Now let’s insert some data and query it.

us-east-1> insert into ddbsh_demo (id, v) values ( 3, 4 ), (4, "a string value"), (5, {a: 4, b: [10, 11, 12], c: true, d: {x: 10, y: 10}});
INSERT
INSERT
INSERT
us-east-1> select * from ddbsh_demo;
{id: 3, v: 4}
{id: 4, v: "a string value"}
{id: 5, v: {a:4, b:[10, 11, 12], c:TRUE, d:{x:10, y:10}}}
us-east-1>  

You can do more fancy things with your query, like this.

us-east-1> select id from ddbsh_demo where v = 4;
{id: 3}
us-east-1> select * from ddbsh_demo where v.c = true;
{id: 5, v: {a:4, b:[10, 11, 12], c:TRUE, d:{x:10, y:10}}}
us-east-1> select * from ddbsh_demo where v.b[1] = 11;
{id: 5, v: {a:4, b:[10, 11, 12], c:TRUE, d:{x:10, y:10}}}
us-east-1> 

How about making some changes to the data? That’s easy enough.

us-east-1> update ddbsh_demo set z = 14, v.b[1] = 13 where id = 5;
UPDATE (0 read, 1 modified, 0 ccf)
us-east-1> select * from ddbsh_demo where id = 5;
{id: 5, v: {a:4, b:[10, 13, 12], c:TRUE, d:{x:10, y:10}}, z: 14}
us-east-1> 

Careful what you do with ddbsh … if you execute a command without a where clause, it can update more items than you expected. For example, consider this.

us-east-1> select * from ddbsh_demo;
{id: 3, v: 4}
{id: 4, v: "a string value"}
{id: 5, v: {a:4, b:[10, 13, 12, 13, 13], c:TRUE, d:{x:10, y:10}}, z: 14}
us-east-1> update ddbsh_demo set newval = "a new value";
UPDATE (3 read, 3 modified, 0 ccf)
us-east-1> select * from ddbsh_demo;
{id: 3, newval: "a new value", v: 4}
{id: 4, newval: "a new value", v: "a string value"}
{id: 5, newval: "a new value", v: {a:4, b:[10, 13, 12, 13, 13], c:TRUE, d:{x:10, y:10}}, z: 14}
us-east-1> 

Equally, you can accidentally delete more data than you expected.

us-east-1> delete from ddbsh_demo;
DELETE (3 read, 3 modified, 0 ccf)
us-east-1> select * from ddbsh_demo;
us-east-1> 

There, all the data is gone! Hopefully that’s what I intended.

There’s a lot more that you can do with ddbsh – to see what else you can do, check out the HELP command which lists all commands and provides help on each.

Two final things. First, ddbsh also supports a number of DDL commands (in addition to CREATE TABLE).

us-east-1> show tables;
ddbsh_demo | ACTIVE | PAY_PER_REQUEST | STANDARD | ba3c5574-d3ca-469b-aeb8-4ad8f8df9d4e | arn:aws:dynamodb:us-east-1:632195519165:table/ddbsh_demo | TTL DISABLED | GSI: 0 | LSI : 0 |
us-east-1> describe ddbsh_demo;
Name: ddbsh_demo (ACTIVE)
Key: HASH id
Attributes: id, N
Created at: 2023-01-25T12:15:15Z
Table ARN: arn:aws:dynamodb:us-east-1:632195519165:table/ddbsh_demo
Table ID: ba3c5574-d3ca-469b-aeb8-4ad8f8df9d4e
Table size (bytes): 0
Item Count: 0
Billing Mode: On Demand
PITR is Disabled.
GSI: None
LSI: None
Stream: Disabled
Table Class: STANDARD
SSE: Not set
us-east-1> 

Now let’s make some changes.

us-east-1> alter table ddbsh_demo set pitr enabled;
ALTER
us-east-1> alter table ddbsh_demo set billing mode provisioned ( 200 rcu, 300 wcu);
ALTER
us-east-1> alter table ddbsh_demo (v number) create gsi gsi_v on (v hash) projecting all billing mode provisioned ( 10 rcu, 20 wcu );
ALTER
us-east-1> describe ddbsh_demo;
Name: ddbsh_demo (ACTIVE)
Key: HASH id
Attributes: id, N, v, N
Created at: 2023-01-25T12:15:15Z
Table ARN: arn:aws:dynamodb:us-east-1:632195519165:table/ddbsh_demo
Table ID: ba3c5574-d3ca-469b-aeb8-4ad8f8df9d4e
Table size (bytes): 0
Item Count: 0
Billing Mode: Provisioned (200 RCU, 300 WCU)
PITR is Enabled: [2023-01-25T12:28:30Z to 2023-01-25T12:28:30Z]
GSI gsi_v: ( HASH v ), Provisioned (10 RCU, 20 WCU), Projecting (ALL), Status: CREATING, Backfilling: YES
LSI: None
Stream: Disabled
Table Class: STANDARD
SSE: Not set
us-east-1> 

Second, if you want to know what ddbsh is doing under the covers, use the EXPLAIN command. For example, how did ddbsh add the GSI?

us-east-1> explain alter table ddbsh_demo (v number) 
us-east-1> create gsi gsi_v on (v hash)
us-east-1> projecting all
us-east-1> billing mode provisioned ( 10 rcu, 20 wcu );
UpdateTable({
"AttributeDefinitions": [{
"AttributeName": "v",
"AttributeType": "N"
}],
"TableName": "ddbsh_demo",
"GlobalSecondaryIndexUpdates": [{
"Create": {
"IndexName": "gsi_v",
"KeySchema": [{
"AttributeName": "v",
"KeyType": "HASH"
}],
"Projection": {
"ProjectionType": "ALL"
},
"ProvisionedThroughput": {
"ReadCapacityUnits": 10,
"WriteCapacityUnits": 20
}
}
}]
})
us-east-1>

You can similarly use EXPLAIN on DML commands too.

us-east-1> explain update ddbsh_demo set z = 14, v.b[6] = 13 where id = 5;
UpdateItem({
   "TableName":   "ddbsh_demo",
   "Key":   {
      "id":   {
         "N":   "5"
      }
   },
   "UpdateExpression":   "SET #awaa1 = :vwaa1, #awaa2.#awaa3[6] = :vwaa2",
   "ConditionExpression":   "attribute_exists(#awaa4)",
   "ExpressionAttributeNames":   {
      "#awaa1":   "z",
      "#awaa2":   "v",
      "#awaa3":   "b",
      "#awaa4":   "id"
   },
   "ExpressionAttributeValues":   {
      ":vwaa1":   {
         "N":   "14"
      },
      ":vwaa2":   {
         "N":   "13"
      }
   }
})
us-east-1> 

When you issue a SELECT, ddbsh automatically decides how to execute it. To understand that, here’s another example. We create a new table with a PK and RK and EXPLAIN several SELECT statements. The first results in GetItem() the second in Query() and the third in Scan().

us-east-1> create table ddbsh_demo2 ( pk number, rk number ) 
us-east-1> primary key (pk hash, rk range);
CREATE
us-east-1> explain select * from ddbsh_demo2 where pk = 3 and rk = 4;
GetItem({
   "TableName":   "ddbsh_demo2",
   "Key":   {
      "pk":   {
         "N":   "3"
      },
      "rk":   {
         "N":   "4"
      }
   },
   "ConsistentRead":   false,
   "ReturnConsumedCapacity":   "NONE"
})
us-east-1> explain select * from ddbsh_demo2 where pk = 3;
Query({
   "TableName":   "ddbsh_demo2",
   "ConsistentRead":   false,
   "ReturnConsumedCapacity":   "NONE",
   "KeyConditionExpression":   "#ayaa1 = :vyaa1",
   "ExpressionAttributeNames":   {
      "#ayaa1":   "pk"
   },
   "ExpressionAttributeValues":   {
      ":vyaa1":   {
         "N":   "3"
      }
   }
})
us-east-1> explain select * from ddbsh_demo2;
Scan({
   "TableName":   "ddbsh_demo2",
   "ReturnConsumedCapacity":   "NONE",
   "ConsistentRead":   false
})
us-east-1> explain select * from ddbsh_demo2 where pk = 3 and rk > 5;
Query({
   "TableName":   "ddbsh_demo2",
   "ConsistentRead":   false,
   "ReturnConsumedCapacity":   "NONE",
   "KeyConditionExpression":   "#aAaa1 = :vAaa1 AND #aAaa2 > :vAaa2",
   "ExpressionAttributeNames":   {
      "#aAaa1":   "pk",
      "#aAaa2":   "rk"
   },
   "ExpressionAttributeValues":   {
      ":vAaa1":   {
         "N":   "3"
      },
      ":vAaa2":   {
         "N":   "5"
      }
   }
})
us-east-1> 

There you have it, a quick introduction to ddbsh. Take it for a ride! And if you like ddbsh, do tell your friends!

3 thoughts on “Hello dynamodb-shell”

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.