Top Level Namespace

Defined Under Namespace

Modules: DynamoDB Classes: Array

Instance Method Summary collapse

Instance Method Details

#evaluate_command(driver, cmd_arg) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/ddbcli/cli/functions.rb', line 68

def evaluate_command(driver, cmd_arg)
  cmd, arg = cmd_arg.split(/\s+/, 2).map {|i| i.strip }
  arg = nil if (arg || '').strip.empty?

  r = /\A#{Regexp.compile(cmd)}/i

  commands = {
    'help' => lambda {
      print_help
    },

    ['exit', 'quit'] => lambda {
      exit 0
    },

    'timeout' => lambda {
      case arg
      when nil
        puts driver.timeout
      when /\d+/
        driver.timeout = arg.to_i
      else
        print_error('Invalid argument')
      end
    },

    'consistent' => lambda {
      if arg
        r_arg = /\A#{Regexp.compile(arg)}/i

        if r_arg =~ 'true'
          driver.consistent = true
        elsif r_arg =~ 'false'
          driver.consistent = false
        else
          print_error('Invalid argument')
        end
      else
        puts driver.consistent
      end
    },

    'iteratable' => lambda {
      if arg
        r_arg = /\A#{Regexp.compile(arg)}/i

        if r_arg =~ 'true'
          driver.iteratable = true
        elsif r_arg =~ 'false'
          driver.iteratable = false
        else
          print_error('Invalid argument')
        end
      else
        puts driver.iteratable
      end
    },

    'retry' => lambda {
      case arg
      when nil
        puts driver.retry_num
      when /\d+/
        driver.retry_num = arg.to_i
      else
        print_error('Invalid argument')
      end
    },

    'retry_interval' => lambda {
      case arg
      when nil
        puts driver.retry_intvl
      when /\d+/
        driver.retry_intvl = arg.to_i
      else
        print_error('Invalid argument')
      end
    },

    'debug' => lambda {
      if arg
        r_arg = /\A#{Regexp.compile(arg)}/i

        if r_arg =~ 'true'
          driver.debug = true
        elsif r_arg =~ 'false'
          driver.debug = false
        else
          print_error('Invalid argument')
        end
      else
        puts driver.debug
      end
    },

    'version' => lambda {
      print_version
    }
  }

  cmd_name, cmd_proc = commands.find do |name, proc|
    if name.kind_of?(Array)
      name.any? {|i| r =~ i }
    else
      r =~ name
    end
  end

  if cmd_proc
    cmd_proc.call
  else
    print_error('Unknown command')
  end
end

#evaluate_query(driver, src, opts = {}) ⇒ Object



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/ddbcli/cli/evaluate.rb', line 1

def evaluate_query(driver, src, opts = {})
  ss = StringScanner.new(src.dup)
  buf = ''

  until ss.eos?
    if (tok = ss.scan %r{[^`'";\\/#]+}) #'
      buf << tok
    elsif (tok = ss.scan /`(?:[^`]|``)*`/)
      buf << tok
    elsif (tok = ss.scan /'(?:[^']|'')*'/) #'
      buf << tok
    elsif (tok = ss.scan /"(?:[^"]|"")*"/) #"
      buf << tok
    elsif (tok = ss.scan %r{/\*/?(?:\n|[^/]|[^*]/)*\*/})
      # nothing to do
    elsif (tok = ss.scan /--[^\r\n]*(?:\r\n|\r|\n|\Z)/)
      # nothing to do
    elsif (tok = ss.scan /#[^\r\n]*(?:\r\n|\r|\n|\Z)/)
      # nothing to do
    elsif (tok = ss.scan /(?:\\;)/)
      buf << ';' # escape of ';'
    elsif (tok = ss.scan /(?:;|\\G)/)
      src.replace(ss.rest)
      query = buf
      buf = ''

      if query.strip.empty?
        print_error('No query specified')
        next
      end

      start_time = Time.new
      out = driver.execute(query)
      elapsed = Time.now - start_time

      if out.kind_of?(DynamoDB::Driver::Rownum)
        print_rownum(out, :time => elapsed)
      elsif out.kind_of?(String)
        puts out
      elsif out
        opts = opts.merge(:inline => (tok != '\G'), :time => elapsed)
        print_json(out, opts)
      end
    elsif (tok = ss.scan /./)
      buf << tok # 落ち穂拾い
    end
  end

  src.replace(buf.strip)
  buf
end

#parse_options ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/ddbcli/cli/options.rb', line 5

def parse_options
  options = OpenStruct.new
  options.access_key_id     = ENV['AWS_ACCESS_KEY_ID']
  options.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
  options.ddb_endpoint_or_region =
    ENV['DDB_ENDPOINT'] || ENV['DDB_REGION'] || 'dynamodb.us-east-1.amazonaws.com'

  # default value
  options.timeout     = 60
  options.consistent  = false
  options.iteratable  = false
  options.retry_num   = 3
  options.retry_intvl = 10
  options.debug       = false

  ARGV.options do |opt|
    opt.on('-k', '--access-key=ACCESS_KEY')          {|v| options.access_key_id          = v      }
    opt.on('-s', '--secret-key=SECRET_KEY')          {|v| options.secret_access_key      = v      }
    opt.on('-r', '--region=REGION_OR_ENDPOINT')      {|v| options.ddb_endpoint_or_region = v      }

    opt.on('',   '--uri=URI') {|v|
      uri = v
      uri = "http://#{uri}" unless uri =~ %r|\A\w+://|
      uri = URI.parse(uri)
      raise URI::InvalidURIError, "invalid shceme: #{v}" unless /\Ahttps?\Z/ =~ uri.scheme
      options.ddb_endpoint_or_region = uri
    }

    opt.on('-e', '--eval=COMMAND')                   {|v| options.command                = v      }
    opt.on('-t', '--timeout=SECOND', Integer)        {|v| options.timeout                = v.to_i }

    opt.on('',   '--import=TABLE,JSON_FILE') {|v|
      v = v.split(/\s*,\s*/, 2)
      options.import = {:table => v[0], :file => v[1]}
    }

    opt.on('',   '--consistent-read')                {    options.consistent             = true   }
    opt.on('',   '--iteratable')                     {    options.iteratable             = true   }
    opt.on('',   '--retry=NUM', Integer)             {|v| options.retry_num              = v.to_i }
    opt.on('',   '--retry-interval=SECOND', Integer) {|v| options.retry_intvl            = v.to_i }
    opt.on('',   '--debug')                          {    options.debug                  = true   }

    opt.on('-h', '--help') {
      puts opt.help
      puts
      print_help
      exit
    }

    opt.parse!

    unless options.access_key_id and options.secret_access_key and options.ddb_endpoint_or_region
      puts opt.help
      exit 1
    end
  end

  options
end


1
2
3
4
5
6
# File 'lib/ddbcli/cli/functions.rb', line 1

def print_error(errmsg, opts = {})
  errmsg = errmsg.join("\n") if errmsg.kind_of?(Array)
  errmsg = errmsg.strip.split("\n").map {|i| "// #{i.strip}" }.join("\n")
  errmsg += "\n\n" unless opts[:strip]
  $stderr.puts errmsg
end


3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/ddbcli/cli/help.rb', line 3

def print_help
  doc =<<EOS
##### Query #####

SHOW TABLES
  displays a table list

SHOW TABLE STATUS
  displays table statues

SHOW REGIONS
  displays a region list

SHOW CREATE TABLE table_name
  displays a CREATE TABLE statement

CREATE TABLES table_name (
     key_name {STRING|NUMBER|BINARY} HASH
  [, key_name {STRING|NUMBER|BINARY} RANGE]
  [, INDEX index1_name (attr1 {STRING|NUMBER|BINARY}) {ALL|KEYS_ONLY|INCLUDE (attr, ...)}
   , INDEX index2_name (attr2 {STRING|NUMBER|BINARY}) {ALL|KEYS_ONLY|INCLUDE (attr, ...)}
   , ...]
) READ = num, WRITE = num
  creates a table

CREATE TABLES table_name LIKE another_table_name [READ = num, WRITE = num]
  creates a table like another table

DROP TABLE table_name
  deletes a table

ALTER TABLE table_name READ = num, WRITE = num
  updates the provisioned throughput

GET {*|attrs} FROM table_name WHERE key1 = '...' AND ...
  gets items

INSERT INTO table_name (attr1, attr2, ...) VALUES ('val1', 'val2', ...), ('val3', 'val4', ...), ...
INSERT INTO table_name SELECT ...
INSERT INTO table_name SELECT ALL ...
  creates items

UPDATE table_name {SET|ADD} attr1 = 'val1', ... WHERE key1 = '...' AND ...
UPDATE ALL table_name {SET|ADD} attr1 = 'val1', ... [WHERE attr1 = '...' AND ...] [LIMIT limit]
  updates items

DELETE FROM table_name WHERE key1 = '...' AND ..
DELETE ALL FROM table_name WHERE [WHERE attr1 = '...' AND ...] [ORDER {ASC|DESC}] [LIMIT limit]
  deletes items

SELECT {*|attr1,attr2,...|COUNT(*)} FROM table_name [USE INDEX (index_name)] [WHERE key1 = '...' AND ...] [ORDER {ASC|DESC}] [LIMIT limit]
SELECT ALL {*|attr1,attr2,...|COUNT(*)} FROM table_name [WHERE attr1 = '...' AND ...] [LIMIT limit]
SELECT segment/total_segments {*|attr1,attr2,...|COUNT(*)} FROM table_name [WHERE attr1 = '...' AND ...] [LIMIT limit]
  queries using the Query/Scan action
  see http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/QueryAndScan.html

DESC[RIBE] table_name
  displays information about the table

USE region_or_endpoint
  changes an endpoint

NEXT
  displays a continuation of a result
  (NEXT statement is published after SELECT statement)


##### Type #####

String
  'London Bridge is...',  "is broken down..." ...

Number
  10, 100, 0.3 ...

Binary
  x'123456789abcd...', x"123456789abcd..." ...

Identifier
  `ABCD...` or Non-keywords


##### Operator #####

Query (SELECT)
  = | <= | < | >= | > | BEGINS_WITH | BETWEEN
  see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-KeyConditions

Scan (SELECT ALL)
  = | <> | != | <= | < | >= | > | IS NOT NULL | IS NULL | CONTAINS | NOT CONTAINS | BEGINS_WITH | IN | BETWEEN
  see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html#DDB-Scan-request-ScanFilter


##### Pass to Ruby/Shell #####

Ryby
  query | ruby_script

  ex) SELECT ALL * FROM employees WHERE gender = 'M' | birth_date.map {|i| Time.parse(i) };
      [
        "1957-09-16 00:00:00 +0900",
        "1954-12-16 00:00:00 +0900",
        "1964-05-23 00:00:00 +0900",
        ...

Shell
  query ! shell_command

  ex) SELECT ALL * FROM employees LIMIT 10 ! sort;
      {"birth_date"=>"1957-09-16", "emp_no"=>452020,...
      {"birth_date"=>"1963-07-14", "emp_no"=>16998, ...
      {"birth_date"=>"1964-04-30", "emp_no"=>225407,...
      ...


##### Command #####

.help                           displays this message
.quit | .exit                   exits ddbcli
.consistent      (true|false)?  displays ConsistentRead parameter or changes it
.iteratable      (true|false)?  displays iteratable option or changes it
                                all results are displayed if true
.debug           (true|false)?  displays a debug status or changes it
.retry           NUM?           displays number of times of a retry or changes it
.retry_interval  SECOND?        displays a retry interval second or changes it
.timeout         SECOND?        displays a timeout second or changes it
.version                        displays a version

EOS

  Tempfile.open("ddbcli.#{$$}.#{Time.now.to_i}") do |f|
    f.puts(doc)
    f.flush

    unless system("less #{f.path}")
      puts doc
    end
  end
end


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/ddbcli/cli/functions.rb', line 20

def print_json(data, opts = {})
  str = nil
  last_evaluated_key = nil

  if data.kind_of?(DynamoDB::Iteratorable)
    last_evaluated_key = data.last_evaluated_key
    data = data.data
  end

  if data.kind_of?(Array) and opts[:inline]
    str = "[\n"

    data.each_with_index do |item, i|
      str << "  #{item.to_json}"
      str << ',' if i < (data.length - 1)
      str << "\n"
    end

    str << "]"
  else
    if data.kind_of?(Array) or data.kind_of?(Hash)
      str = JSON.pretty_generate(data)
    else
      str = data.to_json
    end
  end

  str.sub!(/(?:\r\n|\r|\n)*\Z/, "\n")

  if opts[:show_rows]
   if [Array, Hash].any? {|i| data.kind_of?(i) }
      str << "// #{data.length} #{data.length > 1 ? 'rows' : 'row'} in set"
    else
      str << '// 1 row in set'
    end

    str << " (%.2f sec)" % opts[:time] if opts[:time]
    str << "\n"
  end

  if last_evaluated_key
    str << "// has more\n"
  end

  str << "\n"
  puts str
end


8
9
10
11
12
13
14
# File 'lib/ddbcli/cli/functions.rb', line 8

def print_rownum(data, opts = {})
  rownum = data.to_i
  msg = "// #{rownum} #{rownum > 1 ? 'rows' : 'row'} changed"
  msg << " (%.2f sec)" % opts[:time] if opts[:time]
  msg << "\n\n"
  puts msg
end


16
17
18
# File 'lib/ddbcli/cli/functions.rb', line 16

def print_version
  puts "#{File.basename($0)} #{Version}"
end