ddbcli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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