ddbcli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,148 @@
1
+ = sdbcli
2
+
3
+ == Description
4
+
5
+ ddbcli is an interactive command-line client of Amazon DynamoDB.
6
+
7
+ == Source Code
8
+
9
+ https://bitbucket.org/winebarrel/ddbcli
10
+
11
+ == Install
12
+
13
+ shell> gem install ddbcli
14
+ shell> export AWS_ACCESS_KEY_ID='...'
15
+ shell> export AWS_SECRET_ACCESS_KEY='...'
16
+ shell> export DDB_REGION=ap-northeast-1
17
+ shell> ddbcli -e 'show tables'
18
+ [
19
+ "employees"
20
+ ]
21
+
22
+ shell> ddbcli -h
23
+ Usage: ddbcli [options]
24
+ -k, --access-key=ACCESS_KEY
25
+ -s, --secret-key=SECRET_KEY
26
+ -r, --region=REGION_OR_ENDPOINT
27
+ -e, --eval=COMMAND
28
+ -t, --timeout=SECOND
29
+ --consistent-read
30
+ --retry=NUM
31
+ --retry-interval=SECOND
32
+ --debug
33
+ -h, --help
34
+
35
+ ##### Query #####
36
+
37
+ SHOW TABLES
38
+ displays a table list
39
+
40
+ SHOW REGIONS
41
+ displays a region list
42
+
43
+ SHOW CREATE TABLE table_name
44
+ displays a CREATE TABLE statement
45
+
46
+ CREATE TABLES table_name (
47
+ key_name {STRING|NUMBER|BINARY} HASH
48
+ [, key_name {STRING|NUMBER|BINARY} RANGE]
49
+ [, INDEX index1_name (attr1 {STRING|NUMBER|BINARY}) {ALL|KEYS_ONLY|INCLUDE (attr, ...)}
50
+ , INDEX index2_name (attr2 {STRING|NUMBER|BINARY}) {ALL|KEYS_ONLY|INCLUDE (attr, ...)}
51
+ , ...]
52
+ )
53
+ creates a table
54
+
55
+ DROP TABLE table_name
56
+ deletes a table
57
+
58
+ GET {*|attrs} FROM table_name WHERE key1 = '...' AND ...
59
+ gets items
60
+
61
+ INSERT INTO table_name (attr1, attr2, ...) VALUES ('val1', 'val2', ...), ('val3', 'val4', ...), ...
62
+ creates items
63
+
64
+ UPDATE table_name {SET|ADD} attr1 = 'val1', ... WHERE key1 = '...' AND ...
65
+ UPDATE ALL table_name {SET|ADD} attr1 = 'val1', ... [WHERE attr1 = '...' AND ...] [LIMIT limit]
66
+ updates items
67
+
68
+ DELETE FROM table_name WHERE key1 = '...' AND ..
69
+ DELETE ALL FROM table_name WHERE [WHERE attr1 = '...' AND ...] [ORDER {ASC|DESC}] [LIMIT limit]
70
+ deletes items
71
+
72
+ SELECT {*|attrs|COUNT(*)} FROM table_name [USE INDEX (index_name)] [WHERE key1 = '...' AND ...] [ORDER {ASC|DESC}] [LIMIT limit]
73
+ SELECT ALL {*|attrs|COUNT(*)} FROM table_name [WHERE attr1 = '...' AND ...] [LIMIT limit]
74
+ queries using the Query/Scan action
75
+ see http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/QueryAndScan.html
76
+
77
+ DESC[RIBE] table_name
78
+ displays information about the table
79
+
80
+ USE region_or_endpoint
81
+ changes an endpoint
82
+
83
+ NEXT
84
+ displays a continuation of a result
85
+ (NEXT statement is published after SELECT statement)
86
+
87
+
88
+ ##### Type #####
89
+
90
+ String
91
+ 'London Bridge is...', "is broken down..." ...
92
+
93
+ Number
94
+ 10, 100, 0.3 ...
95
+
96
+ Binary
97
+ x'123456789abcd...', x"123456789abcd..." ...
98
+
99
+ Identifier
100
+ `ABCD...` or Non-keywords
101
+
102
+
103
+ ##### Operator #####
104
+
105
+ Query (SELECT)
106
+ = | <= | < | >= | > | BEGINS_WITH | BETWEEN
107
+ see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-KeyConditions
108
+
109
+ Scan (SELECT ALL)
110
+ = | <> | != | <= | < | >= | > | NOT NULL | NULL | CONTAINS | NOT CONTAINS | BEGINS_WITH | IN | BETWEEN
111
+ see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html#DDB-Scan-request-ScanFilter
112
+
113
+
114
+ ##### Pass to Ruby/Shell #####
115
+
116
+ Ryby
117
+ query | ruby_script
118
+
119
+ ex) SELECT ALL * FROM employees WHERE gender = 'M' | birth_date.map {|i| Time.parse(i) };
120
+ [
121
+ "1957-09-16 00:00:00 +0900",
122
+ "1954-12-16 00:00:00 +0900",
123
+ "1964-05-23 00:00:00 +0900",
124
+ ...
125
+
126
+ Shell
127
+ query ! shell_command
128
+
129
+ ex) SELECT ALL * FROM employees LIMIT 10 ! sort;
130
+ {"birth_date"=>"1957-09-16", "emp_no"=>452020,...
131
+ {"birth_date"=>"1963-07-14", "emp_no"=>16998, ...
132
+ {"birth_date"=>"1964-04-30", "emp_no"=>225407,...
133
+ ...
134
+
135
+
136
+ ##### Command #####
137
+
138
+ .help displays this message
139
+ .quit | .exit exits sdbcli
140
+ .consistent (true|false)? displays ConsistentRead parameter or changes it
141
+ .debug (true|false)? displays a debug status or changes it
142
+ .retry NUM? displays number of times of a retry or changes it
143
+ .retry_interval SECOND? displays a retry interval second or changes it
144
+ .timeout SECOND? displays a timeout second or changes it
145
+ .version displays a version
146
+
147
+ shell> ddbcli # show prompt
148
+
data/bin/ddbcli ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ Version = '0.1.0'
5
+
6
+ require 'rubygems'
7
+ require 'ddbcli'
8
+ require 'readline'
9
+
10
+ options = parse_options
11
+
12
+ driver = DynamoDB::Driver.new(
13
+ options.access_key_id,
14
+ options.secret_access_key,
15
+ options.ddb_endpoint_or_region)
16
+
17
+ driver.timeout = options.timeout
18
+ driver.consistent = !!options.consistent
19
+ driver.retry_num = options.retry_num
20
+ driver.retry_intvl = options.retry_intvl
21
+ driver.debug = options.debug
22
+
23
+ if not $stdin.tty? or options.command
24
+ # run mode
25
+ src = options.command || $stdin.read.strip
26
+
27
+ # complements separator
28
+ unless src =~ /\s*(?:;|\\G)\s*\Z/i
29
+ src << ';'
30
+ end
31
+
32
+ begin
33
+ evaluate_query(driver, src)
34
+ rescue => e
35
+ print_error(e.message)
36
+ print_error(e.backtrace) if driver.debug
37
+ exit 1
38
+ end
39
+ else
40
+ # interactive mode
41
+ src = ''
42
+ prompt1 = lambda { "#{driver.region || 'unknown'}> " }
43
+ prompt2 = lambda { "#{' ' * (prompt1.call.length - 3)}-> " }
44
+
45
+ while buf = Readline.readline((src.empty? ? prompt1.call : prompt2.call), true)
46
+ # ignore blank lines
47
+ if /\A\s*\Z/ =~ buf
48
+ Readline::HISTORY.pop
49
+ next
50
+ end
51
+
52
+ if src.empty? and buf =~ /\A\.(.+)/
53
+ evaluate_command(driver, $1)
54
+ else
55
+ begin
56
+ src << (src.empty? ? buf : ("\n" + buf))
57
+ evaluate_query(driver, src, :show_rows => true)
58
+ rescue => e
59
+ print_error(e.message)
60
+ print_error(e.backtrace) if driver.debug
61
+ end
62
+
63
+ prompt = src.empty? ? prompt1.call : prompt2.call
64
+ end
65
+ end # end of while
66
+ end
data/lib/ddbcli.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'ddbcli/ddb-binary'
2
+ require 'ddbcli/ddb-client'
3
+ require 'ddbcli/ddb-driver'
4
+ require 'ddbcli/ddb-endpoint'
5
+ require 'ddbcli/ddb-error'
6
+ require 'ddbcli/ddb-iteratorable'
7
+ require 'ddbcli/ddb-rubyext'
8
+
9
+ # CLI
10
+ require 'ddbcli/cli/functions'
11
+ require 'ddbcli/cli/evaluate'
12
+ require 'ddbcli/cli/help'
13
+ require 'ddbcli/cli/options'
@@ -0,0 +1,51 @@
1
+ def evaluate_query(driver, src, opts = {})
2
+ ss = StringScanner.new(src.dup)
3
+ buf = ''
4
+
5
+ until ss.eos?
6
+ if (tok = ss.scan %r{[^`'";\\/#]+}) #'
7
+ buf << tok
8
+ elsif (tok = ss.scan /`(?:[^`]|``)*`/)
9
+ buf << tok
10
+ elsif (tok = ss.scan /'(?:[^']|'')*'/) #'
11
+ buf << tok
12
+ elsif (tok = ss.scan /"(?:[^"]|"")*"/) #"
13
+ buf << tok
14
+ elsif (tok = ss.scan %r{/\*/?(?:\n|[^/]|[^*]/)*\*/})
15
+ # nothing to do
16
+ elsif (tok = ss.scan /--[^\r\n]*(?:\r\n|\r|\n|\Z)/)
17
+ # nothing to do
18
+ elsif (tok = ss.scan /#[^\r\n]*(?:\r\n|\r|\n|\Z)/)
19
+ # nothing to do
20
+ elsif (tok = ss.scan /(?:\\;)/)
21
+ buf << ';' # escape of ';'
22
+ elsif (tok = ss.scan /(?:;|\\G)/)
23
+ src.replace(ss.rest)
24
+ query = buf
25
+ buf = ''
26
+
27
+ if query.strip.empty?
28
+ print_error('No query specified')
29
+ next
30
+ end
31
+
32
+ start_time = Time.new
33
+ out = driver.execute(query)
34
+ elapsed = Time.now - start_time
35
+
36
+ if out.kind_of?(DynamoDB::Driver::Rownum)
37
+ print_rownum(out, :time => elapsed)
38
+ elsif out.kind_of?(String)
39
+ puts out
40
+ elsif out
41
+ opts = opts.merge(:inline => (tok != '\G'), :time => elapsed)
42
+ print_json(out, opts)
43
+ end
44
+ elsif (tok = ss.scan /./)
45
+ buf << tok # 落ち穂拾い
46
+ end
47
+ end
48
+
49
+ src.replace(buf.strip)
50
+ buf
51
+ end
@@ -0,0 +1,160 @@
1
+ def print_error(errmsg, opts = {})
2
+ errmsg = errmsg.join("\n") if errmsg.kind_of?(Array)
3
+ errmsg = errmsg.strip.split("\n").map {|i| "// #{i.strip}" }.join("\n")
4
+ errmsg += "\n\n" unless opts[:strip]
5
+ $stderr.puts errmsg
6
+ end
7
+
8
+ def print_rownum(data, opts = {})
9
+ rownum = data.to_i
10
+ msg = "// #{rownum} #{rownum > 1 ? 'rows' : 'row'} changed"
11
+ msg << " (%.2f sec)" % opts[:time] if opts[:time]
12
+ puts msg
13
+ end
14
+
15
+ def print_version
16
+ puts "#{File.basename($0)} #{Version}"
17
+ end
18
+
19
+ def print_json(data, opts = {})
20
+ str = nil
21
+ last_evaluated_key = nil
22
+
23
+ if data.kind_of?(DynamoDB::Iteratorable)
24
+ last_evaluated_key = data.last_evaluated_key
25
+ data = data.data
26
+ end
27
+
28
+ if data.kind_of?(Array) and opts[:inline]
29
+ str = "[\n"
30
+
31
+ data.each_with_index do |item, i|
32
+ str << " #{item.to_json}"
33
+ str << ',' if i < (data.length - 1)
34
+ str << "\n"
35
+ end
36
+
37
+ str << "]"
38
+ else
39
+ if data.kind_of?(Array) or data.kind_of?(Hash)
40
+ str = JSON.pretty_generate(data)
41
+ else
42
+ str = data.to_json
43
+ end
44
+ end
45
+
46
+ str.sub!(/(?:\r\n|\r|\n)*\Z/, "\n")
47
+
48
+ if opts[:show_rows] and data.kind_of?(Array)
49
+ str << "// #{data.length} #{data.length > 1 ? 'rows' : 'row'} in set"
50
+ str << " (%.2f sec)" % opts[:time] if opts[:time]
51
+ str << "\n"
52
+ end
53
+
54
+ if last_evaluated_key
55
+ str << "// has more\n"
56
+ end
57
+
58
+ str << "\n"
59
+ puts str
60
+ end
61
+
62
+ def evaluate_command(driver, cmd_arg)
63
+ cmd, arg = cmd_arg.split(/\s+/, 2).map {|i| i.strip }
64
+ arg = nil if (arg || '').strip.empty?
65
+
66
+ r = /\A#{Regexp.compile(cmd)}/i
67
+
68
+ commands = {
69
+ 'help' => lambda {
70
+ print_help
71
+ },
72
+
73
+ ['exit', 'quit'] => lambda {
74
+ exit 0
75
+ },
76
+
77
+ 'timeout' => lambda {
78
+ case arg
79
+ when nil
80
+ puts driver.timeout
81
+ when /\d+/
82
+ driver.timeout = arg.to_i
83
+ else
84
+ print_error('Invalid argument')
85
+ end
86
+ },
87
+
88
+ 'consistent' => lambda {
89
+ if arg
90
+ r_arg = /\A#{Regexp.compile(arg)}/i
91
+
92
+ if r_arg =~ 'true'
93
+ driver.consistent = true
94
+ elsif r_arg =~ 'false'
95
+ driver.consistent = false
96
+ else
97
+ print_error('Invalid argument')
98
+ end
99
+ else
100
+ puts driver.consistent
101
+ end
102
+ },
103
+
104
+ 'retry' => lambda {
105
+ case arg
106
+ when nil
107
+ puts driver.retry_num
108
+ when /\d+/
109
+ driver.retry_num = arg.to_i
110
+ else
111
+ print_error('Invalid argument')
112
+ end
113
+ },
114
+
115
+ 'retry_interval' => lambda {
116
+ case arg
117
+ when nil
118
+ puts driver.retry_intvl
119
+ when /\d+/
120
+ driver.retry_intvl = arg.to_i
121
+ else
122
+ print_error('Invalid argument')
123
+ end
124
+ },
125
+
126
+ 'debug' => lambda {
127
+ if arg
128
+ r_arg = /\A#{Regexp.compile(arg)}/i
129
+
130
+ if r_arg =~ 'true'
131
+ driver.debug = true
132
+ elsif r_arg =~ 'false'
133
+ driver.debug = false
134
+ else
135
+ print_error('Invalid argument')
136
+ end
137
+ else
138
+ puts driver.debug
139
+ end
140
+ },
141
+
142
+ 'version' => lambda {
143
+ print_version
144
+ }
145
+ }
146
+
147
+ cmd_name, cmd_proc = commands.find do |name, proc|
148
+ if name.kind_of?(Array)
149
+ name.any? {|i| r =~ i }
150
+ else
151
+ r =~ name
152
+ end
153
+ end
154
+
155
+ if cmd_proc
156
+ cmd_proc.call
157
+ else
158
+ print_error('Unknown command')
159
+ end
160
+ end
@@ -0,0 +1,116 @@
1
+ def print_help
2
+ puts <<'EOS'
3
+ ##### Query #####
4
+
5
+ SHOW TABLES
6
+ displays a table list
7
+
8
+ SHOW REGIONS
9
+ displays a region list
10
+
11
+ SHOW CREATE TABLE table_name
12
+ displays a CREATE TABLE statement
13
+
14
+ CREATE TABLES table_name (
15
+ key_name {STRING|NUMBER|BINARY} HASH
16
+ [, key_name {STRING|NUMBER|BINARY} RANGE]
17
+ [, INDEX index1_name (attr1 {STRING|NUMBER|BINARY}) {ALL|KEYS_ONLY|INCLUDE (attr, ...)}
18
+ , INDEX index2_name (attr2 {STRING|NUMBER|BINARY}) {ALL|KEYS_ONLY|INCLUDE (attr, ...)}
19
+ , ...]
20
+ )
21
+ creates a table
22
+
23
+ DROP TABLE table_name
24
+ deletes a table
25
+
26
+ GET {*|attrs} FROM table_name WHERE key1 = '...' AND ...
27
+ gets items
28
+
29
+ INSERT INTO table_name (attr1, attr2, ...) VALUES ('val1', 'val2', ...), ('val3', 'val4', ...), ...
30
+ creates items
31
+
32
+ UPDATE table_name {SET|ADD} attr1 = 'val1', ... WHERE key1 = '...' AND ...
33
+ UPDATE ALL table_name {SET|ADD} attr1 = 'val1', ... [WHERE attr1 = '...' AND ...] [LIMIT limit]
34
+ updates items
35
+
36
+ DELETE FROM table_name WHERE key1 = '...' AND ..
37
+ DELETE ALL FROM table_name WHERE [WHERE attr1 = '...' AND ...] [ORDER {ASC|DESC}] [LIMIT limit]
38
+ deletes items
39
+
40
+ SELECT {*|attrs|COUNT(*)} FROM table_name [USE INDEX (index_name)] [WHERE key1 = '...' AND ...] [ORDER {ASC|DESC}] [LIMIT limit]
41
+ SELECT ALL {*|attrs|COUNT(*)} FROM table_name [WHERE attr1 = '...' AND ...] [LIMIT limit]
42
+ queries using the Query/Scan action
43
+ see http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/QueryAndScan.html
44
+
45
+ DESC[RIBE] table_name
46
+ displays information about the table
47
+
48
+ USE region_or_endpoint
49
+ changes an endpoint
50
+
51
+ NEXT
52
+ displays a continuation of a result
53
+ (NEXT statement is published after SELECT statement)
54
+
55
+
56
+ ##### Type #####
57
+
58
+ String
59
+ 'London Bridge is...', "is broken down..." ...
60
+
61
+ Number
62
+ 10, 100, 0.3 ...
63
+
64
+ Binary
65
+ x'123456789abcd...', x"123456789abcd..." ...
66
+
67
+ Identifier
68
+ `ABCD...` or Non-keywords
69
+
70
+
71
+ ##### Operator #####
72
+
73
+ Query (SELECT)
74
+ = | <= | < | >= | > | BEGINS_WITH | BETWEEN
75
+ see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-KeyConditions
76
+
77
+ Scan (SELECT ALL)
78
+ = | <> | != | <= | < | >= | > | NOT NULL | NULL | CONTAINS | NOT CONTAINS | BEGINS_WITH | IN | BETWEEN
79
+ see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html#DDB-Scan-request-ScanFilter
80
+
81
+
82
+ ##### Pass to Ruby/Shell #####
83
+
84
+ Ryby
85
+ query | ruby_script
86
+
87
+ ex) SELECT ALL * FROM employees WHERE gender = 'M' | birth_date.map {|i| Time.parse(i) };
88
+ [
89
+ "1957-09-16 00:00:00 +0900",
90
+ "1954-12-16 00:00:00 +0900",
91
+ "1964-05-23 00:00:00 +0900",
92
+ ...
93
+
94
+ Shell
95
+ query ! shell_command
96
+
97
+ ex) SELECT ALL * FROM employees LIMIT 10 ! sort;
98
+ {"birth_date"=>"1957-09-16", "emp_no"=>452020,...
99
+ {"birth_date"=>"1963-07-14", "emp_no"=>16998, ...
100
+ {"birth_date"=>"1964-04-30", "emp_no"=>225407,...
101
+ ...
102
+
103
+
104
+ ##### Command #####
105
+
106
+ .help displays this message
107
+ .quit | .exit exits sdbcli
108
+ .consistent (true|false)? displays ConsistentRead parameter or changes it
109
+ .debug (true|false)? displays a debug status or changes it
110
+ .retry NUM? displays number of times of a retry or changes it
111
+ .retry_interval SECOND? displays a retry interval second or changes it
112
+ .timeout SECOND? displays a timeout second or changes it
113
+ .version displays a version
114
+
115
+ EOS
116
+ end