├── .gitignore ├── .rspec ├── .travis.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin └── ddbcli ├── ddbcli.gemspec ├── etc ├── ddbcli-demo.gif └── pack_to_one_script.rb ├── lib ├── ddbcli.rb └── ddbcli │ ├── cli │ ├── evaluate.rb │ ├── functions.rb │ ├── help.rb │ └── options.rb │ ├── ddb-binary.rb │ ├── ddb-client.rb │ ├── ddb-driver.rb │ ├── ddb-endpoint.rb │ ├── ddb-error.rb │ ├── ddb-iteratorable.rb │ ├── ddb-parser.tab.rb │ ├── ddb-parser.y │ └── version.rb └── spec ├── ddbcli_spec.rb ├── delete_spec.rb ├── insert_spec.rb ├── select_spec.rb ├── spec_helper.rb └── update_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | .DS_Store 19 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | --colour 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.4.5 4 | - 2.5.3 5 | - 2.6.0 6 | install: 7 | - mkdir /tmp/dynamodb 8 | - wget -O - https://s3-us-west-2.amazonaws.com/dynamodb-local/dynamodb_local_latest.tar.gz | tar xz --directory /tmp/dynamodb 9 | before_script: 10 | - java -Djava.library.path=/tmp/dynamodb/DynamoDBLocal_lib -jar /tmp/dynamodb/DynamoDBLocal.jar -inMemory & 11 | script: 12 | - bundle install 13 | - bundle exec rake 14 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in ddbcli.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Genki Sugawara 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ddbcli 2 | 3 | ddbcli is an interactive command-line client of Amazon DynamoDB. 4 | 5 | [![Gem Version](https://badge.fury.io/rb/ddbcli.svg)](http://badge.fury.io/rb/ddbcli) 6 | [![Build Status](https://travis-ci.org/winebarrel/ddbcli.svg?branch=master)](https://travis-ci.org/winebarrel/ddbcli) 7 | 8 | ## Installation 9 | 10 | $ gem install ddbcli 11 | 12 | If you are not using RubyGems, you can use the script file that depend on only Ruby. 13 | 14 | see https://github.com/winebarrel/ddbcli/releases 15 | 16 | ```sh 17 | wget https://github.com/winebarrel/ddbcli/releases/download/x.x.x/ddbcli-x.x.x.gz 18 | gunzip -c ddbcli-0.x.x.gz > ddbcli 19 | chmod 755 ddbcli 20 | ``` 21 | 22 | ## Usage 23 | 24 | ```sh 25 | export AWS_ACCESS_KEY_ID='...' 26 | export AWS_SECRET_ACCESS_KEY='...' 27 | export AWS_REGION=ap-northeast-1 28 | 29 | ddbcli -e 'show tables' 30 | # [ 31 | # "employees" 32 | # ] 33 | 34 | ddbcli # show prompt 35 | ``` 36 | 37 | ### Use DynamoDB Local 38 | 39 | $ ddbcli --url localhost:8000 40 | 41 | ## Demo 42 | 43 | ![ddbcli demo](https://raw.githubusercontent.com/winebarrel/ddbcli/master/etc/ddbcli-demo.gif) 44 | 45 | ## Use Global Secondary Indexes 46 | 47 | * [https://gist.github.com/winebarrel/7938971](https://gist.github.com/winebarrel/7938971) 48 | 49 | ## Use QueryFilter 50 | 51 | * [https://gist.github.com/winebarrel/cdfc59ff6188b1e49027](https://gist.github.com/winebarrel/cdfc59ff6188b1e49027) 52 | 53 | ## Enable ctrl-r (reverse-search-history) on OS X 54 | 55 | $ echo 'bind "^R" em-inc-search-prev' >> ~/.editrc 56 | 57 | ## Help 58 | 59 | ```sql 60 | ##### Query ##### 61 | 62 | SHOW TABLES [LIMIT num] [LIKE '...'] 63 | display a table list 64 | 65 | SHOW TABLE STATUS [LIKE '...'] 66 | display table statues 67 | 68 | SHOW REGIONS 69 | display a region list 70 | 71 | SHOW CREATE TABLE table_name 72 | display a CREATE TABLE statement 73 | 74 | CREATE TABLE table_name ( 75 | key_name {STRING|NUMBER|BINARY} HASH 76 | [, key_name {STRING|NUMBER|BINARY} RANGE] 77 | [, INDEX index1_name (attr1 {STRING|NUMBER|BINARY}) {ALL|KEYS_ONLY|INCLUDE (attr, ...)} 78 | , INDEX index2_name (attr2 {STRING|NUMBER|BINARY}) {ALL|KEYS_ONLY|INCLUDE (attr, ...)} 79 | , ...] 80 | [, GLOBAL INDEX index1_name (hash_attr1 {STRING|NUMBER|BINARY} [, range_attr1 {STRING|NUMBER|BINARY}]) {ALL|KEYS_ONLY|INCLUDE (attr, ...)} [READ = num WRITE = num] 81 | , GLOBAL INDEX index2_name (hash_attr2 {STRING|NUMBER|BINARY} [, range_attr2 {STRING|NUMBER|BINARY}]) {ALL|KEYS_ONLY|INCLUDE (attr, ...)} [READ = num WRITE = num] 82 | , ...] 83 | ) READ = num WRITE = num [STREAM = {true|false|NEW_IMAGE|OLD_IMAGE|NEW_AND_OLD_IMAGES|KEYS_ONLY}] 84 | create a table 85 | 86 | CREATE TABLE table_name LIKE another_table_name [READ = num WRITE = num] [STREAM = {true|false|NEW_IMAGE|OLD_IMAGE|NEW_AND_OLD_IMAGES|KEYS_ONLY}] 87 | create a table like another table 88 | 89 | DROP TABLE table_name [, table_name2, ...] 90 | delete tables 91 | 92 | ALTER TABLE table_name {READ = num WRITE = num|STREAM = {true|false|NEW_IMAGE|OLD_IMAGE|NEW_AND_OLD_IMAGES|KEYS_ONLY}} 93 | update the provisioned throughput 94 | 95 | ALTER TABLE table_name CHANGE GLOBAL INDEX index_name READ = num WRITE = num 96 | update GSI provisioned throughput 97 | 98 | ALTER TABLE table_name ADD GLOBAL INDEX index_name (hash_attr1 {STRING|NUMBER|BINARY} [, range_attr1 {STRING|NUMBER|BINARY}]) {ALL|KEYS_ONLY|INCLUDE (attr, ...)} READ = num WRITE = num 99 | add GSI 100 | 101 | ALTER TABLE table_name DROP GLOBAL INDEX index_name 102 | delete GSI 103 | 104 | GET {*|attr1,attr2,...} FROM table_name WHERE key1 = '...' AND ... 105 | get items 106 | 107 | INSERT INTO table_name (attr1, attr2, ...) VALUES ('val1', 'val2', ...), ('val3', 'val4', ...), ... 108 | INSERT INTO table_name SELECT ... 109 | INSERT INTO table_name SELECT ALL ... 110 | create items 111 | 112 | UPDATE table_name {SET|ADD} attr1 = 'val1', ... WHERE key1 = '...' AND ... 113 | UPDATE ALL table_name {SET|ADD} attr1 = 'val1', ... [WHERE attr1 = '...' AND ...] [LIMIT limit] 114 | update items 115 | ("UPDATE" can update only one record. Please use "UPDATE ALL", when you update more than one.) 116 | 117 | UPDATE table_name DEL[ETE] attr1, ... WHERE key1 = '...' AND ... 118 | UPDATE ALL table_name DEL[ETE] attr1, ... [WHERE attr1 = '...' AND ...] [LIMIT limit] 119 | update items (delete attribute) 120 | 121 | DELETE FROM table_name WHERE key1 = '...' AND .. 122 | DELETE ALL FROM table_name WHERE [WHERE attr1 = '...' AND ...] [ORDER {ASC|DESC}] [LIMIT limit] 123 | delete items 124 | ("DELETE" can delete only one record. Please use "DELETE ALL", when you update more than one.) 125 | 126 | SELECT {*|attr1,attr2,...|COUNT(*)} FROM table_name [USE INDEX (index_name)] [WHERE key1 = '...' AND ...] [HAVING attr1 = '...' AND ...] [ORDER {ASC|DESC}] [LIMIT limit] 127 | SELECT ALL {*|attr1,attr2,...|COUNT(*)} FROM table_name [USE INDEX (index_name)] [WHERE attr1 = '...' AND ...] [LIMIT limit] 128 | SELECT segment/total_segments {*|attr1,attr2,...|COUNT(*)} FROM table_name [USE INDEX (index_name)] [WHERE attr1 = '...' AND ...] [LIMIT limit] 129 | query using the Query/Scan action 130 | see http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/QueryAndScan.html 131 | 132 | DESC[RIBE] table_name 133 | display information about the table 134 | 135 | USE region_or_endpoint 136 | change an endpoint 137 | 138 | NEXT 139 | display a continuation of a result 140 | (NEXT statement is published after SELECT statement) 141 | 142 | 143 | ##### Type ##### 144 | 145 | String 146 | 'London Bridge is...', "is falling down..." ... 147 | 148 | Number 149 | 10, 100, 0.3 ... 150 | 151 | Binary 152 | x'123456789abcd...', x"123456789abcd..." ... 153 | 154 | Identifier 155 | `ABCD...` or Non-keywords 156 | 157 | Set 158 | ('String', 'String', ...), (1, 2, 3, ...) 159 | 160 | List 161 | ['String', (1, 2, 3), {foo: 'FOO', bar: 'BAR'}, ...] 162 | 163 | Map 164 | {key1:'String', "key2":(1, 2, 3), key3: ['FOO', 'BAR'], ...} 165 | 166 | Bool 167 | true, false 168 | 169 | Null 170 | null 171 | 172 | 173 | ##### Operator ##### 174 | 175 | Query (SELECT) 176 | = | <= | < | >= | > | BEGINS_WITH | BETWEEN 177 | see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-KeyConditions 178 | 179 | Scan (SELECT ALL), QueryFilter (HAVING) 180 | = | <> | != | <= | < | >= | > | IS NOT NULL | IS NULL | CONTAINS | NOT CONTAINS | BEGINS_WITH | IN | BETWEEN 181 | see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html#DDB-Scan-request-ScanFilter, 182 | http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-QueryFilter 183 | 184 | 185 | ##### Pass to Ruby/Shell ##### 186 | 187 | Ryby 188 | query | ruby_script 189 | 190 | ex) SELECT ALL * FROM employees WHERE gender = 'M' | map {|i| Time.parse(i["birth_date"]) }; 191 | [ 192 | "1957-09-16 00:00:00 +0900", 193 | "1954-12-16 00:00:00 +0900", 194 | "1964-05-23 00:00:00 +0900", 195 | ... 196 | 197 | Shell 198 | query ! shell_command 199 | 200 | ex) SELECT ALL * FROM employees LIMIT 10 ! sort; 201 | {"birth_date"=>"1957-09-16", "emp_no"=>452020,... 202 | {"birth_date"=>"1963-07-14", "emp_no"=>16998, ... 203 | {"birth_date"=>"1964-04-30", "emp_no"=>225407,... 204 | ... 205 | 206 | 207 | ##### Output to a file ##### 208 | 209 | Overwrite 210 | SELECT ALL * FROM employees > 'foo.json'; 211 | 212 | Append 213 | SELECT ALL * FROM employees >> 'foo.json'; 214 | 215 | 216 | ##### Command ##### 217 | 218 | .help display this message 219 | .quit | .exit exit ddbcli 220 | .consistent (true|false)? display ConsistentRead parameter or changes it 221 | .iteratable (true|false)? display iteratable option or changes it 222 | all results are displayed if true 223 | .debug (true|false)? display a debug status or changes it 224 | .retry NUM? display number of times of a retry or changes it 225 | .retry_interval SECOND? display a retry interval second or changes it 226 | .timeout SECOND? display a timeout second or changes it 227 | .version display a version 228 | ``` 229 | 230 | # Test 231 | 232 | ```sh 233 | # see https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html 234 | java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar & 235 | bundle install 236 | bundle exec rake 237 | ``` 238 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rspec/core/rake_task' 3 | 4 | RSpec::Core::RakeTask.new('spec') 5 | 6 | task :default => :spec 7 | 8 | task :build => :pack_scripts 9 | 10 | task :pack_scripts do 11 | system(File.dirname(__FILE__) + '/etc/pack_to_one_script.rb') 12 | end 13 | -------------------------------------------------------------------------------- /bin/ddbcli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib') 3 | 4 | HISTORY_FILE = File.join((ENV['HOME'] || ENV['USERPROFILE'] || '.'), '.ddbcli_history') 5 | HISTSIZE = 500 6 | 7 | require 'rubygems' 8 | require 'ddbcli' 9 | require 'readline' 10 | 11 | Version = DynamoDB::VERSION 12 | 13 | options = parse_options 14 | 15 | driver = DynamoDB::Driver.new( 16 | options.access_key_id, 17 | options.secret_access_key, 18 | options.ddb_endpoint_or_region) 19 | 20 | driver.timeout = options.timeout 21 | driver.consistent = !!options.consistent 22 | driver.iteratable = !!options.iteratable 23 | driver.retry_num = options.retry_num 24 | driver.retry_intvl = options.retry_intvl 25 | driver.debug = options.debug 26 | 27 | if options.import 28 | # import mode 29 | table, file = options.import.values_at(:table, :file) 30 | items = open(file) {|f| JSON.load(f) } 31 | n = driver.import(table, items) 32 | print_rownum(n) 33 | elsif not $stdin.tty? or options.command 34 | 35 | # run mode 36 | src = options.command || $stdin.read.strip 37 | 38 | # complements separator 39 | unless src =~ /\s*(?:;|\\G)\s*\Z/i 40 | src << ';' 41 | end 42 | 43 | begin 44 | evaluate_query(driver, src, :strip => true) 45 | rescue => e 46 | print_error(e.message) 47 | print_error(e.backtrace) if driver.debug 48 | exit 1 49 | end 50 | 51 | else 52 | 53 | # load history file 54 | if File.exist?(HISTORY_FILE) 55 | open(HISTORY_FILE) do |f| 56 | f.each_line do |line| 57 | line = line.strip 58 | Readline::HISTORY.push(line) unless line.empty? 59 | end 60 | end 61 | end 62 | 63 | # interactive mode 64 | Readline.completion_proc = lambda do |word| 65 | complete_words = DynamoDB::Parser::KEYWORDS.grep(/\A#{Regexp.quote word}/i) 66 | 67 | if word.gsub(/[^a-z]+/i, '') =~ /[a-z]+/ 68 | complete_words = complete_words.map {|i| i.downcase } 69 | end 70 | 71 | complete_words 72 | end 73 | 74 | src = '' 75 | prompt1 = lambda { "#{driver.region || 'unknown'}> " } 76 | prompt2 = lambda { "#{' ' * (prompt1.call.length - 3)}-> " } 77 | 78 | while buf = Readline.readline((src.empty? ? prompt1.call : prompt2.call), true) 79 | # ignore blank lines 80 | if /\A\s*\Z/ =~ buf 81 | Readline::HISTORY.pop 82 | next 83 | end 84 | 85 | if src.empty? and buf =~ /\A\.(.+)/ 86 | evaluate_command(driver, $1) 87 | else 88 | begin 89 | src << (src.empty? ? buf : ("\n" + buf)) 90 | evaluate_query(driver, src, :show_rows => true) 91 | rescue => e 92 | print_error(e.message) 93 | print_error(e.backtrace) if driver.debug 94 | end 95 | 96 | prompt = src.empty? ? prompt1.call : prompt2.call 97 | end 98 | end # end of while 99 | 100 | # save history file 101 | unless Readline::HISTORY.empty? 102 | open(HISTORY_FILE, 'wb') do |f| 103 | (Readline::HISTORY.to_a.slice(-(Readline::HISTORY.length < HISTSIZE ? Readline::HISTORY.length : HISTSIZE)..-1) || []).each do |line| 104 | next if /\A\s*\Z/ =~ line 105 | f.puts line 106 | end 107 | end 108 | end 109 | 110 | end 111 | -------------------------------------------------------------------------------- /ddbcli.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'ddbcli/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'ddbcli' 8 | spec.version = DynamoDB::VERSION 9 | spec.authors = 'Genki Sugawara' 10 | spec.email = 'sgwr_dts@yahoo.co.jp' 11 | spec.description = 'ddbcli is an interactive command-line client of Amazon DynamoDB.' 12 | spec.summary = spec.description 13 | spec.homepage = 'https://github.com/winebarrel/ddbcli' 14 | spec.license = 'MIT' 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ['lib'] 20 | 21 | spec.add_development_dependency 'bundler' 22 | spec.add_development_dependency 'rake' 23 | spec.add_development_dependency 'rspec', '>= 3.0.0' 24 | spec.add_development_dependency 'racc' 25 | end 26 | -------------------------------------------------------------------------------- /etc/ddbcli-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winebarrel/ddbcli/e0bbe92b50a5692c93f2d27f5131cbb344e1db94/etc/ddbcli-demo.gif -------------------------------------------------------------------------------- /etc/pack_to_one_script.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'fileutils' 4 | require 'pathname' 5 | require 'stringio' 6 | 7 | ROOT_DIR = Pathname.new(__FILE__).dirname.join('..').expand_path 8 | LIB_DIR = ROOT_DIR.join('lib') 9 | 10 | require LIB_DIR.join('ddbcli/version') 11 | 12 | DEST_FILE = "pkg/ddbcli-#{DynamoDB::VERSION}" 13 | 14 | def recursive_print(file, prefix, lib_path, fout, buf = []) 15 | return if buf.include?(file) 16 | 17 | buf << file 18 | path = lib_path.join(file) 19 | 20 | path.read.split("\n").each do |line| 21 | if line =~ %r|\A\s*require\s+['"](#{prefix}/.+)['"]\s*\Z| 22 | recursive_print($1 + '.rb', prefix, lib_path, fout, buf) 23 | else 24 | fout.puts line 25 | end 26 | end 27 | end 28 | 29 | def read_bin_file 30 | ROOT_DIR.join('bin/ddbcli').read.split("\n").select {|line| 31 | [ 32 | /\A\s*#!/, 33 | /\A\s*\$LOAD_PATH/, 34 | /\A\s*require\s+['"]ddbcli\b/, 35 | ].all? {|r| line !~ r } 36 | }.join("\n") 37 | end 38 | 39 | ddbcli_buf = StringIO.new 40 | recursive_print('ddbcli.rb', 'ddbcli', LIB_DIR, ddbcli_buf) 41 | 42 | FileUtils.mkdir_p(ROOT_DIR.join(DEST_FILE).dirname) 43 | 44 | ROOT_DIR.join(DEST_FILE).open('wb', 0755) do |f| 45 | f.puts '#!/usr/bin/env ruby' 46 | f.puts ddbcli_buf.string 47 | f.puts read_bin_file 48 | end 49 | 50 | puts "pack to #{DEST_FILE}." 51 | -------------------------------------------------------------------------------- /lib/ddbcli.rb: -------------------------------------------------------------------------------- 1 | require 'ddbcli/version' 2 | 3 | require 'ddbcli/ddb-binary' 4 | require 'ddbcli/ddb-client' 5 | require 'ddbcli/ddb-driver' 6 | require 'ddbcli/ddb-endpoint' 7 | require 'ddbcli/ddb-error' 8 | require 'ddbcli/ddb-iteratorable' 9 | 10 | # CLI 11 | require 'ddbcli/cli/functions' 12 | require 'ddbcli/cli/evaluate' 13 | require 'ddbcli/cli/help' 14 | require 'ddbcli/cli/options' 15 | -------------------------------------------------------------------------------- /lib/ddbcli/cli/evaluate.rb: -------------------------------------------------------------------------------- 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, opts.merge(:inline => (tok != '\G'))) 34 | elapsed = Time.now - start_time 35 | 36 | if out.kind_of?(DynamoDB::Driver::Rownum) 37 | print_rownum(out, opts.merge(: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, $stdout, 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 52 | -------------------------------------------------------------------------------- /lib/ddbcli/cli/functions.rb: -------------------------------------------------------------------------------- 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 | msg << "\n" 13 | msg << "\n" unless opts[:strip] 14 | puts msg 15 | end 16 | 17 | def print_version 18 | puts "#{File.basename($0)} #{Version}" 19 | end 20 | 21 | def print_json(data, out, opts = {}) 22 | str = nil 23 | last_evaluated_key = nil 24 | 25 | if data.kind_of?(DynamoDB::Iteratorable) 26 | last_evaluated_key = data.last_evaluated_key 27 | data = data.data 28 | end 29 | 30 | if data.kind_of?(Array) and opts[:inline] 31 | str = "[\n" 32 | 33 | data.each_with_index do |item, i| 34 | str << " #{item.to_json}" 35 | str << ',' if i < (data.length - 1) 36 | str << "\n" 37 | end 38 | 39 | str << "]" 40 | else 41 | if data.kind_of?(Array) or data.kind_of?(Hash) 42 | str = JSON.pretty_generate(data) 43 | else 44 | str = data.to_json 45 | end 46 | end 47 | 48 | str.sub!(/(?:\r\n|\r|\n)*\Z/, "\n") 49 | 50 | if opts[:show_rows] 51 | if [Array, Hash].any? {|i| data.kind_of?(i) } 52 | str << "// #{data.length} #{data.length > 1 ? 'rows' : 'row'} in set" 53 | else 54 | str << '// 1 row in set' 55 | end 56 | 57 | str << " (%.2f sec)" % opts[:time] if opts[:time] 58 | str << "\n" 59 | end 60 | 61 | if last_evaluated_key 62 | str << "// has more\n" 63 | end 64 | 65 | str << "\n" unless opts[:strip] 66 | out.puts(str) 67 | end 68 | 69 | def evaluate_command(driver, cmd_arg) 70 | cmd, arg = cmd_arg.split(/\s+/, 2).map {|i| i.strip } 71 | arg = nil if (arg || '').strip.empty? 72 | 73 | r = /\A#{Regexp.compile(cmd)}/i 74 | 75 | commands = { 76 | 'help' => lambda { 77 | print_help(:pagerize => true) 78 | }, 79 | 80 | ['exit', 'quit'] => lambda { 81 | exit 0 82 | }, 83 | 84 | 'timeout' => lambda { 85 | case arg 86 | when nil 87 | puts driver.timeout 88 | when /\d+/ 89 | driver.timeout = arg.to_i 90 | else 91 | print_error('Invalid argument') 92 | end 93 | }, 94 | 95 | 'consistent' => lambda { 96 | if arg 97 | r_arg = /\A#{Regexp.compile(arg)}/i 98 | 99 | if r_arg =~ 'true' 100 | driver.consistent = true 101 | elsif r_arg =~ 'false' 102 | driver.consistent = false 103 | else 104 | print_error('Invalid argument') 105 | end 106 | else 107 | puts driver.consistent 108 | end 109 | }, 110 | 111 | 'iteratable' => lambda { 112 | if arg 113 | r_arg = /\A#{Regexp.compile(arg)}/i 114 | 115 | if r_arg =~ 'true' 116 | driver.iteratable = true 117 | elsif r_arg =~ 'false' 118 | driver.iteratable = false 119 | else 120 | print_error('Invalid argument') 121 | end 122 | else 123 | puts driver.iteratable 124 | end 125 | }, 126 | 127 | 'retry' => lambda { 128 | case arg 129 | when nil 130 | puts driver.retry_num 131 | when /\d+/ 132 | driver.retry_num = arg.to_i 133 | else 134 | print_error('Invalid argument') 135 | end 136 | }, 137 | 138 | 'retry_interval' => lambda { 139 | case arg 140 | when nil 141 | puts driver.retry_intvl 142 | when /\d+/ 143 | driver.retry_intvl = arg.to_i 144 | else 145 | print_error('Invalid argument') 146 | end 147 | }, 148 | 149 | 'debug' => lambda { 150 | if arg 151 | r_arg = /\A#{Regexp.compile(arg)}/i 152 | 153 | if r_arg =~ 'true' 154 | driver.debug = true 155 | elsif r_arg =~ 'false' 156 | driver.debug = false 157 | else 158 | print_error('Invalid argument') 159 | end 160 | else 161 | puts driver.debug 162 | end 163 | }, 164 | 165 | 'version' => lambda { 166 | print_version 167 | } 168 | } 169 | 170 | cmd_name, cmd_proc = commands.find do |name, proc| 171 | if name.kind_of?(Array) 172 | name.any? {|i| r =~ i } 173 | else 174 | r =~ name 175 | end 176 | end 177 | 178 | if cmd_proc 179 | cmd_proc.call 180 | else 181 | print_error('Unknown command') 182 | end 183 | end 184 | -------------------------------------------------------------------------------- /lib/ddbcli/cli/help.rb: -------------------------------------------------------------------------------- 1 | require 'tempfile' 2 | 3 | def print_help(options = {}) 4 | doc =<= | > | BEGINS_WITH | BETWEEN 122 | see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-KeyConditions 123 | 124 | Scan (SELECT ALL), QueryFilter (HAVING) 125 | = | <> | != | <= | < | >= | > | IS NOT NULL | IS NULL | CONTAINS | NOT CONTAINS | BEGINS_WITH | IN | BETWEEN 126 | see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html#DDB-Scan-request-ScanFilter, 127 | http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-QueryFilter 128 | 129 | 130 | ##### Pass to Ruby/Shell ##### 131 | 132 | Ryby 133 | query | ruby_script 134 | 135 | ex) SELECT ALL * FROM employees WHERE gender = 'M' | map {|i| Time.parse(i["birth_date"]) }; 136 | [ 137 | "1957-09-16 00:00:00 +0900", 138 | "1954-12-16 00:00:00 +0900", 139 | "1964-05-23 00:00:00 +0900", 140 | ... 141 | 142 | Shell 143 | query ! shell_command 144 | 145 | ex) SELECT ALL * FROM employees LIMIT 10 ! sort; 146 | {"birth_date"=>"1957-09-16", "emp_no"=>452020,... 147 | {"birth_date"=>"1963-07-14", "emp_no"=>16998, ... 148 | {"birth_date"=>"1964-04-30", "emp_no"=>225407,... 149 | ... 150 | 151 | 152 | ##### Output to a file ##### 153 | 154 | Overwrite 155 | SELECT ALL * FROM employees > 'foo.json'; 156 | 157 | Append 158 | SELECT ALL * FROM employees >> 'foo.json'; 159 | 160 | 161 | ##### Command ##### 162 | 163 | .help display this message 164 | .quit | .exit exit ddbcli 165 | .consistent (true|false)? display ConsistentRead parameter or changes it 166 | .iteratable (true|false)? display iteratable option or changes it 167 | all results are displayed if true 168 | .debug (true|false)? display a debug status or changes it 169 | .retry NUM? display number of times of a retry or changes it 170 | .retry_interval SECOND? display a retry interval second or changes it 171 | .timeout SECOND? display a timeout second or changes it 172 | .version display a version 173 | 174 | EOS 175 | 176 | if options[:pagerize] 177 | Tempfile.open("ddbcli.#{$$}.#{Time.now.to_i}") do |f| 178 | f.puts(doc) 179 | f.flush 180 | 181 | unless system("less #{f.path}") 182 | puts doc 183 | end 184 | end 185 | else 186 | puts doc 187 | end 188 | end 189 | -------------------------------------------------------------------------------- /lib/ddbcli/cli/options.rb: -------------------------------------------------------------------------------- 1 | require 'optparse' 2 | require 'ostruct' 3 | require 'uri' 4 | 5 | def parse_options 6 | options = OpenStruct.new 7 | options.access_key_id = ENV['AWS_ACCESS_KEY_ID'] 8 | options.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY'] 9 | options.ddb_endpoint_or_region = 10 | ENV['AWS_REGION'] || ENV['AWS_DEFAULT_REGION'] || ENV['DDB_ENDPOINT'] || ENV['DDB_REGION'] || 'dynamodb.us-east-1.amazonaws.com' 11 | 12 | # default value 13 | options.timeout = 60 14 | options.consistent = false 15 | options.iteratable = false 16 | options.retry_num = 3 17 | options.retry_intvl = 10 18 | options.debug = false 19 | 20 | ARGV.options do |opt| 21 | opt.on('-k', '--access-key=ACCESS_KEY') {|v| options.access_key_id = v } 22 | opt.on('-s', '--secret-key=SECRET_KEY') {|v| options.secret_access_key = v } 23 | opt.on('-r', '--region=REGION_OR_ENDPOINT') {|v| options.ddb_endpoint_or_region = v } 24 | 25 | url_opt = proc do |v| 26 | uri = v 27 | uri = "http://#{uri}" unless uri =~ %r|\A\w+://| 28 | uri = URI.parse(uri) 29 | raise URI::InvalidURIError, "invalid shceme: #{v}" unless /\Ahttps?\Z/ =~ uri.scheme 30 | options.ddb_endpoint_or_region = uri 31 | end 32 | 33 | opt.on('', '--url=URL', &url_opt) 34 | opt.on('', '--uri=URL (DEPRECATION)', &url_opt) 35 | 36 | opt.on('-e', '--eval=COMMAND') {|v| options.command = v } 37 | opt.on('-t', '--timeout=SECOND', Integer) {|v| options.timeout = v.to_i } 38 | 39 | opt.on('', '--import=TABLE,JSON_FILE') {|v| 40 | v = v.split(/\s*,\s*/, 2) 41 | options.import = {:table => v[0], :file => v[1]} 42 | } 43 | 44 | opt.on('', '--consistent-read') { options.consistent = true } 45 | opt.on('', '--iteratable') { options.iteratable = true } 46 | opt.on('', '--retry=NUM', Integer) {|v| options.retry_num = v.to_i } 47 | opt.on('', '--retry-interval=SECOND', Integer) {|v| options.retry_intvl = v.to_i } 48 | opt.on('', '--debug') { options.debug = true } 49 | 50 | opt.on('-h', '--help') { 51 | puts opt.help 52 | exit 53 | } 54 | 55 | opt.parse! 56 | 57 | if options.ddb_endpoint_or_region.kind_of?(URI) and not (options.access_key_id and options.secret_access_key) 58 | options.access_key_id = 'scott' 59 | options.secret_access_key = 'tiger' 60 | puts 'Warning: dummy auth key was set because ACCESS_KEY/SECRET_KEY is not set' 61 | end 62 | 63 | unless options.access_key_id and options.secret_access_key and options.ddb_endpoint_or_region 64 | puts opt.help 65 | exit 1 66 | end 67 | end 68 | 69 | options 70 | end 71 | -------------------------------------------------------------------------------- /lib/ddbcli/ddb-binary.rb: -------------------------------------------------------------------------------- 1 | require 'base64' 2 | 3 | module DynamoDB 4 | class Binary 5 | def initialize(value) 6 | @value = value 7 | end 8 | 9 | def value 10 | Base64.encode64(@value).delete("\n") 11 | end 12 | 13 | alias to_s value 14 | alias to_str value 15 | 16 | def inspect 17 | @value.inspect 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/ddbcli/ddb-client.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'openssl' 3 | require 'net/http' 4 | require 'net/https' 5 | require 'time' 6 | require 'stringio' 7 | require 'zlib' 8 | require 'uri' 9 | 10 | require 'ddbcli/ddb-error' 11 | require 'ddbcli/ddb-endpoint' 12 | 13 | module DynamoDB 14 | class Client 15 | 16 | SERVICE_NAME = 'dynamodb' 17 | API_VERSION = '2012-08-10' 18 | USER_AGENT = "ddbcli/#{DynamoDB::VERSION}" 19 | 20 | DEFAULT_TIMEOUT = 60 21 | 22 | attr_reader :region 23 | attr_accessor :timeout 24 | attr_accessor :retry_num 25 | attr_accessor :retry_intvl 26 | attr_accessor :debug 27 | 28 | def initialize(accessKeyId, secretAccessKey, endpoint_or_region) 29 | @accessKeyId = accessKeyId 30 | @secretAccessKey = secretAccessKey 31 | set_endpoint_and_region(endpoint_or_region) 32 | @timeout = DEFAULT_TIMEOUT 33 | @debug = false 34 | @retry_num = 3 35 | @retry_intvl = 10 36 | end 37 | 38 | def set_endpoint_and_region(endpoint_or_region) 39 | if endpoint_or_region.kind_of?(URI) 40 | @endpoint = endpoint_or_region 41 | 42 | aws_endpoint, region = DynamoDB::Endpoint::ENDPOINTS.find do |k, v| 43 | @endpoint.host[k] 44 | end 45 | 46 | if region 47 | @region = region 48 | else 49 | @region = [@endpoint.host, @endpoint.port].join(':') 50 | end 51 | else 52 | host, @region = DynamoDB::Endpoint.endpoint_and_region(endpoint_or_region) 53 | @endpoint = URI.parse("https://#{host}") 54 | end 55 | end 56 | 57 | def query(action, hash) 58 | retry_query do 59 | query0(action, hash) 60 | end 61 | end 62 | 63 | def query0(action, hash) 64 | if @debug 65 | $stderr.puts(< 'application/x-amz-json-1.0', 78 | 'X-Amz-Target' => "DynamoDB_#{API_VERSION.gsub('-', '')}.#{action}", 79 | 'Content-Length' => req_body.length.to_s, 80 | 'User-Agent' => USER_AGENT, 81 | 'Host' => @endpoint.host, 82 | 'X-Amz-Date' => iso8601(date), 83 | 'X-Amz-Content-Sha256' => hexhash(req_body), 84 | 'Accept' => '*/*', 85 | 'Accept-Encoding' => 'gzip', 86 | } 87 | 88 | headers['Authorization'] = authorization(date, headers, req_body) 89 | 90 | Net::HTTP.version_1_2 91 | http = Net::HTTP.new(@endpoint.host, @endpoint.port) 92 | 93 | if @endpoint.scheme == 'https' 94 | http.use_ssl = true 95 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 96 | end 97 | 98 | http.open_timeout = @timeout 99 | http.read_timeout = @timeout 100 | 101 | res_code = nil 102 | res_msg = nil 103 | 104 | res_body = http.start do |w| 105 | req = Net::HTTP::Post.new('/', headers) 106 | req.body = req_body 107 | res = w.request(req) 108 | 109 | res_code = res.code.to_i 110 | res_msg = res.message 111 | 112 | if res['Content-Encoding'] == 'gzip' 113 | StringIO.open(res.body, 'rb') do |f| 114 | Zlib::GzipReader.wrap(f).read 115 | end 116 | else 117 | res.body 118 | end 119 | end 120 | 121 | res_data = JSON.parse(res_body) 122 | 123 | if @debug 124 | $stderr.puts(< e 235 | raise e if i >= @retry_num 236 | rescue DynamoDB::Error => e 237 | if [/\bServiceUnavailable\b/i, /\bexceeded\b/i].any? {|i| i =~ e.message } 238 | raise e if i >= @retry_num 239 | else 240 | raise e 241 | end 242 | rescue Timeout::Error => e 243 | raise e if i >= @retry_num 244 | end 245 | 246 | wait_sec = @retry_intvl * (i + 1) 247 | 248 | if @debug 249 | $stderr.puts("Retry... (wait %d seconds)" % wait_sec) 250 | end 251 | 252 | sleep wait_sec 253 | end 254 | 255 | return retval 256 | end 257 | end # Client 258 | end # DynamoDB 259 | -------------------------------------------------------------------------------- /lib/ddbcli/ddb-driver.rb: -------------------------------------------------------------------------------- 1 | require 'ddbcli/ddb-client' 2 | require 'ddbcli/ddb-parser.tab' 3 | require 'ddbcli/ddb-iteratorable' 4 | 5 | require 'forwardable' 6 | require 'strscan' 7 | 8 | module DynamoDB 9 | class Driver 10 | extend Forwardable 11 | 12 | MAX_NUMBER_BATCH_PROCESS_ITEMS = 25 13 | 14 | class Rownum 15 | def initialize(rownum) 16 | @rownum = rownum 17 | end 18 | 19 | def to_i 20 | @rownum 21 | end 22 | end # Rownum 23 | 24 | def initialize(accessKeyId, secretAccessKey, endpoint_or_region) 25 | @client = DynamoDB::Client.new(accessKeyId, secretAccessKey, endpoint_or_region) 26 | @consistent = false 27 | @iteratable = false 28 | end 29 | 30 | def_delegators( 31 | :@client, 32 | :endpoint, 33 | :region, 34 | :timeout, :'timeout=', 35 | :set_endpoint_and_region, 36 | :retry_num, :'retry_num=', 37 | :retry_intvl, :'retry_intvl=', 38 | :debug, :'debug=') 39 | 40 | attr_accessor :consistent 41 | attr_accessor :iteratable 42 | 43 | def execute(query, opts = {}) 44 | parsed, script_type, script = Parser.parse(query) 45 | command = parsed.class.name.split('::').last.to_sym 46 | 47 | if command != :NEXT 48 | @last_action = nil 49 | @last_parsed = nil 50 | @last_evaluated_key = nil 51 | end 52 | 53 | retval = case command 54 | when :SHOW_TABLES 55 | do_show_tables(parsed) 56 | when :SHOW_TABLE_STATUS 57 | do_show_table_status(parsed) 58 | when :SHOW_REGIONS 59 | do_show_regions(parsed) 60 | when :SHOW_CREATE_TABLE 61 | do_show_create_table(parsed) 62 | when :ALTER_TABLE 63 | do_alter_table(parsed) 64 | when :ALTER_TABLE_INDEX 65 | do_alter_table_index(parsed) 66 | when :USE 67 | do_use(parsed) 68 | when :CREATE 69 | do_create(parsed) 70 | when :CREATE_LIKE 71 | do_create_like(parsed) 72 | when :DROP 73 | do_drop(parsed) 74 | when :DESCRIBE 75 | do_describe(parsed) 76 | when :SELECT 77 | do_select('Query', parsed) 78 | when :SCAN 79 | do_select('Scan', parsed) 80 | when :GET 81 | do_get(parsed) 82 | when :UPDATE 83 | do_update(parsed) 84 | when :UPDATE_ALL 85 | do_update_all(parsed) 86 | when :DELETE 87 | do_delete(parsed) 88 | when :DELETE_ALL 89 | do_delete_all(parsed) 90 | when :INSERT 91 | do_insert(parsed) 92 | when :INSERT_SELECT 93 | do_insert_select('Query', parsed) 94 | when :INSERT_SCAN 95 | do_insert_select('Scan', parsed) 96 | when :NEXT 97 | if @last_action and @last_parsed and @last_evaluated_key 98 | do_select(@last_action, @last_parsed, :last_evaluated_key => @last_evaluated_key) 99 | else 100 | [] 101 | end 102 | when :NULL 103 | nil 104 | else 105 | raise 'must not happen' 106 | end 107 | 108 | begin 109 | case script_type 110 | when :ruby 111 | retval = retval.data if retval.kind_of?(DynamoDB::Iteratorable) 112 | retval.instance_eval(script) 113 | when :shell 114 | retval = retval.data if retval.kind_of?(DynamoDB::Iteratorable) 115 | IO.popen(script, "r+") do |f| 116 | f.puts(retval.kind_of?(Array) ? retval.map {|i| i.to_s }.join("\n") : retval.to_s) 117 | f.close_write 118 | f.read 119 | end 120 | when :overwrite 121 | open(script, 'wb') {|f| print_json(retval, f, opts.merge(:show_rows => false, :strip => true)) } 122 | retval = nil 123 | when :append 124 | open(script, 'ab') {|f| print_json(retval, f, opts.merge(:show_rows => false, :strip => true)) } 125 | retval = nil 126 | else 127 | retval 128 | end 129 | rescue Exception => e 130 | raise DynamoDB::Error, e.message, e.backtrace 131 | end 132 | end 133 | 134 | def import(table, items) 135 | n = 0 136 | 137 | until (chunk = items.slice!(0, MAX_NUMBER_BATCH_PROCESS_ITEMS)).empty? 138 | operations = [] 139 | 140 | req_hash = { 141 | 'RequestItems' => { 142 | table => operations, 143 | }, 144 | } 145 | 146 | chunk.each do |item| 147 | h = {} 148 | 149 | operations << { 150 | 'PutRequest' => { 151 | 'Item' => h, 152 | }, 153 | } 154 | 155 | item.each do |name, val| 156 | h[name] = convert_to_attribute_value(val) 157 | end 158 | end 159 | 160 | batch_write_item(req_hash) 161 | n += chunk.length 162 | end 163 | 164 | return n 165 | end 166 | 167 | private 168 | 169 | def do_show_tables(parsed) 170 | do_show_tables0(parsed.like, parsed.limit) 171 | end 172 | 173 | def do_show_tables0(like, limit = nil) 174 | like = like ? like_to_regexp(like) : nil 175 | req_hash = {} 176 | table_names = [] 177 | 178 | req_hash['Limit'] = limit if limit 179 | 180 | list = lambda do |last_evaluated_table_name| 181 | req_hash['ExclusiveStartTableName'] = last_evaluated_table_name if last_evaluated_table_name 182 | res_data = @client.query('ListTables', req_hash) 183 | table_names.concat(res_data['TableNames']) 184 | res_data['LastEvaluatedTableName'] 185 | end 186 | 187 | letn = nil 188 | 189 | loop do 190 | letn = list.call(letn) 191 | 192 | if limit or not letn 193 | break 194 | end 195 | end 196 | 197 | return like ? table_names.select {|i| i =~ like } : table_names 198 | end 199 | 200 | def do_show_table_status(parsed) 201 | table_names = do_show_tables0(parsed.like) 202 | h = {} 203 | 204 | table_names.map do |table_name| 205 | table_info = @client.query('DescribeTable', 'TableName' => table_name)['Table'] 206 | h[table_name] = {} 207 | 208 | %w(TableStatus ItemCount TableSizeBytes).each do |i| 209 | h[table_name][i] = table_info[i] 210 | end 211 | 212 | provisioned_throughput = table_info['ProvisionedThroughput'] 213 | 214 | %w(ReadCapacityUnits WriteCapacityUnits).each do |i| 215 | h[table_name][i] = provisioned_throughput[i] 216 | end 217 | 218 | lsis = table_info.fetch('LocalSecondaryIndexes', []) 219 | 220 | unless lsis.empty? 221 | lsi_h = h[table_name]['LocalSecondaryIndexes'] = {} 222 | 223 | lsis.each do |lsi| 224 | lsi_h[lsi['IndexName']] = { 225 | 'IndexSizeBytes' => lsi['IndexSizeBytes'], 226 | 'ItemCount' => lsi['ItemCount'], 227 | } 228 | end 229 | end 230 | 231 | gsis = table_info.fetch('GlobalSecondaryIndexes', []) 232 | 233 | unless gsis.empty? 234 | gsi_h = h[table_name]['GlobalSecondaryIndexes'] = {} 235 | 236 | gsis.each do |gsi| 237 | gsi_h[gsi['IndexName']] = { 238 | 'IndexSizeBytes' => gsi['IndexSizeBytes'], 239 | 'IndexStatus' => gsi['IndexStatus'], 240 | 'ItemCount' => gsi['ItemCount'], 241 | 'ProvisionedThroughput' => { 242 | 'ReadCapacityUnits' => gsi['ProvisionedThroughput']['ReadCapacityUnits'], 243 | 'WriteCapacityUnits' => gsi['ProvisionedThroughput']['WriteCapacityUnits'], 244 | }, 245 | } 246 | end 247 | end 248 | end 249 | 250 | return h 251 | end 252 | 253 | def do_show_regions(parsed) 254 | DynamoDB::Endpoint.regions 255 | end 256 | 257 | def do_show_create_table(parsed) 258 | table_info = @client.query('DescribeTable', 'TableName' => parsed.table)['Table'] 259 | table_name = table_info['TableName'] 260 | 261 | attr_types = {} 262 | table_info['AttributeDefinitions'].each do |i| 263 | name = i['AttributeName'] 264 | attr_types[name] = { 265 | 'S' => 'STRING', 266 | 'N' => 'NUMBER', 267 | 'B' => 'BINARY', 268 | }.fetch(i['AttributeType']) 269 | end 270 | 271 | key_schema = {} 272 | table_info['KeySchema'].map do |i| 273 | name = i['AttributeName'] 274 | key_type = i['KeyType'] 275 | key_schema[name] = key_type 276 | end 277 | 278 | indexes = {} 279 | 280 | (table_info['LocalSecondaryIndexes'] || []).each do |i| 281 | index_name = i['IndexName'] 282 | key_name = i['KeySchema'].find {|j| j['KeyType'] == 'RANGE' }['AttributeName'] 283 | proj_type = i['Projection']['ProjectionType'] 284 | proj_attrs = i['Projection']['NonKeyAttributes'] 285 | indexes[index_name] = [key_name, proj_type, proj_attrs] 286 | end 287 | 288 | global_indexes = {} 289 | 290 | (table_info['GlobalSecondaryIndexes'] || []).each do |i| 291 | index_name = i['IndexName'] 292 | next unless i['KeySchema'] 293 | key_names = i['KeySchema'].map {|j| j['AttributeName'] } 294 | proj_type = i['Projection']['ProjectionType'] 295 | proj_attrs = i['Projection']['NonKeyAttributes'] 296 | 297 | idx_throughput = i['ProvisionedThroughput'] 298 | idx_throughput = { 299 | :read => idx_throughput['ReadCapacityUnits'], 300 | :write => idx_throughput['WriteCapacityUnits'], 301 | } 302 | 303 | global_indexes[index_name] = [key_names, proj_type, proj_attrs, idx_throughput] 304 | end 305 | 306 | throughput = table_info['ProvisionedThroughput'] 307 | throughput = { 308 | :read => throughput['ReadCapacityUnits'], 309 | :write => throughput['WriteCapacityUnits'], 310 | } 311 | 312 | stream = table_info['StreamSpecification'] 313 | 314 | quote = lambda {|i| '`' + i.gsub('`', '``') + '`' } # ` 315 | 316 | buf = "CREATE TABLE #{quote[table_name]} (" 317 | 318 | buf << "\n " + key_schema.map {|name, key_type| 319 | attr_type = attr_types[name] 320 | "#{quote[name]} #{attr_type} #{key_type}" 321 | }.join(",\n ") 322 | 323 | unless indexes.empty? 324 | buf << ",\n " + indexes.map {|index_name, key_name_proj| 325 | key_name, proj_type, proj_attrs = key_name_proj 326 | attr_type = attr_types[key_name] 327 | index_clause = "INDEX #{quote[index_name]} (#{quote[key_name]} #{attr_type}) #{proj_type}" 328 | index_clause << " (#{proj_attrs.join(', ')})" if proj_attrs 329 | index_clause 330 | }.join(",\n ") 331 | end 332 | 333 | unless global_indexes.empty? 334 | buf << ",\n " + global_indexes.map {|index_name, key_names_proj_itp| 335 | key_names, proj_type, proj_attrs, idx_throughput = key_names_proj_itp 336 | index_clause = "GLOBAL INDEX #{quote[index_name]} (" 337 | 338 | index_clause << key_names.map {|key_name| 339 | attr_type = attr_types[key_name] 340 | "#{quote[key_name]} #{attr_type}" 341 | }.join(', ') 342 | 343 | index_clause << ") #{proj_type}" 344 | index_clause << " (#{proj_attrs.join(', ')})" if proj_attrs 345 | index_clause << ' ' + idx_throughput.map {|k, v| "#{k}=#{v}" }.join(' ') 346 | index_clause 347 | }.join(",\n ") 348 | end 349 | 350 | buf << "\n)" 351 | buf << ' ' + throughput.map {|k, v| "#{k}=#{v}" }.join(' ') 352 | 353 | if stream and stream['StreamEnabled'] 354 | buf << " stream=#{stream['StreamViewType']}" 355 | end 356 | 357 | buf << "\n\n" 358 | 359 | return buf 360 | end 361 | 362 | def do_alter_table(parsed) 363 | req_hash = {'TableName' => parsed.table} 364 | 365 | if parsed.capacity 366 | req_hash['ProvisionedThroughput'] = { 367 | 'ReadCapacityUnits' => parsed.capacity[:read], 368 | 'WriteCapacityUnits' => parsed.capacity[:write], 369 | } 370 | end 371 | 372 | unless parsed.stream.nil? 373 | if parsed.stream 374 | view_type = (parsed.stream == true) ? 'KEYS_ONLY' : parsed.stream.to_s.upcase 375 | 376 | req_hash['StreamSpecification'] = { 377 | 'StreamEnabled' => true, 378 | 'StreamViewType' => view_type, 379 | } 380 | else 381 | req_hash['StreamSpecification'] = {'StreamEnabled' => false} 382 | end 383 | end 384 | 385 | @client.query('UpdateTable', req_hash) 386 | nil 387 | end 388 | 389 | def do_alter_table_index(parsed) 390 | req_hash = {'TableName' => parsed.table} 391 | index_definition = parsed.index_definition 392 | gsi_updates = req_hash['GlobalSecondaryIndexUpdates'] = [] 393 | 394 | case parsed.action 395 | when 'Update' 396 | gsi_updates << { 397 | 'Update' => { 398 | 'IndexName' => index_definition[:name], 399 | 'ProvisionedThroughput' => { 400 | 'ReadCapacityUnits' => index_definition[:capacity][:read], 401 | 'WriteCapacityUnits' => index_definition[:capacity][:write], 402 | }, 403 | }, 404 | } 405 | when 'Create' 406 | attr_defs = req_hash['AttributeDefinitions'] = [] 407 | 408 | gsi_updates << { 409 | 'Create' => define_index(index_definition, attr_defs, :global => true), 410 | } 411 | when 'Delete' 412 | gsi_updates << { 413 | 'Delete' => { 414 | 'IndexName' => index_definition[:name], 415 | }, 416 | } 417 | end 418 | 419 | @client.query('UpdateTable', req_hash) 420 | nil 421 | end 422 | 423 | def do_use(parsed) 424 | eor = parsed.endpoint_or_region 425 | 426 | if %r|\A\w+://| =~ eor or /:\d+\Z/ =~ eor 427 | eor = "http://#{eor}" unless eor =~ %r|\A\w+://| 428 | eor = URI.parse(eor) 429 | 430 | unless /\Ahttps?\Z/ =~ eor.scheme 431 | raise URI::InvalidURIError, "invalid shceme: #{parsed.endpoint_or_region}" 432 | end 433 | end 434 | 435 | set_endpoint_and_region(eor) 436 | nil 437 | end 438 | 439 | def do_create(parsed) 440 | req_hash = { 441 | 'TableName' => parsed.table, 442 | 'ProvisionedThroughput' => { 443 | 'ReadCapacityUnits' => parsed.capacity[:read], 444 | 'WriteCapacityUnits' => parsed.capacity[:write], 445 | }, 446 | } 447 | 448 | if parsed.stream 449 | view_type = (parsed.stream == true) ? 'KEYS_ONLY' : parsed.stream.to_s.upcase 450 | 451 | req_hash['StreamSpecification'] = { 452 | 'StreamEnabled' => true, 453 | 'StreamViewType' => view_type, 454 | } 455 | end 456 | 457 | # hash key 458 | req_hash['AttributeDefinitions'] = [ 459 | { 460 | 'AttributeName' => parsed.hash[:name], 461 | 'AttributeType' => parsed.hash[:type], 462 | } 463 | ] 464 | 465 | req_hash['KeySchema'] = [ 466 | { 467 | 'AttributeName' => parsed.hash[:name], 468 | 'KeyType' => 'HASH', 469 | } 470 | ] 471 | 472 | # range key 473 | if parsed.range 474 | req_hash['AttributeDefinitions'] << { 475 | 'AttributeName' => parsed.range[:name], 476 | 'AttributeType' => parsed.range[:type], 477 | } 478 | 479 | req_hash['KeySchema'] << { 480 | 'AttributeName' => parsed.range[:name], 481 | 'KeyType' => 'RANGE', 482 | } 483 | end 484 | 485 | # secondary index 486 | local_indices = (parsed.indices || []).select {|i| not i[:global] } 487 | global_indices = (parsed.indices || []).select {|i| i[:global] } 488 | 489 | # local secondary index 490 | unless local_indices.empty? 491 | req_hash['LocalSecondaryIndexes'] = [] 492 | 493 | local_indices.each do |idx_def| 494 | local_secondary_index = define_index(idx_def, req_hash['AttributeDefinitions'], :global => false, :hash_name => parsed.hash[:name]) 495 | req_hash['LocalSecondaryIndexes'] << local_secondary_index 496 | end 497 | end 498 | 499 | # global secondary index 500 | unless global_indices.empty? 501 | req_hash['GlobalSecondaryIndexes'] = [] 502 | 503 | global_indices.each do |idx_def| 504 | global_secondary_index = define_index(idx_def, req_hash['AttributeDefinitions'], :global => true, :capacity => parsed.capacity) 505 | req_hash['GlobalSecondaryIndexes'] << global_secondary_index 506 | end 507 | end 508 | 509 | @client.query('CreateTable', req_hash) 510 | nil 511 | end 512 | 513 | def define_attribute(attr_name, attr_type, attr_defs) 514 | same_attr = attr_defs.find {|i| i['AttributeName'] == attr_name } 515 | 516 | if same_attr 517 | if same_attr['AttributeType'] != attr_type 518 | raise DynamoDB::Error, "different types have been defined: #{attr_name}" 519 | end 520 | else 521 | attr_defs << { 522 | 'AttributeName' => attr_name, 523 | 'AttributeType' => attr_type, 524 | } 525 | end 526 | end 527 | 528 | def define_index(idx_def, attr_defs, def_idx_opts) 529 | global_idx = def_idx_opts[:global] 530 | 531 | if global_idx 532 | idx_def[:keys].each do |key_type, name_type| 533 | define_attribute(name_type[:key], name_type[:type], attr_defs) 534 | end 535 | else 536 | define_attribute(idx_def[:key], idx_def[:type], attr_defs) 537 | end 538 | 539 | secondary_index = { 540 | 'IndexName' => idx_def[:name], 541 | 'Projection' => { 542 | 'ProjectionType' => idx_def[:projection][:type], 543 | } 544 | } 545 | 546 | if global_idx 547 | secondary_index['KeySchema'] = [] 548 | 549 | [:hash, :range].each do |key_type| 550 | name_type = idx_def[:keys][key_type] 551 | 552 | if name_type 553 | secondary_index['KeySchema'] << { 554 | 'AttributeName' => name_type[:key], 555 | 'KeyType' => key_type.to_s.upcase, 556 | } 557 | end 558 | end 559 | else 560 | secondary_index['KeySchema'] = [ 561 | { 562 | 'AttributeName' => def_idx_opts.fetch(:hash_name), 563 | 'KeyType' => 'HASH', 564 | }, 565 | { 566 | 'AttributeName' => idx_def[:key], 567 | 'KeyType' => 'RANGE', 568 | }, 569 | ] 570 | end 571 | 572 | if idx_def[:projection][:attrs] 573 | secondary_index['Projection']['NonKeyAttributes'] = idx_def[:projection][:attrs] 574 | end 575 | 576 | if global_idx 577 | capacity = idx_def[:capacity] || def_idx_opts.fetch(:capacity) 578 | 579 | secondary_index['ProvisionedThroughput'] = { 580 | 'ReadCapacityUnits' => capacity[:read], 581 | 'WriteCapacityUnits' => capacity[:write], 582 | } 583 | end 584 | 585 | secondary_index 586 | end 587 | 588 | def do_create_like(parsed) 589 | table_info = @client.query('DescribeTable', 'TableName' => parsed.like)['Table'] 590 | 591 | req_hash = { 592 | 'TableName' => parsed.table, 593 | 'AttributeDefinitions' => table_info['AttributeDefinitions'], 594 | 'KeySchema' => table_info['KeySchema'], 595 | } 596 | 597 | local_secondary_indexes = (table_info['LocalSecondaryIndexes'] || []) 598 | 599 | unless local_secondary_indexes.empty? 600 | req_hash['LocalSecondaryIndexes'] = local_secondary_indexes.map do |lsi| 601 | h = {} 602 | 603 | %w(IndexName KeySchema Projection).each do |i| 604 | h[i] = lsi[i] 605 | end 606 | 607 | h 608 | end 609 | end 610 | 611 | global_secondary_indexes = (table_info['GlobalSecondaryIndexes'] || []) 612 | 613 | unless global_secondary_indexes.empty? 614 | req_hash['GlobalSecondaryIndexes'] = global_secondary_indexes.map do |gsi| 615 | h = {} 616 | 617 | %w(IndexName KeySchema Projection).each do |i| 618 | h[i] = gsi[i] 619 | end 620 | 621 | h['ProvisionedThroughput'] = h_pt = {} 622 | 623 | %w(ReadCapacityUnits WriteCapacityUnits).each do |i| 624 | h_pt[i] = gsi['ProvisionedThroughput'][i] 625 | end 626 | 627 | h 628 | end 629 | end 630 | 631 | if parsed.capacity 632 | req_hash['ProvisionedThroughput'] = { 633 | 'ReadCapacityUnits' => parsed.capacity[:read], 634 | 'WriteCapacityUnits' => parsed.capacity[:write], 635 | } 636 | else 637 | req_hash['ProvisionedThroughput'] = { 638 | 'ReadCapacityUnits' => table_info['ProvisionedThroughput']['ReadCapacityUnits'], 639 | 'WriteCapacityUnits' => table_info['ProvisionedThroughput']['WriteCapacityUnits'], 640 | } 641 | end 642 | 643 | if not parsed.stream.nil? 644 | if parsed.stream 645 | view_type = (parsed.stream == true) ? 'KEYS_ONLY' : parsed.stream.to_s.upcase 646 | 647 | req_hash['StreamSpecification'] = { 648 | 'StreamEnabled' => true, 649 | 'StreamViewType' => view_type, 650 | } 651 | else 652 | req_hash['StreamSpecification'] = {'StreamEnabled' => false} 653 | end 654 | elsif table_info['StreamSpecification'] 655 | req_hash['StreamSpecification'] = table_info['StreamSpecification'] 656 | end 657 | 658 | @client.query('CreateTable', req_hash) 659 | nil 660 | end 661 | 662 | def do_drop(parsed) 663 | parsed.tables.each do |table_name| 664 | @client.query('DeleteTable', 'TableName' => table_name) 665 | end 666 | 667 | nil 668 | end 669 | 670 | def do_describe(parsed) 671 | (@client.query('DescribeTable', 'TableName' => parsed.table) || {}).fetch('Table', {}) 672 | end 673 | 674 | def do_select(action, parsed, opts = {}) 675 | do_select0(action, parsed, opts) do |i| 676 | convert_to_ruby_value(i) 677 | end 678 | end 679 | 680 | def do_select0(action, parsed, opts = {}) 681 | select_proc = lambda do |last_evaluated_key| 682 | req_hash = {'TableName' => parsed.table} 683 | req_hash['AttributesToGet'] = parsed.attrs unless parsed.attrs.empty? 684 | req_hash['Limit'] = parsed.limit if parsed.limit 685 | req_hash['ExclusiveStartKey'] = last_evaluated_key if last_evaluated_key 686 | 687 | case action 688 | when 'Query' 689 | req_hash['ConsistentRead'] = @consistent if @consistent 690 | req_hash['IndexName'] = parsed.index if parsed.index 691 | req_hash['ScanIndexForward'] = parsed.order_asc unless parsed.order_asc.nil? 692 | when 'Scan' 693 | req_hash['Segment'] = parsed.segment if parsed.segment 694 | req_hash['TotalSegments'] = parsed.total_segments if parsed.total_segments 695 | req_hash['IndexName'] = parsed.index if parsed.index 696 | end 697 | 698 | # XXX: req_hash['ReturnConsumedCapacity'] = ... 699 | 700 | if parsed.count 701 | req_hash['Select'] = 'COUNT' 702 | elsif not parsed.attrs.empty? 703 | req_hash['Select'] = 'SPECIFIC_ATTRIBUTES' 704 | end 705 | 706 | # key conditions / scan filter 707 | if parsed.conds 708 | param_name = (action == 'Query') ? 'KeyConditions' : 'ScanFilter' 709 | req_hash[param_name] = {} 710 | 711 | parsed.conds.each do |key, operator, values| 712 | h = req_hash[param_name][key] = { 713 | 'ComparisonOperator' => operator.to_s 714 | } 715 | 716 | h['AttributeValueList'] = values.map do |val| 717 | convert_to_attribute_value(val) 718 | end 719 | end 720 | end # key conditions / scan filter 721 | 722 | # query filter 723 | if action == 'Query' and parsed.having 724 | req_hash['QueryFilter'] = {} 725 | 726 | parsed.having.each do |key, operator, values| 727 | h = req_hash['QueryFilter'][key] = { 728 | 'ComparisonOperator' => operator.to_s 729 | } 730 | 731 | h['AttributeValueList'] = values.map do |val| 732 | convert_to_attribute_value(val) 733 | end 734 | end 735 | end # query filter 736 | 737 | rd = nil 738 | 739 | begin 740 | rd = @client.query(action, req_hash) 741 | rescue DynamoDB::Error => e 742 | if action == 'Query' and e.data['__type'] == 'com.amazon.coral.service#InternalFailure' and not (e.data['message'] || e.data['Message']) 743 | table_info = (@client.query('DescribeTable', 'TableName' => parsed.table) || {}).fetch('Table', {}) rescue {} 744 | 745 | unless table_info.fetch('KeySchema', []).any? {|i| i ||= {}; i['KeyType'] == 'RANGE' } 746 | e.message << 'Query can be performed only on a table with a HASH,RANGE key schema' 747 | end 748 | end 749 | 750 | raise e 751 | end 752 | 753 | rd 754 | end 755 | 756 | res_data = select_proc.call(opts[:last_evaluated_key]) 757 | retval = nil 758 | 759 | if parsed.count 760 | retval = res_data['Count'] 761 | 762 | while res_data['LastEvaluatedKey'] 763 | res_data = select_proc.call(res_data['LastEvaluatedKey']) 764 | retval += res_data['Count'] 765 | end 766 | else 767 | retval = block_given? ? res_data['Items'].map {|i| yield(i) } : res_data['Items'] 768 | limit_orig = parsed.limit 769 | 770 | if @iteratable or opts[:iteratable] or (parsed.limit and retval.length < parsed.limit) 771 | parsed.limit -= retval.length if parsed.limit 772 | 773 | while res_data['LastEvaluatedKey'] 774 | res_data = select_proc.call(res_data['LastEvaluatedKey']) 775 | items = block_given? ? res_data['Items'].map {|i| yield(i) } : res_data['Items'] 776 | 777 | retval.concat(items) 778 | 779 | if parsed.limit 780 | parsed.limit -= items.length 781 | break if parsed.limit < 1 782 | end 783 | end 784 | end 785 | end 786 | 787 | parsed.limit = limit_orig; 788 | 789 | if res_data['LastEvaluatedKey'] 790 | @last_action = action 791 | @last_parsed = parsed 792 | @last_evaluated_key = res_data['LastEvaluatedKey'] 793 | retval = DynamoDB::Iteratorable.new(retval, res_data['LastEvaluatedKey']) 794 | else 795 | @last_action = nil 796 | @last_parsed = nil 797 | @last_evaluated_key = nil 798 | end 799 | 800 | return retval 801 | end 802 | 803 | def do_get(parsed) 804 | req_hash = {'TableName' => parsed.table} 805 | req_hash['AttributesToGet'] = parsed.attrs unless parsed.attrs.empty? 806 | req_hash['ConsistentRead'] = @consistent if @consistent 807 | 808 | # key 809 | req_hash['Key'] = {} 810 | 811 | parsed.conds.each do |key, val| 812 | req_hash['Key'][key] = convert_to_attribute_value(val) 813 | end # key 814 | 815 | convert_to_ruby_value(@client.query('GetItem', req_hash)['Item']) 816 | end 817 | 818 | def do_update(parsed) 819 | req_hash = { 820 | 'TableName' => parsed.table, 821 | } 822 | 823 | # key 824 | req_hash['Key'] = {} 825 | 826 | parsed.conds.each do |key, val| 827 | req_hash['Key'][key] = convert_to_attribute_value(val) 828 | end # key 829 | 830 | # attribute updates 831 | req_hash['AttributeUpdates'] = {} 832 | 833 | parsed.attrs.each do |attr, val| 834 | h = req_hash['AttributeUpdates'][attr] = {} 835 | h['Action'] = parsed.action.to_s.upcase 836 | 837 | if h['Action'] != 'DELETE' 838 | h['Value'] = convert_to_attribute_value(val) 839 | end 840 | end # attribute updates 841 | 842 | @client.query('UpdateItem', req_hash) 843 | 844 | Rownum.new(1) 845 | end 846 | 847 | def do_update_all(parsed) 848 | items = scan_for_update(parsed) 849 | return Rownum.new(0) if items.empty? 850 | 851 | n = items.length 852 | 853 | items.each do |key_hash| 854 | req_hash = { 855 | 'TableName' => parsed.table, 856 | } 857 | 858 | # key 859 | req_hash['Key'] = {} 860 | 861 | key_hash.each do |key, val| 862 | req_hash['Key'][key] = val 863 | end # key 864 | 865 | # attribute updates 866 | req_hash['AttributeUpdates'] = {} 867 | 868 | parsed.attrs.each do |attr, val| 869 | h = req_hash['AttributeUpdates'][attr] = {} 870 | h['Action'] = parsed.action.to_s.upcase 871 | 872 | if h['Action'] != 'DELETE' 873 | h['Value'] = convert_to_attribute_value(val) 874 | end 875 | end # attribute updates 876 | 877 | @client.query('UpdateItem', req_hash) 878 | end 879 | 880 | Rownum.new(n) 881 | end 882 | 883 | def do_delete(parsed) 884 | req_hash = { 885 | 'TableName' => parsed.table, 886 | } 887 | 888 | # key 889 | req_hash['Key'] = {} 890 | 891 | parsed.conds.each do |key, val| 892 | req_hash['Key'][key] = convert_to_attribute_value(val) 893 | end # key 894 | 895 | @client.query('DeleteItem', req_hash) 896 | 897 | Rownum.new(1) 898 | end 899 | 900 | def do_delete_all(parsed) 901 | items = scan_for_update(parsed) 902 | return Rownum.new(0) if items.empty? 903 | 904 | n = items.length 905 | 906 | until (chunk = items.slice!(0, MAX_NUMBER_BATCH_PROCESS_ITEMS)).empty? 907 | operations = [] 908 | 909 | req_hash = { 910 | 'RequestItems' => { 911 | parsed.table => operations, 912 | }, 913 | } 914 | 915 | chunk.each do |key_hash| 916 | operations << { 917 | 'DeleteRequest' => { 918 | 'Key' => key_hash, 919 | }, 920 | } 921 | end 922 | 923 | batch_write_item(req_hash) 924 | end 925 | 926 | Rownum.new(n) 927 | end 928 | 929 | def scan_for_update(parsed) 930 | # DESCRIBE 931 | key_names = @client.query('DescribeTable', 'TableName' => parsed.table)['Table']['KeySchema'] 932 | key_names = key_names.map {|h| h['AttributeName'] } 933 | 934 | items = [] 935 | 936 | # SCAN 937 | scan = lambda do |last_evaluated_key| 938 | req_hash = {'TableName' => parsed.table} 939 | req_hash['AttributesToGet'] = key_names 940 | req_hash['Limit'] = parsed.limit if parsed.limit 941 | req_hash['Select'] = 'SPECIFIC_ATTRIBUTES' 942 | req_hash['ExclusiveStartKey'] = last_evaluated_key if last_evaluated_key 943 | 944 | # XXX: req_hash['ReturnConsumedCapacity'] = ... 945 | 946 | # scan filter 947 | if parsed.conds 948 | req_hash['ScanFilter'] = {} 949 | 950 | parsed.conds.each do |key, operator, values| 951 | h = req_hash['ScanFilter'][key] = { 952 | 'ComparisonOperator' => operator.to_s 953 | } 954 | 955 | h['AttributeValueList'] = values.map do |val| 956 | convert_to_attribute_value(val) 957 | end 958 | end 959 | end # scan filter 960 | 961 | res_data = @client.query('Scan', req_hash) 962 | res_data_items = res_data['Items'] 963 | parsed.limit -= res_data_items.length if parsed.limit 964 | items.concat(res_data_items) 965 | res_data['LastEvaluatedKey'] 966 | end 967 | 968 | lek = nil 969 | 970 | loop do 971 | lek = scan.call(lek) 972 | 973 | if not lek or (parsed.limit and parsed.limit < 1) 974 | break 975 | end 976 | end 977 | 978 | return items 979 | end 980 | 981 | def convert_to_attribute_value(val) 982 | case val 983 | when Array 984 | {'L' => val.map {|i| convert_to_attribute_value(i) }} 985 | when Hash 986 | h = {} 987 | val.each {|k, v| h[k] = convert_to_attribute_value(v) } 988 | {'M' => h} 989 | when TrueClass, FalseClass 990 | {'BOOL' => val.to_s} 991 | when NilClass 992 | {'NULL' => "true"} 993 | else 994 | suffix = '' 995 | obj = val 996 | 997 | if val.kind_of?(Set) 998 | suffix = 'S' 999 | obj = val.first 1000 | val = val.map {|i| i.to_s } 1001 | else 1002 | val = val.to_s 1003 | end 1004 | 1005 | case obj 1006 | when DynamoDB::Binary 1007 | {"B#{suffix}" => val} 1008 | when String 1009 | {"S#{suffix}" => val} 1010 | when Numeric 1011 | {"N#{suffix}" => val} 1012 | else 1013 | raise 'must not happen' 1014 | end 1015 | end 1016 | end 1017 | 1018 | def convert_to_ruby_value(item) 1019 | h = {} 1020 | 1021 | (item || {}).sort_by {|a, b| a }.map do |name, val| 1022 | h[name] = convert_to_ruby_value0(val) 1023 | end 1024 | 1025 | return h 1026 | end 1027 | 1028 | def convert_to_ruby_value0(val) 1029 | val = val.map do |val_type, ddb_val| 1030 | case val_type 1031 | when 'L' 1032 | ddb_val.map {|i| convert_to_ruby_value0(i) } 1033 | when 'M' 1034 | h = {} 1035 | ddb_val.map {|k, v| h[k] = convert_to_ruby_value0(v) } 1036 | h 1037 | when 'NS' 1038 | ddb_val.map {|i| str_to_num(i) } 1039 | when 'N' 1040 | str_to_num(ddb_val) 1041 | when 'NULL' 1042 | nil 1043 | else 1044 | ddb_val 1045 | end 1046 | end 1047 | 1048 | val = val.first if val.length == 1 1049 | val 1050 | end 1051 | 1052 | def do_insert(parsed) 1053 | n = 0 1054 | 1055 | until (chunk = parsed.values.slice!(0, MAX_NUMBER_BATCH_PROCESS_ITEMS)).empty? 1056 | operations = [] 1057 | 1058 | req_hash = { 1059 | 'RequestItems' => { 1060 | parsed.table => operations, 1061 | }, 1062 | } 1063 | 1064 | chunk.each do |val_list| 1065 | h = {} 1066 | 1067 | operations << { 1068 | 'PutRequest' => { 1069 | 'Item' => h, 1070 | }, 1071 | } 1072 | 1073 | 1074 | if parsed.attrs.length != val_list.length 1075 | raise DynamoDB::Error, "number of attribute name and value are different: #{parsed.attrs.inspect} != #{val_list.inspect}" 1076 | end 1077 | 1078 | parsed.attrs.zip(val_list).each do |name, val| 1079 | h[name] = convert_to_attribute_value(val) 1080 | end 1081 | end 1082 | 1083 | batch_write_item(req_hash) 1084 | n += chunk.length 1085 | end 1086 | 1087 | Rownum.new(n) 1088 | end 1089 | 1090 | def do_insert_select(action, parsed) 1091 | if parsed.select.count 1092 | raise DynamoDB::Error, '"COUNT(*)" cannot be inserted.' 1093 | end 1094 | 1095 | items = do_select0(action, parsed.select, :iteratable => true) 1096 | items = items.data if items.kind_of?(Iteratorable) 1097 | 1098 | n = 0 1099 | 1100 | until (chunk = items.slice!(0, MAX_NUMBER_BATCH_PROCESS_ITEMS)).empty? 1101 | operations = [] 1102 | 1103 | req_hash = { 1104 | 'RequestItems' => { 1105 | parsed.table => operations, 1106 | }, 1107 | } 1108 | 1109 | chunk.each do |item| 1110 | operations << { 1111 | 'PutRequest' => {'Item' => item} 1112 | } 1113 | end 1114 | 1115 | batch_write_item(req_hash) 1116 | n += chunk.length 1117 | end 1118 | 1119 | Rownum.new(n) 1120 | end 1121 | 1122 | def str_to_num(str) 1123 | str =~ /\./ ? str.to_f : str.to_i 1124 | end 1125 | 1126 | def batch_write_item(req_hash) 1127 | res_data = @client.query('BatchWriteItem', req_hash) 1128 | 1129 | until (res_data['UnprocessedItems'] || {}).empty? 1130 | req_hash['RequestItems'] = res_data['UnprocessedItems'] 1131 | res_data = @client.query('BatchWriteItem', req_hash) 1132 | end 1133 | end 1134 | 1135 | def like_to_regexp(like) 1136 | ss = StringScanner.new(like) 1137 | tok = nil 1138 | regexp = '' 1139 | 1140 | until ss.eos? 1141 | if (tok = ss.scan /\\\\/) 1142 | regexp << '\\' 1143 | elsif (tok = ss.scan /\\%/) 1144 | regexp << '%' 1145 | elsif (tok = ss.scan /\\_/) 1146 | regexp << '_' 1147 | elsif (tok = ss.scan /%/) 1148 | regexp << '.*' 1149 | elsif (tok = ss.scan /_/) 1150 | regexp << '.' 1151 | elsif (tok = ss.scan /[^\\%_]+/) 1152 | regexp << tok 1153 | end 1154 | end 1155 | 1156 | Regexp.compile("\\A#{regexp}\\Z") 1157 | end 1158 | 1159 | end # Driver 1160 | end # DynamoDB 1161 | -------------------------------------------------------------------------------- /lib/ddbcli/ddb-endpoint.rb: -------------------------------------------------------------------------------- 1 | require 'ddbcli/ddb-error' 2 | 3 | module DynamoDB 4 | class Endpoint 5 | 6 | # http://docs.aws.amazon.com/general/latest/gr/rande.html#ddb_region 7 | ENDPOINTS = { 8 | 'dynamodb.us-east-1.amazonaws.com' => 'us-east-1', 9 | 'dynamodb.us-west-2.amazonaws.com' => 'us-west-2', 10 | 'dynamodb.us-west-1.amazonaws.com' => 'us-west-1', 11 | 'dynamodb.eu-west-1.amazonaws.com' => 'eu-west-1', 12 | 'dynamodb.eu-central-1.amazonaws.com' => 'eu-central-1', 13 | 'dynamodb.ap-southeast-1.amazonaws.com' => 'ap-southeast-1', 14 | 'dynamodb.ap-southeast-2.amazonaws.com' => 'ap-southeast-2', 15 | 'dynamodb.ap-northeast-1.amazonaws.com' => 'ap-northeast-1', 16 | 'dynamodb.ap-northeast-2.amazonaws.com' => 'ap-northeast-2', 17 | 'dynamodb.sa-east-1.amazonaws.com' => 'sa-east-1', 18 | } 19 | 20 | def self.endpoint_and_region(endpoint_or_region) 21 | if ENDPOINTS.key?(endpoint_or_region) 22 | [endpoint_or_region, ENDPOINTS[endpoint_or_region]] 23 | elsif ENDPOINTS.value?(endpoint_or_region) 24 | ep_key = ENDPOINTS.respond_to?(:key) ? ENDPOINTS.key(endpoint_or_region) : ENDPOINTS.index(endpoint_or_region) 25 | [ep_key, endpoint_or_region] 26 | else 27 | raise DynamoDB::Error, 'Unknown endpoint or region' 28 | end 29 | end 30 | 31 | def self.regions 32 | ENDPOINTS.values.dup 33 | end 34 | end # Endpoint 35 | end # DynamoDB 36 | -------------------------------------------------------------------------------- /lib/ddbcli/ddb-error.rb: -------------------------------------------------------------------------------- 1 | module DynamoDB 2 | class Error < StandardError 3 | attr_reader :data 4 | 5 | def initialize(error_message, data = {}) 6 | super(error_message) 7 | @data = data 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/ddbcli/ddb-iteratorable.rb: -------------------------------------------------------------------------------- 1 | module DynamoDB 2 | class Iteratorable 3 | attr_reader :data 4 | attr_reader :last_evaluated_key 5 | 6 | def initialize(data, last_evaluated_key) 7 | @data = data 8 | @last_evaluated_key = last_evaluated_key 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/ddbcli/ddb-parser.tab.rb: -------------------------------------------------------------------------------- 1 | # 2 | # DO NOT MODIFY!!!! 3 | # This file is automatically generated by Racc 1.4.12 4 | # from Racc grammer file "". 5 | # 6 | 7 | require 'racc/parser.rb' 8 | 9 | 10 | require 'strscan' 11 | require 'ddbcli/ddb-binary' 12 | require 'set' 13 | 14 | module DynamoDB 15 | 16 | class Parser < Racc::Parser 17 | 18 | module_eval(<<'...end ddb-parser.y/module_eval...', 'ddb-parser.y', 685) 19 | 20 | KEYWORDS = %w( 21 | ADD 22 | ALL 23 | ALTER 24 | AND 25 | ASC 26 | BEGINS_WITH 27 | BETWEEN 28 | BINARY 29 | CHANGE 30 | CREATE 31 | CONTAINS 32 | COUNT 33 | DELETE 34 | DEL 35 | DESCRIBE 36 | DESC 37 | DROP 38 | FROM 39 | GET 40 | GLOBAL 41 | HASH 42 | HAVING 43 | INCLUDE 44 | INDEX 45 | INSERT 46 | INTO 47 | IN 48 | IS 49 | KEYS_ONLY 50 | LIKE 51 | LIMIT 52 | NEW_AND_OLD_IMAGES 53 | NEW_IMAGE 54 | NEXT 55 | NOT 56 | NUMBER 57 | OLD_IMAGE 58 | ORDER 59 | RANGE 60 | READ 61 | REGIONS 62 | SELECT 63 | SET 64 | SHOW 65 | STATUS 66 | STREAM 67 | STRING 68 | TABLES 69 | TABLE 70 | UPDATE 71 | VALUES 72 | WHERE 73 | WRITE 74 | USE 75 | ) 76 | 77 | KEYWORD_REGEXP = Regexp.compile("(?:#{KEYWORDS.join '|'})\\b", Regexp::IGNORECASE) 78 | 79 | def initialize(obj) 80 | src = obj.is_a?(IO) ? obj.read : obj.to_s 81 | @ss = StringScanner.new(src) 82 | end 83 | 84 | @@structs = {} 85 | 86 | def struct(name, attrs = {}) 87 | unless (clazz = @@structs[name]) 88 | clazz = attrs.empty? ? Struct.new(name.to_s) : Struct.new(name.to_s, *attrs.keys) 89 | @@structs[name] = clazz 90 | end 91 | 92 | obj = clazz.new 93 | 94 | attrs.each do |key, val| 95 | obj.send("#{key}=", val) 96 | end 97 | 98 | return obj 99 | end 100 | private :struct 101 | 102 | def scan 103 | tok = nil 104 | @prev_tokens = [] 105 | 106 | until @ss.eos? 107 | if (tok = @ss.scan /\s+/) 108 | # nothing to do 109 | elsif (tok = @ss.scan /(?:>>|<>|!=|>=|<=|>|<|=)/) 110 | sym = { 111 | '>>' => :GTGT, 112 | '<>' => :NE, 113 | '!=' => :NE, 114 | '>=' => :GE, 115 | '<=' => :LE, 116 | '>' => :GT, 117 | '<' => :LT, 118 | '=' => :EQ, 119 | }.fetch(tok) 120 | yield [sym, tok] 121 | elsif (tok = @ss.check KEYWORD_REGEXP) and @ss.rest.slice(tok.length) !~ /\A\w/ 122 | tok = @ss.scan KEYWORD_REGEXP 123 | yield [tok.upcase.to_sym, tok] 124 | elsif (tok = @ss.check /NULL/i) and @ss.rest.slice(tok.length) !~ /\A\w/ 125 | tok = @ss.scan /NULL/i 126 | yield [:NULL, nil] 127 | elsif (tok = @ss.scan /`(?:[^`]|``)*`/) 128 | yield [:IDENTIFIER, tok.slice(1...-1).gsub(/``/, '`')] 129 | elsif (tok = @ss.scan /x'(?:[^']|'')*'/) #' 130 | hex = tok.slice(2...-1).gsub(/''/, "'") 131 | bin = DynamoDB::Binary.new([hex].pack('H*')) 132 | yield [:BINARY_VALUE, bin] 133 | elsif (tok = @ss.scan /x"(?:[^"]|"")*"/) #" 134 | hex = tok.slice(2...-1).gsub(/""/, '"') 135 | bin = DynamoDB::Binary.new([hex].pack('H*')) 136 | yield [:BINARY_VALUE, bin] 137 | elsif (tok = @ss.scan /'(?:[^']|'')*'/) #' 138 | yield [:STRING_VALUE, tok.slice(1...-1).gsub(/''/, "'")] 139 | elsif (tok = @ss.scan /"(?:[^"]|"")*"/) #" 140 | yield [:STRING_VALUE, tok.slice(1...-1).gsub(/""/, '"')] 141 | elsif (tok = @ss.scan /\d+(?:\.\d+)?/) 142 | yield [:NUMBER_VALUE, (tok =~ /\./ ? tok.to_f : tok.to_i)] 143 | elsif (tok = @ss.scan /[,\(\)\*\/\[\]\{\}:]/) 144 | yield [tok, tok] 145 | elsif (tok = @ss.scan /\|(?:.*)/) 146 | yield [:RUBY_SCRIPT, tok.slice(1..-1)] 147 | elsif (tok = @ss.scan /\|(?:.*)/) 148 | yield [:RUBY_SCRIPT, tok.slice(1..-1)] 149 | elsif (tok = @ss.scan /\!(?:.*)/) 150 | yield [:SHELL_SCRIPT, tok.slice(1..-1)] 151 | elsif (tok = @ss.scan %r|[-.0-9a-z_]*|i) 152 | if ['true', 'false'].include?(tok) 153 | yield [:BOOL, 'true' == tok] 154 | else 155 | yield [:IDENTIFIER, tok] 156 | end 157 | else 158 | raise_error(tok, @prev_tokens, @ss) 159 | end 160 | 161 | @prev_tokens << tok 162 | end 163 | 164 | yield [false, ''] 165 | end 166 | private :scan 167 | 168 | def raise_error(error_value, prev_tokens, scanner) 169 | errmsg = ["__#{error_value}__"] 170 | 171 | if prev_tokens and not prev_tokens.empty? 172 | toks = prev_tokens.reverse[0, 5].reverse 173 | toks.unshift('...') if prev_tokens.length > toks.length 174 | errmsg.unshift(toks.join.strip) 175 | end 176 | 177 | if scanner and not (rest = (scanner.rest || '').strip).empty? 178 | str = rest[0, 16] 179 | str += '...' if rest.length > str.length 180 | errmsg << str 181 | end 182 | 183 | raise Racc::ParseError, ('parse error on value: %s' % errmsg.join(' ')) 184 | end 185 | private :raise_error 186 | 187 | def parse 188 | yyparse self, :scan 189 | end 190 | 191 | def on_error(error_token_id, error_value, value_stack) 192 | raise_error(error_value, @prev_tokens, @ss) 193 | end 194 | 195 | def self.parse(obj) 196 | self.new(obj).parse 197 | end 198 | 199 | ...end ddb-parser.y/module_eval... 200 | ##### State transition tables begin ### 201 | 202 | racc_action_table = [ 203 | 197, 197, 83, 286, 57, 243, 238, 197, 238, 288, 204 | 352, 106, 83, 55, 237, 123, 237, 197, 199, 199, 205 | 324, 55, 192, 192, 212, 199, 197, 197, 120, 192, 206 | 213, 58, 55, 196, 196, 199, 27, 60, 242, 192, 207 | 196, 323, 59, 120, 199, 199, 285, 53, 192, 192, 208 | 196, 82, 84, 85, 287, 53, 139, 193, 193, 196, 209 | 196, 82, 84, 85, 193, 76, 53, 101, 149, 102, 210 | 194, 194, 195, 195, 193, 198, 198, 194, 231, 195, 211 | 244, 234, 198, 193, 193, 197, 197, 194, 148, 195, 212 | 303, 302, 198, 197, 197, 55, 194, 194, 195, 195, 213 | 106, 198, 198, 199, 199, 156, 148, 192, 192, 321, 214 | 318, 199, 199, 197, 197, 192, 192, 156, 196, 196, 215 | 52, 138, 51, 120, 173, 383, 196, 196, 50, 53, 216 | 254, 199, 199, 116, 359, 192, 192, 174, 175, 362, 217 | 72, 214, 193, 193, 295, 215, 196, 196, 361, 363, 218 | 193, 193, 250, 296, 197, 194, 194, 195, 195, 155, 219 | 198, 198, 156, 194, 194, 195, 195, 72, 198, 198, 220 | 193, 193, 199, 197, 197, 101, 192, 102, 278, 251, 221 | 252, 253, 255, 194, 194, 195, 195, 196, 198, 198, 222 | 100, 199, 199, 314, 160, 192, 192, 321, 318, 105, 223 | 3, 4, 101, 254, 102, 18, 196, 196, 104, 173, 224 | 23, 193, 97, 21, 173, 98, 99, 161, 24, 22, 225 | 100, 100, 174, 175, 194, 250, 195, 174, 175, 198, 226 | 193, 193, 101, 101, 102, 102, 101, 374, 102, 375, 227 | 25, 26, 27, 194, 194, 195, 195, 28, 198, 198, 228 | 173, 258, 251, 252, 253, 255, 257, 259, 262, 263, 229 | 264, 173, 358, 174, 175, 29, 162, 30, 362, 31, 230 | 362, 359, 32, 291, 174, 175, 394, 361, 363, 361, 231 | 363, 293, 292, 43, 101, 395, 102, 34, 35, 36, 232 | 294, 37, 44, 38, 39, 137, 40, 41, 163, 165, 233 | 166, 167, 168, 374, 169, 375, 321, 318, 170, 171, 234 | 135, 134, 180, 181, 182, 141, 184, 185, 116, 133, 235 | 132, 131, 202, 203, 206, 66, 208, 106, 209, 210, 236 | 211, 129, 100, 126, 218, 122, 100, 222, 225, 226, 237 | 227, 156, 229, 120, 72, 116, 113, 66, 66, 155, 238 | 112, 249, 111, 265, 110, 267, 268, 269, 270, 271, 239 | 109, 272, 274, 206, 276, 108, 280, 141, 66, 283, 240 | 141, 107, 106, 289, 290, 94, 91, 92, 206, 91, 241 | 199, 89, 142, 304, 306, 308, 309, 310, 311, 312, 242 | 88, 66, 87, 249, 225, 86, 79, 328, 180, 141, 243 | 156, 78, 77, 74, 73, 336, 337, 338, 339, 340, 244 | 341, 72, 344, 70, 346, 347, 348, 69, 350, 351, 245 | 141, 222, 156, 66, 68, 357, 67, 66, 364, 144, 246 | 366, 367, 368, 64, 274, 66, 63, 62, 61, 377, 247 | 378, 379, 380, 381, 66, 48, 384, 385, 387, 47, 248 | 143, 46, 308, 392, 393, 45, 348, 396, 397, 213, 249 | 215, 398, 42, 33, 230 ] 250 | 251 | racc_action_check = [ 252 | 357, 149, 86, 232, 29, 199, 288, 194, 195, 235, 253 | 327, 117, 57, 28, 288, 89, 195, 203, 357, 149, 254 | 274, 111, 357, 149, 169, 194, 256, 258, 117, 194, 255 | 169, 29, 51, 357, 149, 203, 89, 30, 199, 203, 256 | 194, 274, 30, 113, 256, 258, 232, 28, 256, 258, 257 | 203, 86, 86, 86, 235, 111, 106, 357, 149, 256, 258 | 258, 57, 57, 57, 194, 51, 51, 96, 116, 96, 259 | 357, 149, 357, 149, 203, 357, 149, 194, 194, 194, 260 | 199, 195, 194, 256, 258, 277, 359, 203, 151, 203, 261 | 259, 259, 203, 286, 289, 27, 256, 258, 256, 258, 262 | 152, 256, 258, 277, 359, 151, 114, 277, 359, 379, 263 | 379, 286, 289, 290, 306, 286, 289, 152, 277, 359, 264 | 27, 105, 27, 114, 378, 372, 286, 289, 27, 27, 265 | 225, 290, 306, 118, 372, 290, 306, 378, 378, 397, 266 | 119, 170, 277, 359, 241, 170, 290, 306, 397, 397, 267 | 286, 289, 225, 241, 339, 277, 359, 277, 359, 120, 268 | 277, 359, 122, 286, 289, 286, 289, 123, 286, 289, 269 | 290, 306, 339, 352, 278, 176, 339, 176, 225, 225, 270 | 225, 225, 225, 290, 306, 290, 306, 339, 290, 306, 271 | 138, 352, 278, 272, 129, 352, 278, 272, 272, 70, 272 | 0, 0, 138, 206, 138, 0, 352, 278, 70, 137, 273 | 0, 339, 69, 0, 380, 69, 69, 131, 0, 0, 274 | 171, 69, 137, 137, 339, 206, 339, 380, 380, 339, 275 | 352, 278, 171, 69, 171, 69, 209, 360, 209, 360, 276 | 0, 0, 0, 352, 278, 352, 278, 0, 352, 278, 277 | 314, 206, 206, 206, 206, 206, 206, 206, 206, 206, 278 | 206, 308, 342, 314, 314, 0, 132, 0, 396, 0, 279 | 344, 342, 0, 239, 308, 308, 386, 396, 396, 344, 280 | 344, 240, 239, 22, 217, 386, 217, 2, 2, 2, 281 | 240, 2, 22, 18, 18, 104, 18, 18, 133, 133, 282 | 133, 133, 133, 400, 134, 400, 348, 348, 135, 136, 283 | 102, 101, 140, 141, 142, 143, 144, 145, 148, 100, 284 | 99, 98, 153, 155, 156, 157, 158, 159, 160, 161, 285 | 162, 97, 95, 91, 172, 88, 178, 179, 180, 181, 286 | 182, 183, 184, 87, 81, 80, 79, 200, 201, 202, 287 | 78, 204, 77, 208, 76, 210, 212, 213, 214, 215, 288 | 75, 218, 221, 222, 223, 74, 226, 227, 228, 229, 289 | 230, 73, 71, 237, 238, 68, 67, 66, 249, 65, 290 | 257, 61, 108, 264, 265, 267, 268, 269, 270, 271, 291 | 60, 273, 59, 275, 276, 58, 56, 280, 281, 283, 292 | 284, 54, 52, 50, 49, 292, 294, 296, 300, 303, 293 | 305, 46, 307, 45, 309, 311, 313, 42, 318, 321, 294 | 107, 329, 330, 331, 41, 341, 39, 38, 345, 110, 295 | 349, 350, 351, 37, 353, 354, 36, 33, 31, 363, 296 | 364, 366, 367, 368, 370, 26, 374, 375, 377, 25, 297 | 109, 24, 381, 384, 385, 23, 389, 390, 391, 392, 298 | 393, 395, 21, 1, 185 ] 299 | 300 | racc_action_pointer = [ 301 | 198, 463, 285, nil, nil, nil, nil, nil, nil, nil, 302 | nil, nil, nil, nil, nil, nil, nil, nil, 285, nil, 303 | nil, 453, 270, 446, 442, 436, 432, 82, 0, -9, 304 | -3, 366, nil, 437, nil, nil, 431, 428, 362, 416, 305 | nil, 415, 404, nil, nil, 400, 398, nil, nil, 359, 306 | 380, 19, 354, nil, 368, nil, 351, -7, 382, 379, 307 | 345, 368, nil, nil, nil, 365, 339, 362, 362, 196, 308 | 185, 339, nil, 358, 318, 315, 331, 314, 337, 333, 309 | 332, 331, nil, nil, nil, nil, -17, 293, 322, -8, 310 | nil, 328, nil, nil, nil, 307, 30, 314, 304, 303, 311 | 293, 285, 284, nil, 282, 108, 43, 399, 358, 437, 312 | 382, 8, nil, -7, 73, nil, 42, -22, 120, 127, 313 | 146, nil, 112, 154, nil, nil, nil, nil, nil, 176, 314 | nil, 199, 248, 271, 266, 270, 285, 187, 165, nil, 315 | 262, 295, 269, 294, 292, 272, nil, nil, 305, -4, 316 | nil, 55, 67, 271, nil, 297, 311, 260, 302, 294, 317 | 315, 316, 317, nil, nil, nil, nil, nil, nil, -9, 318 | 108, 195, 302, nil, nil, nil, 138, nil, 311, 271, 319 | 325, 316, 327, 291, 297, 451, nil, nil, nil, nil, 320 | nil, nil, nil, nil, 2, 3, nil, nil, nil, 0, 321 | 282, 283, 336, 12, 300, nil, 199, nil, 280, 199, 322 | 332, nil, 317, 331, 321, 333, nil, 247, 328, nil, 323 | nil, 299, 350, 313, nil, 126, 353, 346, 303, 356, 324 | 349, nil, -30, nil, nil, -24, nil, 294, 295, 249, 325 | 257, 120, nil, nil, nil, nil, nil, nil, nil, 365, 326 | nil, nil, nil, nil, nil, nil, 21, 357, 22, 29, 327 | nil, nil, nil, nil, 323, 361, nil, 372, 360, 349, 328 | 362, 351, 180, 326, -23, 342, 381, 80, 169, nil, 329 | 373, 348, nil, 378, 350, nil, 88, nil, 1, 89, 330 | 108, nil, 367, nil, 401, nil, 327, nil, nil, nil, 331 | 357, nil, nil, 347, nil, 377, 109, 388, 239, 376, 332 | nil, 377, nil, 383, 228, nil, nil, nil, 405, nil, 333 | nil, 401, nil, nil, nil, nil, nil, -41, nil, 355, 334 | 372, 358, nil, nil, nil, nil, nil, nil, nil, 149, 335 | nil, 402, 238, nil, 239, 395, nil, nil, 289, 396, 336 | 408, 419, 168, 371, 370, nil, nil, -5, nil, 81, 337 | 200, nil, nil, 416, 427, nil, 408, 429, 420, nil, 338 | 379, nil, 101, nil, 420, 421, nil, 435, 102, 92, 339 | 192, 439, nil, nil, 415, 416, 252, nil, nil, 423, 340 | 433, 434, 420, 423, nil, 448, 237, 108, nil, nil, 341 | 266 ] 342 | 343 | racc_action_default = [ 344 | -185, -185, -1, -6, -7, -8, -9, -10, -11, -12, 345 | -13, -14, -15, -16, -17, -18, -19, -20, -185, -27, 346 | -28, -185, -185, -185, -185, -185, -185, -185, -185, -185, 347 | -185, -185, -154, -185, -2, -3, -185, -185, -125, -185, 348 | -23, -185, -185, -36, -37, -185, -185, -80, -81, -185, 349 | -185, -185, -185, -88, -89, -90, -185, -185, -185, -185, 350 | -185, -185, 401, -4, -5, -25, -185, -25, -185, -185, 351 | -185, -79, -183, -185, -185, -185, -185, -185, -185, -185, 352 | -185, -185, -133, -134, -135, -136, -185, -185, -185, -185, 353 | -21, -185, -126, -22, -24, -29, -30, -185, -185, -185, 354 | -185, -185, -185, -60, -185, -185, -185, -92, -185, -185, 355 | -185, -185, -91, -185, -185, -137, -185, -185, -185, -185, 356 | -185, -144, -107, -185, -147, -148, -26, -31, -32, -185, 357 | -34, -185, -185, -185, -185, -185, -185, -185, -39, -184, 358 | -94, -185, -185, -92, -185, -185, -87, -129, -185, -185, 359 | -131, -107, -107, -140, -141, -185, -185, -125, -185, -149, 360 | -185, -185, -185, -45, -46, -47, -48, -49, -50, -185, 361 | -185, -185, -185, -55, -56, -57, -40, -41, -42, -127, 362 | -185, -185, -185, -107, -185, -185, -138, -139, -155, -156, 363 | -157, -158, -159, -160, -185, -185, -171, -172, -173, -185, 364 | -125, -125, -185, -185, -108, -109, -185, -145, -185, -185, 365 | -185, -35, -185, -185, -185, -185, -38, -185, -51, -44, 366 | -43, -122, -185, -95, -96, -185, -185, -92, -125, -185, 367 | -92, -161, -185, -163, -165, -185, -167, -185, -185, -185, 368 | -185, -185, -177, -179, -181, -130, -132, -142, -143, -185, 369 | -101, -102, -103, -104, -105, -106, -185, -185, -185, -185, 370 | -115, -116, -117, -118, -185, -185, -33, -185, -185, -185, 371 | -185, -185, -185, -125, -185, -128, -185, -185, -185, -100, 372 | -185, -94, -84, -92, -107, -162, -185, -166, -185, -185, 373 | -185, -174, -185, -175, -185, -176, -185, -110, -111, -112, 374 | -185, -114, -120, -185, -119, -146, -185, -185, -185, -185, 375 | -61, -185, -62, -52, -185, -63, -65, -66, -185, -68, 376 | -69, -185, -82, -123, -124, -97, -98, -185, -93, -127, 377 | -107, -125, -164, -168, -169, -170, -178, -180, -182, -185, 378 | -121, -185, -185, -152, -185, -72, -58, -59, -185, -185, 379 | -185, -185, -185, -122, -125, -86, -113, -185, -150, -185, 380 | -185, -74, -75, -185, -185, -64, -53, -185, -185, -99, 381 | -125, -85, -185, -153, -185, -185, -71, -185, -185, -185, 382 | -185, -185, -83, -151, -185, -185, -185, -77, -73, -54, 383 | -185, -185, -185, -185, -76, -185, -185, -185, -78, -67, 384 | -70 ] 385 | 386 | racc_goto_table = [ 387 | 65, 172, 187, 157, 140, 221, 179, 307, 95, 273, 388 | 313, 96, 224, 236, 12, 11, 154, 49, 56, 71, 389 | 204, 360, 376, 114, 342, 177, 81, 260, 80, 90, 390 | 1, 93, 200, 201, 386, 128, 164, 127, 136, 121, 391 | 183, 75, 130, 20, 19, 365, 279, 233, 17, 16, 392 | 223, 15, 277, 14, 117, 119, 248, 118, 216, 13, 393 | 10, 151, 376, 297, 228, 146, 147, 256, 299, 150, 394 | 301, 261, 9, 399, 400, 372, 8, 7, 186, 153, 395 | 176, 6, 158, 305, 5, 232, 275, 235, 2, 239, 396 | 240, 241, 152, nil, nil, nil, 159, nil, 247, nil, 397 | nil, 145, nil, 125, 124, nil, 333, nil, 325, 298, 398 | nil, 300, nil, 217, nil, 219, nil, 389, nil, 207, 399 | 220, 391, nil, nil, 281, nil, nil, 284, nil, nil, 400 | 326, 327, nil, nil, nil, nil, nil, nil, nil, 332, 401 | nil, 370, 334, 335, nil, nil, nil, 329, 266, nil, 402 | nil, nil, nil, nil, nil, 353, 219, nil, nil, nil, 403 | nil, nil, 245, 246, nil, 331, nil, nil, nil, nil, 404 | nil, nil, 345, nil, nil, nil, nil, nil, 349, nil, 405 | 330, nil, nil, nil, nil, nil, nil, nil, nil, nil, 406 | 282, nil, 356, nil, nil, nil, nil, nil, nil, nil, 407 | nil, nil, nil, nil, nil, 369, nil, nil, nil, nil, 408 | nil, 354, 373, nil, nil, nil, nil, nil, nil, nil, 409 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 410 | nil, nil, nil, nil, nil, 322, nil, nil, nil, nil, 411 | nil, nil, 388, nil, 390, nil, nil, nil, nil, nil, 412 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 413 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 414 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 415 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 416 | nil, nil, nil, 355, nil, nil, nil, nil, nil, nil, 417 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 418 | nil, nil, nil, nil, nil, nil, 371, nil, nil, nil, 419 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 420 | nil, nil, 382 ] 421 | 422 | racc_goto_check = [ 423 | 16, 26, 48, 42, 38, 40, 39, 34, 20, 41, 424 | 27, 21, 46, 70, 10, 9, 61, 37, 37, 36, 425 | 50, 32, 28, 57, 64, 24, 58, 49, 56, 17, 426 | 1, 17, 42, 42, 35, 20, 25, 21, 23, 43, 427 | 38, 37, 22, 19, 18, 29, 49, 48, 15, 14, 428 | 45, 13, 47, 12, 36, 58, 48, 56, 24, 11, 429 | 8, 57, 28, 51, 42, 43, 43, 52, 53, 43, 430 | 54, 55, 7, 32, 32, 64, 6, 5, 59, 60, 431 | 21, 4, 62, 63, 3, 68, 50, 69, 2, 71, 432 | 72, 73, 36, nil, nil, nil, 36, nil, 61, nil, 433 | nil, 37, nil, 10, 9, nil, 70, nil, 46, 48, 434 | nil, 48, nil, 21, nil, 20, nil, 27, nil, 16, 435 | 21, 34, nil, nil, 38, nil, nil, 38, nil, nil, 436 | 48, 48, nil, nil, nil, nil, nil, nil, nil, 48, 437 | nil, 41, 48, 48, nil, nil, nil, 39, 20, nil, 438 | nil, nil, nil, nil, nil, 40, 20, nil, nil, nil, 439 | nil, nil, 16, 16, nil, 42, nil, nil, nil, nil, 440 | nil, nil, 26, nil, nil, nil, nil, nil, 26, nil, 441 | 38, nil, nil, nil, nil, nil, nil, nil, nil, nil, 442 | 16, nil, 48, nil, nil, nil, nil, nil, nil, nil, 443 | nil, nil, nil, nil, nil, 48, nil, nil, nil, nil, 444 | nil, 42, 48, nil, nil, nil, nil, nil, nil, nil, 445 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 446 | nil, nil, nil, nil, nil, 16, nil, nil, nil, nil, 447 | nil, nil, 26, nil, 26, nil, nil, nil, nil, nil, 448 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 449 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 450 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 451 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 452 | nil, nil, nil, 16, nil, nil, nil, nil, nil, nil, 453 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 454 | nil, nil, nil, nil, nil, nil, 16, nil, nil, nil, 455 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 456 | nil, nil, 16 ] 457 | 458 | racc_goto_pointer = [ 459 | nil, 30, 88, 84, 81, 77, 76, 72, 60, 15, 460 | 14, 59, 53, 51, 49, 48, -38, -36, 44, 43, 461 | -61, -58, -56, -66, -113, -97, -136, -262, -338, -303, 462 | nil, nil, -323, nil, -260, -343, -27, -10, -103, -134, 463 | -174, -212, -119, -48, nil, -130, -168, -173, -147, -179, 464 | -136, -186, -139, -189, -189, -135, -29, -57, -31, -70, 465 | -41, -104, -41, -182, -282, nil, nil, nil, -109, -108, 466 | -182, -110, -109, -108 ] 467 | 468 | racc_goto_default = [ 469 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 470 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 471 | 178, nil, 320, nil, nil, nil, nil, nil, 103, 315, 472 | 316, 317, nil, 319, nil, nil, nil, nil, nil, nil, 473 | nil, nil, nil, nil, 54, nil, nil, nil, 343, nil, 474 | nil, 205, nil, 189, nil, nil, nil, nil, nil, 115, 475 | nil, nil, nil, nil, nil, 188, 190, 191, nil, nil, 476 | nil, nil, nil, nil ] 477 | 478 | racc_reduce_table = [ 479 | 0, 0, :racc_error, 480 | 1, 82, :_reduce_1, 481 | 2, 82, :_reduce_2, 482 | 2, 82, :_reduce_3, 483 | 3, 82, :_reduce_4, 484 | 3, 82, :_reduce_5, 485 | 1, 82, :_reduce_6, 486 | 1, 82, :_reduce_7, 487 | 1, 83, :_reduce_none, 488 | 1, 83, :_reduce_none, 489 | 1, 83, :_reduce_none, 490 | 1, 83, :_reduce_none, 491 | 1, 83, :_reduce_none, 492 | 1, 83, :_reduce_none, 493 | 1, 83, :_reduce_none, 494 | 1, 83, :_reduce_none, 495 | 1, 83, :_reduce_none, 496 | 1, 83, :_reduce_none, 497 | 1, 83, :_reduce_none, 498 | 1, 83, :_reduce_none, 499 | 1, 83, :_reduce_none, 500 | 4, 84, :_reduce_21, 501 | 4, 84, :_reduce_22, 502 | 2, 84, :_reduce_23, 503 | 4, 84, :_reduce_24, 504 | 0, 98, :_reduce_none, 505 | 2, 98, :_reduce_26, 506 | 1, 85, :_reduce_none, 507 | 1, 85, :_reduce_none, 508 | 4, 99, :_reduce_29, 509 | 4, 99, :_reduce_30, 510 | 5, 99, :_reduce_31, 511 | 5, 99, :_reduce_32, 512 | 8, 100, :_reduce_33, 513 | 5, 100, :_reduce_34, 514 | 7, 100, :_reduce_35, 515 | 2, 86, :_reduce_36, 516 | 2, 86, :_reduce_37, 517 | 7, 87, :_reduce_38, 518 | 5, 87, :_reduce_39, 519 | 6, 87, :_reduce_40, 520 | 6, 87, :_reduce_41, 521 | 1, 105, :_reduce_42, 522 | 2, 105, :_reduce_43, 523 | 2, 105, :_reduce_44, 524 | 3, 102, :_reduce_45, 525 | 3, 102, :_reduce_46, 526 | 1, 106, :_reduce_none, 527 | 1, 106, :_reduce_none, 528 | 1, 106, :_reduce_none, 529 | 1, 106, :_reduce_none, 530 | 3, 104, :_reduce_51, 531 | 5, 104, :_reduce_52, 532 | 7, 104, :_reduce_53, 533 | 9, 104, :_reduce_54, 534 | 1, 107, :_reduce_55, 535 | 1, 107, :_reduce_56, 536 | 1, 107, :_reduce_57, 537 | 7, 101, :_reduce_58, 538 | 7, 101, :_reduce_59, 539 | 1, 101, :_reduce_none, 540 | 6, 109, :_reduce_61, 541 | 6, 109, :_reduce_62, 542 | 1, 108, :_reduce_63, 543 | 3, 108, :_reduce_64, 544 | 1, 110, :_reduce_none, 545 | 1, 110, :_reduce_none, 546 | 7, 111, :_reduce_67, 547 | 1, 112, :_reduce_none, 548 | 1, 112, :_reduce_none, 549 | 7, 114, :_reduce_70, 550 | 8, 103, :_reduce_71, 551 | 2, 115, :_reduce_72, 552 | 5, 115, :_reduce_73, 553 | 1, 113, :_reduce_74, 554 | 1, 113, :_reduce_75, 555 | 4, 113, :_reduce_76, 556 | 1, 116, :_reduce_77, 557 | 3, 116, :_reduce_78, 558 | 3, 88, :_reduce_79, 559 | 2, 89, :_reduce_80, 560 | 2, 89, :_reduce_81, 561 | 9, 90, :_reduce_82, 562 | 12, 90, :_reduce_83, 563 | 8, 91, :_reduce_84, 564 | 11, 91, :_reduce_85, 565 | 10, 91, :_reduce_86, 566 | 5, 92, :_reduce_87, 567 | 1, 118, :_reduce_88, 568 | 1, 118, :_reduce_89, 569 | 1, 125, :_reduce_90, 570 | 3, 125, :_reduce_91, 571 | 0, 119, :_reduce_none, 572 | 5, 119, :_reduce_93, 573 | 0, 120, :_reduce_none, 574 | 2, 120, :_reduce_95, 575 | 1, 126, :_reduce_96, 576 | 3, 126, :_reduce_97, 577 | 3, 127, :_reduce_98, 578 | 5, 127, :_reduce_99, 579 | 1, 128, :_reduce_none, 580 | 1, 130, :_reduce_101, 581 | 1, 130, :_reduce_102, 582 | 1, 130, :_reduce_103, 583 | 1, 130, :_reduce_104, 584 | 1, 130, :_reduce_105, 585 | 1, 130, :_reduce_none, 586 | 0, 123, :_reduce_none, 587 | 2, 123, :_reduce_108, 588 | 1, 131, :_reduce_109, 589 | 3, 131, :_reduce_110, 590 | 3, 132, :_reduce_111, 591 | 3, 132, :_reduce_112, 592 | 5, 132, :_reduce_113, 593 | 3, 132, :_reduce_114, 594 | 1, 133, :_reduce_none, 595 | 1, 133, :_reduce_none, 596 | 1, 133, :_reduce_117, 597 | 1, 136, :_reduce_none, 598 | 2, 136, :_reduce_119, 599 | 1, 135, :_reduce_120, 600 | 2, 135, :_reduce_121, 601 | 0, 122, :_reduce_none, 602 | 2, 122, :_reduce_123, 603 | 2, 122, :_reduce_124, 604 | 0, 97, :_reduce_none, 605 | 2, 97, :_reduce_126, 606 | 0, 121, :_reduce_none, 607 | 2, 121, :_reduce_128, 608 | 5, 93, :_reduce_129, 609 | 7, 93, :_reduce_130, 610 | 5, 93, :_reduce_131, 611 | 7, 93, :_reduce_132, 612 | 1, 137, :_reduce_133, 613 | 1, 137, :_reduce_134, 614 | 1, 139, :_reduce_135, 615 | 1, 139, :_reduce_136, 616 | 1, 138, :_reduce_137, 617 | 3, 138, :_reduce_138, 618 | 3, 140, :_reduce_139, 619 | 2, 124, :_reduce_140, 620 | 1, 141, :_reduce_141, 621 | 3, 141, :_reduce_142, 622 | 3, 142, :_reduce_143, 623 | 4, 94, :_reduce_144, 624 | 6, 94, :_reduce_145, 625 | 8, 95, :_reduce_146, 626 | 4, 95, :_reduce_147, 627 | 4, 95, :_reduce_148, 628 | 1, 143, :_reduce_none, 629 | 3, 144, :_reduce_150, 630 | 5, 144, :_reduce_151, 631 | 1, 145, :_reduce_152, 632 | 3, 145, :_reduce_153, 633 | 1, 96, :_reduce_154, 634 | 1, 129, :_reduce_none, 635 | 1, 129, :_reduce_none, 636 | 1, 129, :_reduce_none, 637 | 1, 129, :_reduce_none, 638 | 1, 129, :_reduce_none, 639 | 1, 129, :_reduce_none, 640 | 2, 147, :_reduce_none, 641 | 3, 147, :_reduce_162, 642 | 1, 149, :_reduce_163, 643 | 3, 149, :_reduce_164, 644 | 2, 148, :_reduce_165, 645 | 3, 148, :_reduce_166, 646 | 1, 150, :_reduce_none, 647 | 3, 150, :_reduce_168, 648 | 3, 151, :_reduce_169, 649 | 3, 151, :_reduce_170, 650 | 1, 146, :_reduce_none, 651 | 1, 146, :_reduce_none, 652 | 1, 146, :_reduce_none, 653 | 3, 134, :_reduce_174, 654 | 3, 134, :_reduce_175, 655 | 3, 134, :_reduce_176, 656 | 1, 152, :_reduce_177, 657 | 3, 152, :_reduce_178, 658 | 1, 153, :_reduce_179, 659 | 3, 153, :_reduce_180, 660 | 1, 154, :_reduce_181, 661 | 3, 154, :_reduce_182, 662 | 1, 117, :_reduce_183, 663 | 3, 117, :_reduce_184 ] 664 | 665 | racc_reduce_n = 185 666 | 667 | racc_shift_n = 401 668 | 669 | racc_token_table = { 670 | false => 0, 671 | :error => 1, 672 | :RUBY_SCRIPT => 2, 673 | :SHELL_SCRIPT => 3, 674 | :GT => 4, 675 | :STRING_VALUE => 5, 676 | :GTGT => 6, 677 | :SHOW => 7, 678 | :TABLES => 8, 679 | :TABLE => 9, 680 | :STATUS => 10, 681 | :REGIONS => 11, 682 | :CREATE => 12, 683 | :IDENTIFIER => 13, 684 | :LIKE => 14, 685 | :ALTER => 15, 686 | :CHANGE => 16, 687 | :GLOBAL => 17, 688 | :INDEX => 18, 689 | :ADD => 19, 690 | :DROP => 20, 691 | :USE => 21, 692 | :STRING => 22, 693 | "(" => 23, 694 | ")" => 24, 695 | :STREAM => 25, 696 | :EQ => 26, 697 | :BOOL => 27, 698 | :NEW_IMAGE => 28, 699 | :OLD_IMAGE => 29, 700 | :NEW_AND_OLD_IMAGES => 30, 701 | :KEYS_ONLY => 31, 702 | :HASH => 32, 703 | "," => 33, 704 | :RANGE => 34, 705 | :NUMBER => 35, 706 | :BINARY => 36, 707 | :READ => 37, 708 | :NUMBER_VALUE => 38, 709 | :WRITE => 39, 710 | :ALL => 40, 711 | :INCLUDE => 41, 712 | :DESCRIBE => 42, 713 | :DESC => 43, 714 | :SELECT => 44, 715 | :FROM => 45, 716 | :COUNT => 46, 717 | "*" => 47, 718 | "/" => 48, 719 | :GET => 49, 720 | :WHERE => 50, 721 | :AND => 51, 722 | :BETWEEN => 52, 723 | :LE => 53, 724 | :LT => 54, 725 | :GE => 55, 726 | :BEGINS_WITH => 56, 727 | :IN => 57, 728 | :IS => 58, 729 | :NE => 59, 730 | :CONTAINS => 60, 731 | :NOT => 61, 732 | :NULL => 62, 733 | :ORDER => 63, 734 | :ASC => 64, 735 | :LIMIT => 65, 736 | :HAVING => 66, 737 | :UPDATE => 67, 738 | :SET => 68, 739 | :DELETE => 69, 740 | :DEL => 70, 741 | :INSERT => 71, 742 | :INTO => 72, 743 | :VALUES => 73, 744 | :NEXT => 74, 745 | "[" => 75, 746 | "]" => 76, 747 | "{" => 77, 748 | "}" => 78, 749 | ":" => 79, 750 | :BINARY_VALUE => 80 } 751 | 752 | racc_nt_base = 81 753 | 754 | racc_use_result_var = false 755 | 756 | Racc_arg = [ 757 | racc_action_table, 758 | racc_action_check, 759 | racc_action_default, 760 | racc_action_pointer, 761 | racc_goto_table, 762 | racc_goto_check, 763 | racc_goto_default, 764 | racc_goto_pointer, 765 | racc_nt_base, 766 | racc_reduce_table, 767 | racc_token_table, 768 | racc_shift_n, 769 | racc_reduce_n, 770 | racc_use_result_var ] 771 | 772 | Racc_token_to_s_table = [ 773 | "$end", 774 | "error", 775 | "RUBY_SCRIPT", 776 | "SHELL_SCRIPT", 777 | "GT", 778 | "STRING_VALUE", 779 | "GTGT", 780 | "SHOW", 781 | "TABLES", 782 | "TABLE", 783 | "STATUS", 784 | "REGIONS", 785 | "CREATE", 786 | "IDENTIFIER", 787 | "LIKE", 788 | "ALTER", 789 | "CHANGE", 790 | "GLOBAL", 791 | "INDEX", 792 | "ADD", 793 | "DROP", 794 | "USE", 795 | "STRING", 796 | "\"(\"", 797 | "\")\"", 798 | "STREAM", 799 | "EQ", 800 | "BOOL", 801 | "NEW_IMAGE", 802 | "OLD_IMAGE", 803 | "NEW_AND_OLD_IMAGES", 804 | "KEYS_ONLY", 805 | "HASH", 806 | "\",\"", 807 | "RANGE", 808 | "NUMBER", 809 | "BINARY", 810 | "READ", 811 | "NUMBER_VALUE", 812 | "WRITE", 813 | "ALL", 814 | "INCLUDE", 815 | "DESCRIBE", 816 | "DESC", 817 | "SELECT", 818 | "FROM", 819 | "COUNT", 820 | "\"*\"", 821 | "\"/\"", 822 | "GET", 823 | "WHERE", 824 | "AND", 825 | "BETWEEN", 826 | "LE", 827 | "LT", 828 | "GE", 829 | "BEGINS_WITH", 830 | "IN", 831 | "IS", 832 | "NE", 833 | "CONTAINS", 834 | "NOT", 835 | "NULL", 836 | "ORDER", 837 | "ASC", 838 | "LIMIT", 839 | "HAVING", 840 | "UPDATE", 841 | "SET", 842 | "DELETE", 843 | "DEL", 844 | "INSERT", 845 | "INTO", 846 | "VALUES", 847 | "NEXT", 848 | "\"[\"", 849 | "\"]\"", 850 | "\"{\"", 851 | "\"}\"", 852 | "\":\"", 853 | "BINARY_VALUE", 854 | "$start", 855 | "stmt", 856 | "query", 857 | "show_stmt", 858 | "alter_stmt", 859 | "use_stmt", 860 | "create_stmt", 861 | "drop_stmt", 862 | "describe_stmt", 863 | "select_stmt", 864 | "scan_stmt", 865 | "get_stmt", 866 | "update_stmt", 867 | "delete_stmt", 868 | "insert_stmt", 869 | "next_stmt", 870 | "limit_clause", 871 | "like_clause", 872 | "alter_table_stmt", 873 | "alter_table_index_stmt", 874 | "capacity_clause", 875 | "stream_clause", 876 | "global_index_definition_with_capacity", 877 | "create_definition", 878 | "capacity_stream_clause", 879 | "stream_view_type", 880 | "attr_type_list", 881 | "index_definition_list", 882 | "strict_capacity_clause", 883 | "index_definition", 884 | "locat_index_definition", 885 | "global_index_definition", 886 | "index_type_definition", 887 | "global_index_definition_without_capacity", 888 | "global_index_keys", 889 | "index_include_attr_list", 890 | "identifier_list", 891 | "attrs_to_get", 892 | "use_index_clause", 893 | "select_where_clause", 894 | "having_clause", 895 | "order_clause", 896 | "scan_where_clause", 897 | "update_where_clause", 898 | "attrs_list", 899 | "select_expr_list", 900 | "select_expr", 901 | "select_operator", 902 | "value", 903 | "common_operator", 904 | "scan_expr_list", 905 | "scan_expr", 906 | "scan_operator", 907 | "value_list", 908 | "null_operator", 909 | "contains_operator", 910 | "set_or_add", 911 | "attr_to_update_list", 912 | "delete_or_del", 913 | "attr_to_update", 914 | "update_expr_list", 915 | "update_expr", 916 | "attr_to_insert_list", 917 | "insert_value_clause", 918 | "insert_value_list", 919 | "single_value", 920 | "list", 921 | "map", 922 | "list_items", 923 | "map_items", 924 | "map_item", 925 | "number_list", 926 | "string_list", 927 | "binary_list" ] 928 | 929 | Racc_debug_parser = false 930 | 931 | ##### State transition tables end ##### 932 | 933 | # reduce 0 omitted 934 | 935 | module_eval(<<'.,.,', 'ddb-parser.y', 5) 936 | def _reduce_1(val, _values) 937 | [val[0], nil, nil] 938 | 939 | end 940 | .,., 941 | 942 | module_eval(<<'.,.,', 'ddb-parser.y', 9) 943 | def _reduce_2(val, _values) 944 | [val[0], :ruby, val[1]] 945 | 946 | end 947 | .,., 948 | 949 | module_eval(<<'.,.,', 'ddb-parser.y', 13) 950 | def _reduce_3(val, _values) 951 | [val[0], :shell, val[1]] 952 | 953 | end 954 | .,., 955 | 956 | module_eval(<<'.,.,', 'ddb-parser.y', 17) 957 | def _reduce_4(val, _values) 958 | [val[0], :overwrite, val[2]] 959 | 960 | end 961 | .,., 962 | 963 | module_eval(<<'.,.,', 'ddb-parser.y', 21) 964 | def _reduce_5(val, _values) 965 | [val[0], :append, val[2]] 966 | 967 | end 968 | .,., 969 | 970 | module_eval(<<'.,.,', 'ddb-parser.y', 25) 971 | def _reduce_6(val, _values) 972 | [struct(:NULL), :ruby, val[0]] 973 | 974 | end 975 | .,., 976 | 977 | module_eval(<<'.,.,', 'ddb-parser.y', 29) 978 | def _reduce_7(val, _values) 979 | [struct(:NULL), :shell, val[0]] 980 | 981 | end 982 | .,., 983 | 984 | # reduce 8 omitted 985 | 986 | # reduce 9 omitted 987 | 988 | # reduce 10 omitted 989 | 990 | # reduce 11 omitted 991 | 992 | # reduce 12 omitted 993 | 994 | # reduce 13 omitted 995 | 996 | # reduce 14 omitted 997 | 998 | # reduce 15 omitted 999 | 1000 | # reduce 16 omitted 1001 | 1002 | # reduce 17 omitted 1003 | 1004 | # reduce 18 omitted 1005 | 1006 | # reduce 19 omitted 1007 | 1008 | # reduce 20 omitted 1009 | 1010 | module_eval(<<'.,.,', 'ddb-parser.y', 48) 1011 | def _reduce_21(val, _values) 1012 | struct(:SHOW_TABLES, :limit => val[2], :like => val[3]) 1013 | 1014 | end 1015 | .,., 1016 | 1017 | module_eval(<<'.,.,', 'ddb-parser.y', 52) 1018 | def _reduce_22(val, _values) 1019 | struct(:SHOW_TABLE_STATUS, :like => val[3]) 1020 | 1021 | end 1022 | .,., 1023 | 1024 | module_eval(<<'.,.,', 'ddb-parser.y', 56) 1025 | def _reduce_23(val, _values) 1026 | struct(:SHOW_REGIONS) 1027 | 1028 | end 1029 | .,., 1030 | 1031 | module_eval(<<'.,.,', 'ddb-parser.y', 60) 1032 | def _reduce_24(val, _values) 1033 | struct(:SHOW_CREATE_TABLE, :table => val[3]) 1034 | 1035 | end 1036 | .,., 1037 | 1038 | # reduce 25 omitted 1039 | 1040 | module_eval(<<'.,.,', 'ddb-parser.y', 66) 1041 | def _reduce_26(val, _values) 1042 | val[1] 1043 | 1044 | end 1045 | .,., 1046 | 1047 | # reduce 27 omitted 1048 | 1049 | # reduce 28 omitted 1050 | 1051 | module_eval(<<'.,.,', 'ddb-parser.y', 75) 1052 | def _reduce_29(val, _values) 1053 | struct(:ALTER_TABLE, :table => val[2], :capacity => val[3], :stream => nil) 1054 | 1055 | end 1056 | .,., 1057 | 1058 | module_eval(<<'.,.,', 'ddb-parser.y', 79) 1059 | def _reduce_30(val, _values) 1060 | struct(:ALTER_TABLE, :table => val[2], :capacity => nil, :stream => val[3]) 1061 | 1062 | end 1063 | .,., 1064 | 1065 | module_eval(<<'.,.,', 'ddb-parser.y', 83) 1066 | def _reduce_31(val, _values) 1067 | struct(:ALTER_TABLE, :table => val[2], :capacity => val[3], :stream => val[4]) 1068 | 1069 | end 1070 | .,., 1071 | 1072 | module_eval(<<'.,.,', 'ddb-parser.y', 87) 1073 | def _reduce_32(val, _values) 1074 | struct(:ALTER_TABLE, :table => val[2], :capacity => val[4], :stream => val[3]) 1075 | 1076 | end 1077 | .,., 1078 | 1079 | module_eval(<<'.,.,', 'ddb-parser.y', 92) 1080 | def _reduce_33(val, _values) 1081 | struct(:ALTER_TABLE_INDEX, :table => val[2], :action => 'Update', :index_definition => {:name => val[6], :capacity => val[7]}) 1082 | 1083 | end 1084 | .,., 1085 | 1086 | module_eval(<<'.,.,', 'ddb-parser.y', 96) 1087 | def _reduce_34(val, _values) 1088 | struct(:ALTER_TABLE_INDEX, :table => val[2], :action => 'Create', :index_definition => val[4]) 1089 | 1090 | end 1091 | .,., 1092 | 1093 | module_eval(<<'.,.,', 'ddb-parser.y', 100) 1094 | def _reduce_35(val, _values) 1095 | struct(:ALTER_TABLE_INDEX, :table => val[2], :action => 'Delete', :index_definition => {:name => val[6]}) 1096 | 1097 | end 1098 | .,., 1099 | 1100 | module_eval(<<'.,.,', 'ddb-parser.y', 105) 1101 | def _reduce_36(val, _values) 1102 | struct(:USE, :endpoint_or_region => val[1]) 1103 | 1104 | end 1105 | .,., 1106 | 1107 | module_eval(<<'.,.,', 'ddb-parser.y', 110) 1108 | def _reduce_37(val, _values) 1109 | struct(:USE, :endpoint_or_region => val[1]) 1110 | 1111 | end 1112 | .,., 1113 | 1114 | module_eval(<<'.,.,', 'ddb-parser.y', 115) 1115 | def _reduce_38(val, _values) 1116 | struct(:CREATE, val[4].merge(:table => val[2], :capacity => val[6][:capacity], :stream => val[6][:stream])) 1117 | 1118 | end 1119 | .,., 1120 | 1121 | module_eval(<<'.,.,', 'ddb-parser.y', 119) 1122 | def _reduce_39(val, _values) 1123 | struct(:CREATE_LIKE, :table => val[2], :like => val[4], :capacity => nil, :stream => nil) 1124 | 1125 | end 1126 | .,., 1127 | 1128 | module_eval(<<'.,.,', 'ddb-parser.y', 123) 1129 | def _reduce_40(val, _values) 1130 | struct(:CREATE_LIKE, :table => val[2], :like => val[4], :capacity => nil, :stream => val[5]) 1131 | 1132 | end 1133 | .,., 1134 | 1135 | module_eval(<<'.,.,', 'ddb-parser.y', 127) 1136 | def _reduce_41(val, _values) 1137 | struct(:CREATE_LIKE, :table => val[2], :like => val[4], :capacity => val[5][:capacity], :stream => val[5][:stream]) 1138 | 1139 | end 1140 | .,., 1141 | 1142 | module_eval(<<'.,.,', 'ddb-parser.y', 132) 1143 | def _reduce_42(val, _values) 1144 | {:capacity => val[0], :stream => nil} 1145 | 1146 | end 1147 | .,., 1148 | 1149 | module_eval(<<'.,.,', 'ddb-parser.y', 136) 1150 | def _reduce_43(val, _values) 1151 | {:capacity => val[0], :stream => val[1]} 1152 | 1153 | end 1154 | .,., 1155 | 1156 | module_eval(<<'.,.,', 'ddb-parser.y', 140) 1157 | def _reduce_44(val, _values) 1158 | {:capacity => val[1], :stream => val[0]} 1159 | 1160 | end 1161 | .,., 1162 | 1163 | module_eval(<<'.,.,', 'ddb-parser.y', 145) 1164 | def _reduce_45(val, _values) 1165 | val[2] 1166 | 1167 | end 1168 | .,., 1169 | 1170 | module_eval(<<'.,.,', 'ddb-parser.y', 149) 1171 | def _reduce_46(val, _values) 1172 | val[2] 1173 | 1174 | end 1175 | .,., 1176 | 1177 | # reduce 47 omitted 1178 | 1179 | # reduce 48 omitted 1180 | 1181 | # reduce 49 omitted 1182 | 1183 | # reduce 50 omitted 1184 | 1185 | module_eval(<<'.,.,', 'ddb-parser.y', 159) 1186 | def _reduce_51(val, _values) 1187 | {:hash => {:name => val[0], :type => val[1]}, :range => nil, :indices => nil} 1188 | 1189 | end 1190 | .,., 1191 | 1192 | module_eval(<<'.,.,', 'ddb-parser.y', 163) 1193 | def _reduce_52(val, _values) 1194 | {:hash => {:name => val[0], :type => val[1]}, :range => nil, :indices => val[4]} 1195 | 1196 | end 1197 | .,., 1198 | 1199 | module_eval(<<'.,.,', 'ddb-parser.y', 167) 1200 | def _reduce_53(val, _values) 1201 | {:hash => {:name => val[0], :type => val[1]}, :range => {:name => val[4], :type => val[5]}, :indices => nil} 1202 | 1203 | end 1204 | .,., 1205 | 1206 | module_eval(<<'.,.,', 'ddb-parser.y', 171) 1207 | def _reduce_54(val, _values) 1208 | {:hash => {:name => val[0], :type => val[1]}, :range => {:name => val[4], :type => val[5]}, :indices => val[8]} 1209 | 1210 | end 1211 | .,., 1212 | 1213 | module_eval(<<'.,.,', 'ddb-parser.y', 176) 1214 | def _reduce_55(val, _values) 1215 | 'S' 1216 | 1217 | end 1218 | .,., 1219 | 1220 | module_eval(<<'.,.,', 'ddb-parser.y', 180) 1221 | def _reduce_56(val, _values) 1222 | 'N' 1223 | 1224 | end 1225 | .,., 1226 | 1227 | module_eval(<<'.,.,', 'ddb-parser.y', 184) 1228 | def _reduce_57(val, _values) 1229 | 'B' 1230 | 1231 | end 1232 | .,., 1233 | 1234 | module_eval(<<'.,.,', 'ddb-parser.y', 189) 1235 | def _reduce_58(val, _values) 1236 | {:read => val[2], :write => val[6]} 1237 | 1238 | end 1239 | .,., 1240 | 1241 | module_eval(<<'.,.,', 'ddb-parser.y', 193) 1242 | def _reduce_59(val, _values) 1243 | {:read => val[6], :write => val[2]} 1244 | 1245 | end 1246 | .,., 1247 | 1248 | # reduce 60 omitted 1249 | 1250 | module_eval(<<'.,.,', 'ddb-parser.y', 199) 1251 | def _reduce_61(val, _values) 1252 | {:read => val[2], :write => val[5]} 1253 | 1254 | end 1255 | .,., 1256 | 1257 | module_eval(<<'.,.,', 'ddb-parser.y', 203) 1258 | def _reduce_62(val, _values) 1259 | {:read => val[5], :write => val[2]} 1260 | 1261 | end 1262 | .,., 1263 | 1264 | module_eval(<<'.,.,', 'ddb-parser.y', 208) 1265 | def _reduce_63(val, _values) 1266 | [val[0]] 1267 | 1268 | end 1269 | .,., 1270 | 1271 | module_eval(<<'.,.,', 'ddb-parser.y', 212) 1272 | def _reduce_64(val, _values) 1273 | val[0] + [val[2]] 1274 | 1275 | end 1276 | .,., 1277 | 1278 | # reduce 65 omitted 1279 | 1280 | # reduce 66 omitted 1281 | 1282 | module_eval(<<'.,.,', 'ddb-parser.y', 220) 1283 | def _reduce_67(val, _values) 1284 | {:name => val[1], :key => val[3], :type => val[4], :projection => val[6]} 1285 | 1286 | end 1287 | .,., 1288 | 1289 | # reduce 68 omitted 1290 | 1291 | # reduce 69 omitted 1292 | 1293 | module_eval(<<'.,.,', 'ddb-parser.y', 228) 1294 | def _reduce_70(val, _values) 1295 | {:name => val[2], :keys => val[4], :projection => val[6], :global => true} 1296 | 1297 | end 1298 | .,., 1299 | 1300 | module_eval(<<'.,.,', 'ddb-parser.y', 233) 1301 | def _reduce_71(val, _values) 1302 | {:name => val[2], :keys => val[4], :projection => val[6], :capacity => val[7], :global => true} 1303 | 1304 | end 1305 | .,., 1306 | 1307 | module_eval(<<'.,.,', 'ddb-parser.y', 238) 1308 | def _reduce_72(val, _values) 1309 | {:hash => {:key => val[0], :type => val[1]}} 1310 | 1311 | end 1312 | .,., 1313 | 1314 | module_eval(<<'.,.,', 'ddb-parser.y', 242) 1315 | def _reduce_73(val, _values) 1316 | { 1317 | :hash => {:key => val[0], :type => val[1]}, 1318 | :range => {:key => val[3], :type => val[4]}, 1319 | } 1320 | 1321 | end 1322 | .,., 1323 | 1324 | module_eval(<<'.,.,', 'ddb-parser.y', 250) 1325 | def _reduce_74(val, _values) 1326 | {:type => 'ALL'} 1327 | 1328 | end 1329 | .,., 1330 | 1331 | module_eval(<<'.,.,', 'ddb-parser.y', 254) 1332 | def _reduce_75(val, _values) 1333 | {:type => 'KEYS_ONLY'} 1334 | 1335 | end 1336 | .,., 1337 | 1338 | module_eval(<<'.,.,', 'ddb-parser.y', 258) 1339 | def _reduce_76(val, _values) 1340 | {:type => 'INCLUDE', :attrs => val[2]} 1341 | 1342 | end 1343 | .,., 1344 | 1345 | module_eval(<<'.,.,', 'ddb-parser.y', 263) 1346 | def _reduce_77(val, _values) 1347 | [val[0]] 1348 | 1349 | end 1350 | .,., 1351 | 1352 | module_eval(<<'.,.,', 'ddb-parser.y', 267) 1353 | def _reduce_78(val, _values) 1354 | val[0] + [val[2]] 1355 | 1356 | end 1357 | .,., 1358 | 1359 | module_eval(<<'.,.,', 'ddb-parser.y', 272) 1360 | def _reduce_79(val, _values) 1361 | struct(:DROP, :tables => val[2]) 1362 | 1363 | end 1364 | .,., 1365 | 1366 | module_eval(<<'.,.,', 'ddb-parser.y', 277) 1367 | def _reduce_80(val, _values) 1368 | struct(:DESCRIBE, :table => val[1]) 1369 | 1370 | end 1371 | .,., 1372 | 1373 | module_eval(<<'.,.,', 'ddb-parser.y', 281) 1374 | def _reduce_81(val, _values) 1375 | struct(:DESCRIBE, :table => val[1]) 1376 | 1377 | end 1378 | .,., 1379 | 1380 | module_eval(<<'.,.,', 'ddb-parser.y', 286) 1381 | def _reduce_82(val, _values) 1382 | struct(:SELECT, :attrs => val[1], :table => val[3], :index => val[4], :conds => val[5], :having => val[6], :order_asc => val[7], :limit => val[8], :count => false) 1383 | 1384 | end 1385 | .,., 1386 | 1387 | module_eval(<<'.,.,', 'ddb-parser.y', 290) 1388 | def _reduce_83(val, _values) 1389 | struct(:SELECT, :attrs => [], :table => val[6], :index => val[7], :conds => val[8], :having => val[9], :order_asc => val[10], :limit => val[11], :count => true) 1390 | 1391 | end 1392 | .,., 1393 | 1394 | module_eval(<<'.,.,', 'ddb-parser.y', 295) 1395 | def _reduce_84(val, _values) 1396 | struct(:SCAN, :attrs => val[2], :table => val[4], :index => val[5], :conds => val[6], :limit => val[7], :count => false, :segment => nil, :total_segments => nil) 1397 | 1398 | end 1399 | .,., 1400 | 1401 | module_eval(<<'.,.,', 'ddb-parser.y', 299) 1402 | def _reduce_85(val, _values) 1403 | struct(:SCAN, :attrs => [], :table => val[7], :index => val[8], :conds => val[9], :limit => val[10], :count => true, :segment => nil, :total_segments => nil) 1404 | 1405 | end 1406 | .,., 1407 | 1408 | module_eval(<<'.,.,', 'ddb-parser.y', 303) 1409 | def _reduce_86(val, _values) 1410 | struct(:SCAN, :attrs => val[4], :table => val[6], :index => val[7], :conds => val[8], :limit => val[9], :count => false, :segment => val[1], :total_segments => val[3]) 1411 | 1412 | end 1413 | .,., 1414 | 1415 | module_eval(<<'.,.,', 'ddb-parser.y', 308) 1416 | def _reduce_87(val, _values) 1417 | struct(:GET, :attrs => val[1], :table => val[3], :conds => val[4]) 1418 | 1419 | end 1420 | .,., 1421 | 1422 | module_eval(<<'.,.,', 'ddb-parser.y', 313) 1423 | def _reduce_88(val, _values) 1424 | [] 1425 | 1426 | end 1427 | .,., 1428 | 1429 | module_eval(<<'.,.,', 'ddb-parser.y', 317) 1430 | def _reduce_89(val, _values) 1431 | val[0] 1432 | 1433 | end 1434 | .,., 1435 | 1436 | module_eval(<<'.,.,', 'ddb-parser.y', 322) 1437 | def _reduce_90(val, _values) 1438 | [val[0]] 1439 | 1440 | end 1441 | .,., 1442 | 1443 | module_eval(<<'.,.,', 'ddb-parser.y', 326) 1444 | def _reduce_91(val, _values) 1445 | val[0] + [val[2]] 1446 | 1447 | end 1448 | .,., 1449 | 1450 | # reduce 92 omitted 1451 | 1452 | module_eval(<<'.,.,', 'ddb-parser.y', 332) 1453 | def _reduce_93(val, _values) 1454 | val[3] 1455 | 1456 | end 1457 | .,., 1458 | 1459 | # reduce 94 omitted 1460 | 1461 | module_eval(<<'.,.,', 'ddb-parser.y', 338) 1462 | def _reduce_95(val, _values) 1463 | val[1] 1464 | 1465 | end 1466 | .,., 1467 | 1468 | module_eval(<<'.,.,', 'ddb-parser.y', 343) 1469 | def _reduce_96(val, _values) 1470 | [val[0]] 1471 | 1472 | end 1473 | .,., 1474 | 1475 | module_eval(<<'.,.,', 'ddb-parser.y', 347) 1476 | def _reduce_97(val, _values) 1477 | val[0] + [val[2]] 1478 | 1479 | end 1480 | .,., 1481 | 1482 | module_eval(<<'.,.,', 'ddb-parser.y', 352) 1483 | def _reduce_98(val, _values) 1484 | [val[0], val[1].to_s.upcase.to_sym, [val[2]]] 1485 | 1486 | end 1487 | .,., 1488 | 1489 | module_eval(<<'.,.,', 'ddb-parser.y', 356) 1490 | def _reduce_99(val, _values) 1491 | [val[0], val[1].to_s.upcase.to_sym, [val[2], val[4]]] 1492 | 1493 | end 1494 | .,., 1495 | 1496 | # reduce 100 omitted 1497 | 1498 | module_eval(<<'.,.,', 'ddb-parser.y', 363) 1499 | def _reduce_101(val, _values) 1500 | :EQ 1501 | 1502 | end 1503 | .,., 1504 | 1505 | module_eval(<<'.,.,', 'ddb-parser.y', 367) 1506 | def _reduce_102(val, _values) 1507 | :LE 1508 | 1509 | end 1510 | .,., 1511 | 1512 | module_eval(<<'.,.,', 'ddb-parser.y', 371) 1513 | def _reduce_103(val, _values) 1514 | :LT 1515 | 1516 | end 1517 | .,., 1518 | 1519 | module_eval(<<'.,.,', 'ddb-parser.y', 375) 1520 | def _reduce_104(val, _values) 1521 | :GE 1522 | 1523 | end 1524 | .,., 1525 | 1526 | module_eval(<<'.,.,', 'ddb-parser.y', 379) 1527 | def _reduce_105(val, _values) 1528 | :GT 1529 | 1530 | end 1531 | .,., 1532 | 1533 | # reduce 106 omitted 1534 | 1535 | # reduce 107 omitted 1536 | 1537 | module_eval(<<'.,.,', 'ddb-parser.y', 386) 1538 | def _reduce_108(val, _values) 1539 | val[1] 1540 | 1541 | end 1542 | .,., 1543 | 1544 | module_eval(<<'.,.,', 'ddb-parser.y', 391) 1545 | def _reduce_109(val, _values) 1546 | [val[0]] 1547 | 1548 | end 1549 | .,., 1550 | 1551 | module_eval(<<'.,.,', 'ddb-parser.y', 395) 1552 | def _reduce_110(val, _values) 1553 | val[0] + [val[2]] 1554 | 1555 | end 1556 | .,., 1557 | 1558 | module_eval(<<'.,.,', 'ddb-parser.y', 400) 1559 | def _reduce_111(val, _values) 1560 | [val[0], val[1].to_s.upcase.to_sym, [val[2]]] 1561 | 1562 | end 1563 | .,., 1564 | 1565 | module_eval(<<'.,.,', 'ddb-parser.y', 404) 1566 | def _reduce_112(val, _values) 1567 | [val[0], val[1].to_s.upcase.to_sym, val[2]] 1568 | 1569 | end 1570 | .,., 1571 | 1572 | module_eval(<<'.,.,', 'ddb-parser.y', 408) 1573 | def _reduce_113(val, _values) 1574 | [val[0], val[1].to_s.upcase.to_sym, [val[2], val[4]]] 1575 | 1576 | end 1577 | .,., 1578 | 1579 | module_eval(<<'.,.,', 'ddb-parser.y', 412) 1580 | def _reduce_114(val, _values) 1581 | [val[0], val[2].to_s.upcase.to_sym, []] 1582 | 1583 | end 1584 | .,., 1585 | 1586 | # reduce 115 omitted 1587 | 1588 | # reduce 116 omitted 1589 | 1590 | module_eval(<<'.,.,', 'ddb-parser.y', 418) 1591 | def _reduce_117(val, _values) 1592 | :NE 1593 | 1594 | end 1595 | .,., 1596 | 1597 | # reduce 118 omitted 1598 | 1599 | module_eval(<<'.,.,', 'ddb-parser.y', 424) 1600 | def _reduce_119(val, _values) 1601 | :NOT_CONTAINS 1602 | 1603 | end 1604 | .,., 1605 | 1606 | module_eval(<<'.,.,', 'ddb-parser.y', 428) 1607 | def _reduce_120(val, _values) 1608 | :NULL 1609 | 1610 | end 1611 | .,., 1612 | 1613 | module_eval(<<'.,.,', 'ddb-parser.y', 432) 1614 | def _reduce_121(val, _values) 1615 | :NOT_NULL 1616 | 1617 | end 1618 | .,., 1619 | 1620 | # reduce 122 omitted 1621 | 1622 | module_eval(<<'.,.,', 'ddb-parser.y', 438) 1623 | def _reduce_123(val, _values) 1624 | true 1625 | 1626 | end 1627 | .,., 1628 | 1629 | module_eval(<<'.,.,', 'ddb-parser.y', 442) 1630 | def _reduce_124(val, _values) 1631 | false 1632 | 1633 | end 1634 | .,., 1635 | 1636 | # reduce 125 omitted 1637 | 1638 | module_eval(<<'.,.,', 'ddb-parser.y', 448) 1639 | def _reduce_126(val, _values) 1640 | val[1] 1641 | 1642 | end 1643 | .,., 1644 | 1645 | # reduce 127 omitted 1646 | 1647 | module_eval(<<'.,.,', 'ddb-parser.y', 454) 1648 | def _reduce_128(val, _values) 1649 | val[1] 1650 | 1651 | end 1652 | .,., 1653 | 1654 | module_eval(<<'.,.,', 'ddb-parser.y', 459) 1655 | def _reduce_129(val, _values) 1656 | struct(:UPDATE, :table => val[1], :action => val[2], :attrs => val[3], :conds => val[4]) 1657 | 1658 | end 1659 | .,., 1660 | 1661 | module_eval(<<'.,.,', 'ddb-parser.y', 463) 1662 | def _reduce_130(val, _values) 1663 | struct(:UPDATE_ALL, :table => val[2], :action => val[3], :attrs => val[4], :conds => val[5], :limit => val[6]) 1664 | 1665 | end 1666 | .,., 1667 | 1668 | module_eval(<<'.,.,', 'ddb-parser.y', 467) 1669 | def _reduce_131(val, _values) 1670 | attrs = {} 1671 | val[3].each {|i| attrs[i] = true } 1672 | struct(:UPDATE, :table => val[1], :action => val[2], :attrs => attrs, :conds => val[4]) 1673 | 1674 | end 1675 | .,., 1676 | 1677 | module_eval(<<'.,.,', 'ddb-parser.y', 473) 1678 | def _reduce_132(val, _values) 1679 | attrs = {} 1680 | val[4].each {|i| attrs[i] = true } 1681 | struct(:UPDATE_ALL, :table => val[2], :action => val[3], :attrs => attrs, :conds => val[5], :limit => val[6]) 1682 | 1683 | end 1684 | .,., 1685 | 1686 | module_eval(<<'.,.,', 'ddb-parser.y', 480) 1687 | def _reduce_133(val, _values) 1688 | :PUT 1689 | 1690 | end 1691 | .,., 1692 | 1693 | module_eval(<<'.,.,', 'ddb-parser.y', 484) 1694 | def _reduce_134(val, _values) 1695 | :ADD 1696 | 1697 | end 1698 | .,., 1699 | 1700 | module_eval(<<'.,.,', 'ddb-parser.y', 489) 1701 | def _reduce_135(val, _values) 1702 | :DELETE 1703 | 1704 | end 1705 | .,., 1706 | 1707 | module_eval(<<'.,.,', 'ddb-parser.y', 493) 1708 | def _reduce_136(val, _values) 1709 | :DELETE 1710 | 1711 | end 1712 | .,., 1713 | 1714 | module_eval(<<'.,.,', 'ddb-parser.y', 498) 1715 | def _reduce_137(val, _values) 1716 | [val[0]] 1717 | 1718 | end 1719 | .,., 1720 | 1721 | module_eval(<<'.,.,', 'ddb-parser.y', 502) 1722 | def _reduce_138(val, _values) 1723 | val[0] + [val[2]] 1724 | 1725 | end 1726 | .,., 1727 | 1728 | module_eval(<<'.,.,', 'ddb-parser.y', 507) 1729 | def _reduce_139(val, _values) 1730 | [val[0], val[2]] 1731 | 1732 | end 1733 | .,., 1734 | 1735 | module_eval(<<'.,.,', 'ddb-parser.y', 512) 1736 | def _reduce_140(val, _values) 1737 | val[1] 1738 | 1739 | end 1740 | .,., 1741 | 1742 | module_eval(<<'.,.,', 'ddb-parser.y', 517) 1743 | def _reduce_141(val, _values) 1744 | [val[0]] 1745 | 1746 | end 1747 | .,., 1748 | 1749 | module_eval(<<'.,.,', 'ddb-parser.y', 521) 1750 | def _reduce_142(val, _values) 1751 | val[0] + [val[2]] 1752 | 1753 | end 1754 | .,., 1755 | 1756 | module_eval(<<'.,.,', 'ddb-parser.y', 526) 1757 | def _reduce_143(val, _values) 1758 | [val[0], val[2]] 1759 | 1760 | end 1761 | .,., 1762 | 1763 | module_eval(<<'.,.,', 'ddb-parser.y', 531) 1764 | def _reduce_144(val, _values) 1765 | struct(:DELETE, :table => val[2], :conds => val[3]) 1766 | 1767 | end 1768 | .,., 1769 | 1770 | module_eval(<<'.,.,', 'ddb-parser.y', 535) 1771 | def _reduce_145(val, _values) 1772 | struct(:DELETE_ALL, :table => val[3], :conds => val[4], :limit => val[5]) 1773 | 1774 | end 1775 | .,., 1776 | 1777 | module_eval(<<'.,.,', 'ddb-parser.y', 540) 1778 | def _reduce_146(val, _values) 1779 | struct(:INSERT, :table => val[2], :attrs => val[4], :values => val[7]) 1780 | 1781 | end 1782 | .,., 1783 | 1784 | module_eval(<<'.,.,', 'ddb-parser.y', 544) 1785 | def _reduce_147(val, _values) 1786 | struct(:INSERT_SELECT, :table => val[2], :select => val[3]) 1787 | 1788 | end 1789 | .,., 1790 | 1791 | module_eval(<<'.,.,', 'ddb-parser.y', 548) 1792 | def _reduce_148(val, _values) 1793 | struct(:INSERT_SCAN, :table => val[2], :select => val[3]) 1794 | 1795 | end 1796 | .,., 1797 | 1798 | # reduce 149 omitted 1799 | 1800 | module_eval(<<'.,.,', 'ddb-parser.y', 555) 1801 | def _reduce_150(val, _values) 1802 | [val[1]] 1803 | 1804 | end 1805 | .,., 1806 | 1807 | module_eval(<<'.,.,', 'ddb-parser.y', 559) 1808 | def _reduce_151(val, _values) 1809 | val[0] + [val[3]] 1810 | 1811 | end 1812 | .,., 1813 | 1814 | module_eval(<<'.,.,', 'ddb-parser.y', 564) 1815 | def _reduce_152(val, _values) 1816 | [val[0]] 1817 | 1818 | end 1819 | .,., 1820 | 1821 | module_eval(<<'.,.,', 'ddb-parser.y', 568) 1822 | def _reduce_153(val, _values) 1823 | val[0] + [val[2]] 1824 | 1825 | end 1826 | .,., 1827 | 1828 | module_eval(<<'.,.,', 'ddb-parser.y', 573) 1829 | def _reduce_154(val, _values) 1830 | struct(:NEXT) 1831 | 1832 | end 1833 | .,., 1834 | 1835 | # reduce 155 omitted 1836 | 1837 | # reduce 156 omitted 1838 | 1839 | # reduce 157 omitted 1840 | 1841 | # reduce 158 omitted 1842 | 1843 | # reduce 159 omitted 1844 | 1845 | # reduce 160 omitted 1846 | 1847 | # reduce 161 omitted 1848 | 1849 | module_eval(<<'.,.,', 'ddb-parser.y', 586) 1850 | def _reduce_162(val, _values) 1851 | val[1] 1852 | 1853 | end 1854 | .,., 1855 | 1856 | module_eval(<<'.,.,', 'ddb-parser.y', 591) 1857 | def _reduce_163(val, _values) 1858 | [val[0]] 1859 | 1860 | end 1861 | .,., 1862 | 1863 | module_eval(<<'.,.,', 'ddb-parser.y', 595) 1864 | def _reduce_164(val, _values) 1865 | val[0] + [val[2]] 1866 | 1867 | end 1868 | .,., 1869 | 1870 | module_eval(<<'.,.,', 'ddb-parser.y', 600) 1871 | def _reduce_165(val, _values) 1872 | {} 1873 | 1874 | end 1875 | .,., 1876 | 1877 | module_eval(<<'.,.,', 'ddb-parser.y', 604) 1878 | def _reduce_166(val, _values) 1879 | val[1] 1880 | 1881 | end 1882 | .,., 1883 | 1884 | # reduce 167 omitted 1885 | 1886 | module_eval(<<'.,.,', 'ddb-parser.y', 610) 1887 | def _reduce_168(val, _values) 1888 | val[0].merge(val[2]) 1889 | 1890 | end 1891 | .,., 1892 | 1893 | module_eval(<<'.,.,', 'ddb-parser.y', 615) 1894 | def _reduce_169(val, _values) 1895 | {val[0] => val[2]} 1896 | 1897 | end 1898 | .,., 1899 | 1900 | module_eval(<<'.,.,', 'ddb-parser.y', 619) 1901 | def _reduce_170(val, _values) 1902 | {val[0] => val[2]} 1903 | 1904 | end 1905 | .,., 1906 | 1907 | # reduce 171 omitted 1908 | 1909 | # reduce 172 omitted 1910 | 1911 | # reduce 173 omitted 1912 | 1913 | module_eval(<<'.,.,', 'ddb-parser.y', 628) 1914 | def _reduce_174(val, _values) 1915 | val[1] 1916 | 1917 | end 1918 | .,., 1919 | 1920 | module_eval(<<'.,.,', 'ddb-parser.y', 632) 1921 | def _reduce_175(val, _values) 1922 | val[1] 1923 | 1924 | end 1925 | .,., 1926 | 1927 | module_eval(<<'.,.,', 'ddb-parser.y', 636) 1928 | def _reduce_176(val, _values) 1929 | val[1] 1930 | 1931 | end 1932 | .,., 1933 | 1934 | module_eval(<<'.,.,', 'ddb-parser.y', 641) 1935 | def _reduce_177(val, _values) 1936 | Set[val[0]] 1937 | 1938 | end 1939 | .,., 1940 | 1941 | module_eval(<<'.,.,', 'ddb-parser.y', 645) 1942 | def _reduce_178(val, _values) 1943 | val[0] + Set[val[2]] 1944 | 1945 | end 1946 | .,., 1947 | 1948 | module_eval(<<'.,.,', 'ddb-parser.y', 650) 1949 | def _reduce_179(val, _values) 1950 | Set[val[0]] 1951 | 1952 | end 1953 | .,., 1954 | 1955 | module_eval(<<'.,.,', 'ddb-parser.y', 654) 1956 | def _reduce_180(val, _values) 1957 | val[0] + Set[val[2]] 1958 | 1959 | end 1960 | .,., 1961 | 1962 | module_eval(<<'.,.,', 'ddb-parser.y', 659) 1963 | def _reduce_181(val, _values) 1964 | Set[val[0]] 1965 | 1966 | end 1967 | .,., 1968 | 1969 | module_eval(<<'.,.,', 'ddb-parser.y', 663) 1970 | def _reduce_182(val, _values) 1971 | val[0] + Set[val[2]] 1972 | 1973 | end 1974 | .,., 1975 | 1976 | module_eval(<<'.,.,', 'ddb-parser.y', 668) 1977 | def _reduce_183(val, _values) 1978 | [val[0]] 1979 | 1980 | end 1981 | .,., 1982 | 1983 | module_eval(<<'.,.,', 'ddb-parser.y', 672) 1984 | def _reduce_184(val, _values) 1985 | val[0] + [val[2]] 1986 | 1987 | end 1988 | .,., 1989 | 1990 | def _reduce_none(val, _values) 1991 | val[0] 1992 | end 1993 | 1994 | end # class Parser 1995 | 1996 | 1997 | end # DynamoDB 1998 | -------------------------------------------------------------------------------- /lib/ddbcli/ddb-parser.y: -------------------------------------------------------------------------------- 1 | class Parser 2 | options no_result_var 3 | rule 4 | stmt : query 5 | { 6 | [val[0], nil, nil] 7 | } 8 | | query RUBY_SCRIPT 9 | { 10 | [val[0], :ruby, val[1]] 11 | } 12 | | query SHELL_SCRIPT 13 | { 14 | [val[0], :shell, val[1]] 15 | } 16 | | query GT STRING_VALUE 17 | { 18 | [val[0], :overwrite, val[2]] 19 | } 20 | | query GTGT STRING_VALUE 21 | { 22 | [val[0], :append, val[2]] 23 | } 24 | | RUBY_SCRIPT 25 | { 26 | [struct(:NULL), :ruby, val[0]] 27 | } 28 | | SHELL_SCRIPT 29 | { 30 | [struct(:NULL), :shell, val[0]] 31 | } 32 | 33 | query : show_stmt 34 | | alter_stmt 35 | | use_stmt 36 | | create_stmt 37 | | drop_stmt 38 | | describe_stmt 39 | | select_stmt 40 | | scan_stmt 41 | | get_stmt 42 | | update_stmt 43 | | delete_stmt 44 | | insert_stmt 45 | | next_stmt 46 | 47 | show_stmt : SHOW TABLES limit_clause like_clause 48 | { 49 | struct(:SHOW_TABLES, :limit => val[2], :like => val[3]) 50 | } 51 | | SHOW TABLE STATUS like_clause 52 | { 53 | struct(:SHOW_TABLE_STATUS, :like => val[3]) 54 | } 55 | | SHOW REGIONS 56 | { 57 | struct(:SHOW_REGIONS) 58 | } 59 | | SHOW CREATE TABLE IDENTIFIER 60 | { 61 | struct(:SHOW_CREATE_TABLE, :table => val[3]) 62 | } 63 | 64 | like_clause : 65 | | LIKE STRING_VALUE 66 | { 67 | val[1] 68 | } 69 | 70 | alter_stmt : alter_table_stmt 71 | | alter_table_index_stmt 72 | 73 | 74 | alter_table_stmt :ALTER TABLE IDENTIFIER capacity_clause 75 | { 76 | struct(:ALTER_TABLE, :table => val[2], :capacity => val[3], :stream => nil) 77 | } 78 | | ALTER TABLE IDENTIFIER stream_clause 79 | { 80 | struct(:ALTER_TABLE, :table => val[2], :capacity => nil, :stream => val[3]) 81 | } 82 | | ALTER TABLE IDENTIFIER capacity_clause stream_clause 83 | { 84 | struct(:ALTER_TABLE, :table => val[2], :capacity => val[3], :stream => val[4]) 85 | } 86 | | ALTER TABLE IDENTIFIER stream_clause capacity_clause 87 | { 88 | struct(:ALTER_TABLE, :table => val[2], :capacity => val[4], :stream => val[3]) 89 | } 90 | 91 | alter_table_index_stmt : ALTER TABLE IDENTIFIER CHANGE GLOBAL INDEX IDENTIFIER capacity_clause 92 | { 93 | struct(:ALTER_TABLE_INDEX, :table => val[2], :action => 'Update', :index_definition => {:name => val[6], :capacity => val[7]}) 94 | } 95 | | ALTER TABLE IDENTIFIER ADD global_index_definition_with_capacity 96 | { 97 | struct(:ALTER_TABLE_INDEX, :table => val[2], :action => 'Create', :index_definition => val[4]) 98 | } 99 | | ALTER TABLE IDENTIFIER DROP GLOBAL INDEX IDENTIFIER 100 | { 101 | struct(:ALTER_TABLE_INDEX, :table => val[2], :action => 'Delete', :index_definition => {:name => val[6]}) 102 | } 103 | 104 | use_stmt : USE IDENTIFIER 105 | { 106 | struct(:USE, :endpoint_or_region => val[1]) 107 | } 108 | 109 | | USE STRING 110 | { 111 | struct(:USE, :endpoint_or_region => val[1]) 112 | } 113 | 114 | create_stmt : CREATE TABLE IDENTIFIER '(' create_definition ')' capacity_stream_clause 115 | { 116 | struct(:CREATE, val[4].merge(:table => val[2], :capacity => val[6][:capacity], :stream => val[6][:stream])) 117 | } 118 | | CREATE TABLE IDENTIFIER LIKE IDENTIFIER 119 | { 120 | struct(:CREATE_LIKE, :table => val[2], :like => val[4], :capacity => nil, :stream => nil) 121 | } 122 | | CREATE TABLE IDENTIFIER LIKE IDENTIFIER stream_clause 123 | { 124 | struct(:CREATE_LIKE, :table => val[2], :like => val[4], :capacity => nil, :stream => val[5]) 125 | } 126 | | CREATE TABLE IDENTIFIER LIKE IDENTIFIER capacity_stream_clause 127 | { 128 | struct(:CREATE_LIKE, :table => val[2], :like => val[4], :capacity => val[5][:capacity], :stream => val[5][:stream]) 129 | } 130 | 131 | capacity_stream_clause : capacity_clause 132 | { 133 | {:capacity => val[0], :stream => nil} 134 | } 135 | | capacity_clause stream_clause 136 | { 137 | {:capacity => val[0], :stream => val[1]} 138 | } 139 | | stream_clause capacity_clause 140 | { 141 | {:capacity => val[1], :stream => val[0]} 142 | } 143 | 144 | stream_clause : STREAM EQ BOOL 145 | { 146 | val[2] 147 | } 148 | | STREAM EQ stream_view_type 149 | { 150 | val[2] 151 | } 152 | 153 | stream_view_type : NEW_IMAGE 154 | | OLD_IMAGE 155 | | NEW_AND_OLD_IMAGES 156 | | KEYS_ONLY 157 | 158 | create_definition : IDENTIFIER attr_type_list HASH 159 | { 160 | {:hash => {:name => val[0], :type => val[1]}, :range => nil, :indices => nil} 161 | } 162 | | IDENTIFIER attr_type_list HASH ',' index_definition_list 163 | { 164 | {:hash => {:name => val[0], :type => val[1]}, :range => nil, :indices => val[4]} 165 | } 166 | | IDENTIFIER attr_type_list HASH ',' IDENTIFIER attr_type_list RANGE 167 | { 168 | {:hash => {:name => val[0], :type => val[1]}, :range => {:name => val[4], :type => val[5]}, :indices => nil} 169 | } 170 | | IDENTIFIER attr_type_list HASH ',' IDENTIFIER attr_type_list RANGE ',' index_definition_list 171 | { 172 | {:hash => {:name => val[0], :type => val[1]}, :range => {:name => val[4], :type => val[5]}, :indices => val[8]} 173 | } 174 | 175 | attr_type_list : STRING 176 | { 177 | 'S' 178 | } 179 | | NUMBER 180 | { 181 | 'N' 182 | } 183 | | BINARY 184 | { 185 | 'B' 186 | } 187 | 188 | capacity_clause : READ EQ NUMBER_VALUE ',' WRITE EQ NUMBER_VALUE 189 | { 190 | {:read => val[2], :write => val[6]} 191 | } 192 | | WRITE EQ NUMBER_VALUE ',' READ EQ NUMBER_VALUE 193 | { 194 | {:read => val[6], :write => val[2]} 195 | } 196 | | strict_capacity_clause 197 | 198 | strict_capacity_clause : READ EQ NUMBER_VALUE WRITE EQ NUMBER_VALUE 199 | { 200 | {:read => val[2], :write => val[5]} 201 | } 202 | | WRITE EQ NUMBER_VALUE READ EQ NUMBER_VALUE 203 | { 204 | {:read => val[5], :write => val[2]} 205 | } 206 | 207 | index_definition_list : index_definition 208 | { 209 | [val[0]] 210 | } 211 | | index_definition_list ',' index_definition 212 | { 213 | val[0] + [val[2]] 214 | } 215 | 216 | index_definition : locat_index_definition 217 | | global_index_definition 218 | 219 | locat_index_definition : INDEX IDENTIFIER '(' IDENTIFIER attr_type_list ')' index_type_definition 220 | { 221 | {:name => val[1], :key => val[3], :type => val[4], :projection => val[6]} 222 | } 223 | 224 | global_index_definition : global_index_definition_without_capacity 225 | | global_index_definition_with_capacity 226 | 227 | global_index_definition_without_capacity : GLOBAL INDEX IDENTIFIER '(' global_index_keys ')' index_type_definition 228 | { 229 | {:name => val[2], :keys => val[4], :projection => val[6], :global => true} 230 | } 231 | 232 | global_index_definition_with_capacity : GLOBAL INDEX IDENTIFIER '(' global_index_keys ')' index_type_definition strict_capacity_clause 233 | { 234 | {:name => val[2], :keys => val[4], :projection => val[6], :capacity => val[7], :global => true} 235 | } 236 | 237 | global_index_keys : IDENTIFIER attr_type_list 238 | { 239 | {:hash => {:key => val[0], :type => val[1]}} 240 | } 241 | | IDENTIFIER attr_type_list ',' IDENTIFIER attr_type_list 242 | { 243 | { 244 | :hash => {:key => val[0], :type => val[1]}, 245 | :range => {:key => val[3], :type => val[4]}, 246 | } 247 | } 248 | 249 | index_type_definition : ALL 250 | { 251 | {:type => 'ALL'} 252 | } 253 | | KEYS_ONLY 254 | { 255 | {:type => 'KEYS_ONLY'} 256 | } 257 | | INCLUDE '(' index_include_attr_list ')' 258 | { 259 | {:type => 'INCLUDE', :attrs => val[2]} 260 | } 261 | 262 | index_include_attr_list : IDENTIFIER 263 | { 264 | [val[0]] 265 | } 266 | | index_include_attr_list ',' IDENTIFIER 267 | { 268 | val[0] + [val[2]] 269 | } 270 | 271 | drop_stmt : DROP TABLE identifier_list 272 | { 273 | struct(:DROP, :tables => val[2]) 274 | } 275 | 276 | describe_stmt : DESCRIBE IDENTIFIER 277 | { 278 | struct(:DESCRIBE, :table => val[1]) 279 | } 280 | | DESC IDENTIFIER 281 | { 282 | struct(:DESCRIBE, :table => val[1]) 283 | } 284 | 285 | select_stmt : SELECT attrs_to_get FROM IDENTIFIER use_index_clause select_where_clause having_clause order_clause limit_clause 286 | { 287 | struct(:SELECT, :attrs => val[1], :table => val[3], :index => val[4], :conds => val[5], :having => val[6], :order_asc => val[7], :limit => val[8], :count => false) 288 | } 289 | | SELECT COUNT '(' '*' ')' FROM IDENTIFIER use_index_clause select_where_clause having_clause order_clause limit_clause 290 | { 291 | struct(:SELECT, :attrs => [], :table => val[6], :index => val[7], :conds => val[8], :having => val[9], :order_asc => val[10], :limit => val[11], :count => true) 292 | } 293 | 294 | scan_stmt : SELECT ALL attrs_to_get FROM IDENTIFIER use_index_clause scan_where_clause limit_clause 295 | { 296 | struct(:SCAN, :attrs => val[2], :table => val[4], :index => val[5], :conds => val[6], :limit => val[7], :count => false, :segment => nil, :total_segments => nil) 297 | } 298 | | SELECT ALL COUNT '(' '*' ')' FROM IDENTIFIER use_index_clause scan_where_clause limit_clause 299 | { 300 | struct(:SCAN, :attrs => [], :table => val[7], :index => val[8], :conds => val[9], :limit => val[10], :count => true, :segment => nil, :total_segments => nil) 301 | } 302 | | SELECT NUMBER_VALUE '/' NUMBER_VALUE attrs_to_get FROM IDENTIFIER use_index_clause scan_where_clause limit_clause 303 | { 304 | struct(:SCAN, :attrs => val[4], :table => val[6], :index => val[7], :conds => val[8], :limit => val[9], :count => false, :segment => val[1], :total_segments => val[3]) 305 | } 306 | 307 | get_stmt : GET attrs_to_get FROM IDENTIFIER update_where_clause 308 | { 309 | struct(:GET, :attrs => val[1], :table => val[3], :conds => val[4]) 310 | } 311 | 312 | attrs_to_get: '*' 313 | { 314 | [] 315 | } 316 | | attrs_list 317 | { 318 | val[0] 319 | } 320 | 321 | attrs_list : IDENTIFIER 322 | { 323 | [val[0]] 324 | } 325 | | attrs_list ',' IDENTIFIER 326 | { 327 | val[0] + [val[2]] 328 | } 329 | 330 | use_index_clause : 331 | | USE INDEX '(' IDENTIFIER ')' 332 | { 333 | val[3] 334 | } 335 | 336 | select_where_clause : 337 | | WHERE select_expr_list 338 | { 339 | val[1] 340 | } 341 | 342 | select_expr_list : select_expr 343 | { 344 | [val[0]] 345 | } 346 | | select_expr_list AND select_expr 347 | { 348 | val[0] + [val[2]] 349 | } 350 | 351 | select_expr : IDENTIFIER select_operator value 352 | { 353 | [val[0], val[1].to_s.upcase.to_sym, [val[2]]] 354 | } 355 | | IDENTIFIER BETWEEN value AND value 356 | { 357 | [val[0], val[1].to_s.upcase.to_sym, [val[2], val[4]]] 358 | } 359 | 360 | select_operator : common_operator 361 | 362 | common_operator : EQ 363 | { 364 | :EQ 365 | } 366 | | LE 367 | { 368 | :LE 369 | } 370 | | LT 371 | { 372 | :LT 373 | } 374 | | GE 375 | { 376 | :GE 377 | } 378 | | GT 379 | { 380 | :GT 381 | } 382 | | BEGINS_WITH 383 | 384 | scan_where_clause : 385 | | WHERE scan_expr_list 386 | { 387 | val[1] 388 | } 389 | 390 | scan_expr_list : scan_expr 391 | { 392 | [val[0]] 393 | } 394 | | scan_expr_list AND scan_expr 395 | { 396 | val[0] + [val[2]] 397 | } 398 | 399 | scan_expr : IDENTIFIER scan_operator value 400 | { 401 | [val[0], val[1].to_s.upcase.to_sym, [val[2]]] 402 | } 403 | | IDENTIFIER IN value_list 404 | { 405 | [val[0], val[1].to_s.upcase.to_sym, val[2]] 406 | } 407 | | IDENTIFIER BETWEEN value AND value 408 | { 409 | [val[0], val[1].to_s.upcase.to_sym, [val[2], val[4]]] 410 | } 411 | | IDENTIFIER IS null_operator 412 | { 413 | [val[0], val[2].to_s.upcase.to_sym, []] 414 | } 415 | 416 | scan_operator : common_operator | contains_operator 417 | | NE 418 | { 419 | :NE 420 | } 421 | 422 | contains_operator : CONTAINS 423 | | NOT CONTAINS 424 | { 425 | :NOT_CONTAINS 426 | } 427 | null_operator : NULL 428 | { 429 | :NULL 430 | } 431 | | NOT NULL 432 | { 433 | :NOT_NULL 434 | } 435 | 436 | order_clause : 437 | | ORDER ASC 438 | { 439 | true 440 | } 441 | | ORDER DESC 442 | { 443 | false 444 | } 445 | 446 | limit_clause : 447 | | LIMIT NUMBER_VALUE 448 | { 449 | val[1] 450 | } 451 | 452 | having_clause : 453 | | HAVING scan_expr_list 454 | { 455 | val[1] 456 | } 457 | 458 | update_stmt : UPDATE IDENTIFIER set_or_add attr_to_update_list update_where_clause 459 | { 460 | struct(:UPDATE, :table => val[1], :action => val[2], :attrs => val[3], :conds => val[4]) 461 | } 462 | | UPDATE ALL IDENTIFIER set_or_add attr_to_update_list scan_where_clause limit_clause 463 | { 464 | struct(:UPDATE_ALL, :table => val[2], :action => val[3], :attrs => val[4], :conds => val[5], :limit => val[6]) 465 | } 466 | | UPDATE IDENTIFIER delete_or_del identifier_list update_where_clause 467 | { 468 | attrs = {} 469 | val[3].each {|i| attrs[i] = true } 470 | struct(:UPDATE, :table => val[1], :action => val[2], :attrs => attrs, :conds => val[4]) 471 | } 472 | | UPDATE ALL IDENTIFIER delete_or_del identifier_list scan_where_clause limit_clause 473 | { 474 | attrs = {} 475 | val[4].each {|i| attrs[i] = true } 476 | struct(:UPDATE_ALL, :table => val[2], :action => val[3], :attrs => attrs, :conds => val[5], :limit => val[6]) 477 | } 478 | 479 | set_or_add : SET 480 | { 481 | :PUT 482 | } 483 | | ADD 484 | { 485 | :ADD 486 | } 487 | 488 | delete_or_del : DELETE 489 | { 490 | :DELETE 491 | } 492 | | DEL 493 | { 494 | :DELETE 495 | } 496 | 497 | attr_to_update_list : attr_to_update 498 | { 499 | [val[0]] 500 | } 501 | | attr_to_update_list ',' attr_to_update 502 | { 503 | val[0] + [val[2]] 504 | } 505 | 506 | attr_to_update : IDENTIFIER EQ value 507 | { 508 | [val[0], val[2]] 509 | } 510 | 511 | update_where_clause : WHERE update_expr_list 512 | { 513 | val[1] 514 | } 515 | 516 | update_expr_list : update_expr 517 | { 518 | [val[0]] 519 | } 520 | | update_expr_list AND update_expr 521 | { 522 | val[0] + [val[2]] 523 | } 524 | 525 | update_expr : IDENTIFIER EQ value 526 | { 527 | [val[0], val[2]] 528 | } 529 | 530 | delete_stmt : DELETE FROM IDENTIFIER update_where_clause 531 | { 532 | struct(:DELETE, :table => val[2], :conds => val[3]) 533 | } 534 | | DELETE ALL FROM IDENTIFIER scan_where_clause limit_clause 535 | { 536 | struct(:DELETE_ALL, :table => val[3], :conds => val[4], :limit => val[5]) 537 | } 538 | 539 | insert_stmt : INSERT INTO IDENTIFIER '(' attr_to_insert_list ')' VALUES insert_value_clause 540 | { 541 | struct(:INSERT, :table => val[2], :attrs => val[4], :values => val[7]) 542 | } 543 | | INSERT INTO IDENTIFIER select_stmt 544 | { 545 | struct(:INSERT_SELECT, :table => val[2], :select => val[3]) 546 | } 547 | | INSERT INTO IDENTIFIER scan_stmt 548 | { 549 | struct(:INSERT_SCAN, :table => val[2], :select => val[3]) 550 | } 551 | 552 | attr_to_insert_list : identifier_list 553 | 554 | insert_value_clause : '(' insert_value_list ')' 555 | { 556 | [val[1]] 557 | } 558 | | insert_value_clause ',' '(' insert_value_list ')' 559 | { 560 | val[0] + [val[3]] 561 | } 562 | 563 | insert_value_list : value 564 | { 565 | [val[0]] 566 | } 567 | | insert_value_list ',' value 568 | { 569 | val[0] + [val[2]] 570 | } 571 | 572 | next_stmt : NEXT 573 | { 574 | struct(:NEXT) 575 | } 576 | 577 | value : single_value 578 | | value_list 579 | | list 580 | | map 581 | | BOOL 582 | | NULL 583 | 584 | list : '[' ']' 585 | | '[' list_items ']' 586 | { 587 | val[1] 588 | } 589 | 590 | list_items : value 591 | { 592 | [val[0]] 593 | } 594 | | list_items ',' value 595 | { 596 | val[0] + [val[2]] 597 | } 598 | 599 | map : '{' '}' 600 | { 601 | {} 602 | } 603 | | '{' map_items '}' 604 | { 605 | val[1] 606 | } 607 | 608 | map_items : map_item 609 | | map_items ',' map_item 610 | { 611 | val[0].merge(val[2]) 612 | } 613 | 614 | map_item : IDENTIFIER ':' value 615 | { 616 | {val[0] => val[2]} 617 | } 618 | | STRING_VALUE ':' value 619 | { 620 | {val[0] => val[2]} 621 | } 622 | 623 | single_value : NUMBER_VALUE 624 | | STRING_VALUE 625 | | BINARY_VALUE 626 | 627 | value_list : '(' number_list ')' 628 | { 629 | val[1] 630 | } 631 | | '(' string_list ')' 632 | { 633 | val[1] 634 | } 635 | | '(' binary_list ')' 636 | { 637 | val[1] 638 | } 639 | 640 | number_list : NUMBER_VALUE 641 | { 642 | Set[val[0]] 643 | } 644 | | number_list ',' NUMBER_VALUE 645 | { 646 | val[0] + Set[val[2]] 647 | } 648 | 649 | string_list : STRING_VALUE 650 | { 651 | Set[val[0]] 652 | } 653 | | string_list ',' STRING_VALUE 654 | { 655 | val[0] + Set[val[2]] 656 | } 657 | 658 | binary_list : BINARY_VALUE 659 | { 660 | Set[val[0]] 661 | } 662 | | binary_list ',' BINARY_VALUE 663 | { 664 | val[0] + Set[val[2]] 665 | } 666 | 667 | identifier_list : IDENTIFIER 668 | { 669 | [val[0]] 670 | } 671 | | identifier_list ',' IDENTIFIER 672 | { 673 | val[0] + [val[2]] 674 | } 675 | 676 | ---- header 677 | 678 | require 'strscan' 679 | require 'ddbcli/ddb-binary' 680 | require 'set' 681 | 682 | module DynamoDB 683 | 684 | ---- inner 685 | 686 | KEYWORDS = %w( 687 | ADD 688 | ALL 689 | ALTER 690 | AND 691 | ASC 692 | BEGINS_WITH 693 | BETWEEN 694 | BINARY 695 | CHANGE 696 | CREATE 697 | CONTAINS 698 | COUNT 699 | DELETE 700 | DEL 701 | DESCRIBE 702 | DESC 703 | DROP 704 | FROM 705 | GET 706 | GLOBAL 707 | HASH 708 | HAVING 709 | INCLUDE 710 | INDEX 711 | INSERT 712 | INTO 713 | IN 714 | IS 715 | KEYS_ONLY 716 | LIKE 717 | LIMIT 718 | NEW_AND_OLD_IMAGES 719 | NEW_IMAGE 720 | NEXT 721 | NOT 722 | NUMBER 723 | OLD_IMAGE 724 | ORDER 725 | RANGE 726 | READ 727 | REGIONS 728 | SELECT 729 | SET 730 | SHOW 731 | STATUS 732 | STREAM 733 | STRING 734 | TABLES 735 | TABLE 736 | UPDATE 737 | VALUES 738 | WHERE 739 | WRITE 740 | USE 741 | ) 742 | 743 | KEYWORD_REGEXP = Regexp.compile("(?:#{KEYWORDS.join '|'})\\b", Regexp::IGNORECASE) 744 | 745 | def initialize(obj) 746 | src = obj.is_a?(IO) ? obj.read : obj.to_s 747 | @ss = StringScanner.new(src) 748 | end 749 | 750 | @@structs = {} 751 | 752 | def struct(name, attrs = {}) 753 | unless (clazz = @@structs[name]) 754 | clazz = attrs.empty? ? Struct.new(name.to_s) : Struct.new(name.to_s, *attrs.keys) 755 | @@structs[name] = clazz 756 | end 757 | 758 | obj = clazz.new 759 | 760 | attrs.each do |key, val| 761 | obj.send("#{key}=", val) 762 | end 763 | 764 | return obj 765 | end 766 | private :struct 767 | 768 | def scan 769 | tok = nil 770 | @prev_tokens = [] 771 | 772 | until @ss.eos? 773 | if (tok = @ss.scan /\s+/) 774 | # nothing to do 775 | elsif (tok = @ss.scan /(?:>>|<>|!=|>=|<=|>|<|=)/) 776 | sym = { 777 | '>>' => :GTGT, 778 | '<>' => :NE, 779 | '!=' => :NE, 780 | '>=' => :GE, 781 | '<=' => :LE, 782 | '>' => :GT, 783 | '<' => :LT, 784 | '=' => :EQ, 785 | }.fetch(tok) 786 | yield [sym, tok] 787 | elsif (tok = @ss.check KEYWORD_REGEXP) and @ss.rest.slice(tok.length) !~ /\A\w/ 788 | tok = @ss.scan KEYWORD_REGEXP 789 | yield [tok.upcase.to_sym, tok] 790 | elsif (tok = @ss.check /NULL/i) and @ss.rest.slice(tok.length) !~ /\A\w/ 791 | tok = @ss.scan /NULL/i 792 | yield [:NULL, nil] 793 | elsif (tok = @ss.scan /`(?:[^`]|``)*`/) 794 | yield [:IDENTIFIER, tok.slice(1...-1).gsub(/``/, '`')] 795 | elsif (tok = @ss.scan /x'(?:[^']|'')*'/) #' 796 | hex = tok.slice(2...-1).gsub(/''/, "'") 797 | bin = DynamoDB::Binary.new([hex].pack('H*')) 798 | yield [:BINARY_VALUE, bin] 799 | elsif (tok = @ss.scan /x"(?:[^"]|"")*"/) #" 800 | hex = tok.slice(2...-1).gsub(/""/, '"') 801 | bin = DynamoDB::Binary.new([hex].pack('H*')) 802 | yield [:BINARY_VALUE, bin] 803 | elsif (tok = @ss.scan /'(?:[^']|'')*'/) #' 804 | yield [:STRING_VALUE, tok.slice(1...-1).gsub(/''/, "'")] 805 | elsif (tok = @ss.scan /"(?:[^"]|"")*"/) #" 806 | yield [:STRING_VALUE, tok.slice(1...-1).gsub(/""/, '"')] 807 | elsif (tok = @ss.scan /\d+(?:\.\d+)?/) 808 | yield [:NUMBER_VALUE, (tok =~ /\./ ? tok.to_f : tok.to_i)] 809 | elsif (tok = @ss.scan /[,\(\)\*\/\[\]\{\}:]/) 810 | yield [tok, tok] 811 | elsif (tok = @ss.scan /\|(?:.*)/) 812 | yield [:RUBY_SCRIPT, tok.slice(1..-1)] 813 | elsif (tok = @ss.scan /\|(?:.*)/) 814 | yield [:RUBY_SCRIPT, tok.slice(1..-1)] 815 | elsif (tok = @ss.scan /\!(?:.*)/) 816 | yield [:SHELL_SCRIPT, tok.slice(1..-1)] 817 | elsif (tok = @ss.scan %r|[-.0-9a-z_]*|i) 818 | if ['true', 'false'].include?(tok) 819 | yield [:BOOL, 'true' == tok] 820 | else 821 | yield [:IDENTIFIER, tok] 822 | end 823 | else 824 | raise_error(tok, @prev_tokens, @ss) 825 | end 826 | 827 | @prev_tokens << tok 828 | end 829 | 830 | yield [false, ''] 831 | end 832 | private :scan 833 | 834 | def raise_error(error_value, prev_tokens, scanner) 835 | errmsg = ["__#{error_value}__"] 836 | 837 | if prev_tokens and not prev_tokens.empty? 838 | toks = prev_tokens.reverse[0, 5].reverse 839 | toks.unshift('...') if prev_tokens.length > toks.length 840 | errmsg.unshift(toks.join.strip) 841 | end 842 | 843 | if scanner and not (rest = (scanner.rest || '').strip).empty? 844 | str = rest[0, 16] 845 | str += '...' if rest.length > str.length 846 | errmsg << str 847 | end 848 | 849 | raise Racc::ParseError, ('parse error on value: %s' % errmsg.join(' ')) 850 | end 851 | private :raise_error 852 | 853 | def parse 854 | yyparse self, :scan 855 | end 856 | 857 | def on_error(error_token_id, error_value, value_stack) 858 | raise_error(error_value, @prev_tokens, @ss) 859 | end 860 | 861 | def self.parse(obj) 862 | self.new(obj).parse 863 | end 864 | 865 | ---- footer 866 | 867 | end # DynamoDB 868 | -------------------------------------------------------------------------------- /lib/ddbcli/version.rb: -------------------------------------------------------------------------------- 1 | module DynamoDB 2 | VERSION = "0.6.0" 3 | end 4 | -------------------------------------------------------------------------------- /spec/ddbcli_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'ddbcli' do 2 | it 'version' do 3 | out = ddbcli(nil, ['-v']) 4 | expect(out).to match /ddbcli \d+\.\d+\.\d+/ 5 | end 6 | 7 | it 'show tables' do 8 | ddbcli(<<-'EOS') 9 | CREATE TABLE `foo` ( 10 | `id` STRING HASH, 11 | `val` STRING RANGE 12 | ) read=2 write=2 13 | EOS 14 | 15 | out = ddbcli('show tables') 16 | out = JSON.parse(out) 17 | expect(out).to eq(['foo']) 18 | end 19 | 20 | it 'create table (hash only)' do 21 | ddbcli(<<-'EOS') 22 | CREATE TABLE `foo` ( 23 | `id` NUMBER HASH 24 | ) read=2 write=2 25 | EOS 26 | 27 | out = ddbcli('desc foo') 28 | out = JSON.parse(out) 29 | out.delete('CreationDateTime') 30 | 31 | expect(out).to eq( 32 | {"AttributeDefinitions"=>[{"AttributeName"=>"id", "AttributeType"=>"N"}], 33 | "TableArn"=>"arn:aws:dynamodb:ddblocal:000000000000:table/foo", 34 | "TableName"=>"foo", 35 | "KeySchema"=>[{"AttributeName"=>"id", "KeyType"=>"HASH"}], 36 | "TableStatus"=>"ACTIVE", 37 | "ProvisionedThroughput"=> 38 | {"LastIncreaseDateTime"=>0.0, 39 | "LastDecreaseDateTime"=>0.0, 40 | "NumberOfDecreasesToday"=>0, 41 | "ReadCapacityUnits"=>2, 42 | "WriteCapacityUnits"=>2}, 43 | "TableSizeBytes"=>0, 44 | "ItemCount"=>0} 45 | ) 46 | end 47 | 48 | it 'create table (hash and range)' do 49 | ddbcli(<<-'EOS') 50 | CREATE TABLE `foo` ( 51 | `id` NUMBER HASH, 52 | `val` STRING RANGE 53 | ) read=2 write=2 54 | EOS 55 | 56 | out = ddbcli('desc foo') 57 | out = JSON.parse(out) 58 | out.delete('CreationDateTime') 59 | 60 | expect(out).to eq( 61 | {"AttributeDefinitions"=> 62 | [{"AttributeName"=>"id", "AttributeType"=>"N"}, 63 | {"AttributeName"=>"val", "AttributeType"=>"S"}], 64 | "TableArn"=>"arn:aws:dynamodb:ddblocal:000000000000:table/foo", 65 | "TableName"=>"foo", 66 | "KeySchema"=> 67 | [{"AttributeName"=>"id", "KeyType"=>"HASH"}, 68 | {"AttributeName"=>"val", "KeyType"=>"RANGE"}], 69 | "TableStatus"=>"ACTIVE", 70 | "ProvisionedThroughput"=> 71 | {"LastIncreaseDateTime"=>0.0, 72 | "LastDecreaseDateTime"=>0.0, 73 | "NumberOfDecreasesToday"=>0, 74 | "ReadCapacityUnits"=>2, 75 | "WriteCapacityUnits"=>2}, 76 | "TableSizeBytes"=>0, 77 | "ItemCount"=>0} 78 | ) 79 | end 80 | 81 | it 'create table with LSI' do 82 | ddbcli(<<-'EOS') 83 | CREATE TABLE `foo` ( 84 | `id` NUMBER HASH, 85 | `val` STRING RANGE, 86 | INDEX `idx_bar` (`val2` STRING) ALL 87 | ) read=2 write=2 88 | EOS 89 | 90 | out = ddbcli('desc foo') 91 | out = JSON.parse(out) 92 | out.delete('CreationDateTime') 93 | 94 | expect(out).to eq( 95 | {"AttributeDefinitions"=> 96 | [{"AttributeName"=>"id", "AttributeType"=>"N"}, 97 | {"AttributeName"=>"val", "AttributeType"=>"S"}, 98 | {"AttributeName"=>"val2", "AttributeType"=>"S"}], 99 | "TableArn"=>"arn:aws:dynamodb:ddblocal:000000000000:table/foo", 100 | "TableName"=>"foo", 101 | "KeySchema"=> 102 | [{"AttributeName"=>"id", "KeyType"=>"HASH"}, 103 | {"AttributeName"=>"val", "KeyType"=>"RANGE"}], 104 | "TableStatus"=>"ACTIVE", 105 | "ProvisionedThroughput"=> 106 | {"LastIncreaseDateTime"=>0.0, 107 | "LastDecreaseDateTime"=>0.0, 108 | "NumberOfDecreasesToday"=>0, 109 | "ReadCapacityUnits"=>2, 110 | "WriteCapacityUnits"=>2}, 111 | "TableSizeBytes"=>0, 112 | "ItemCount"=>0, 113 | "LocalSecondaryIndexes"=> 114 | [{"IndexArn"=>"arn:aws:dynamodb:ddblocal:000000000000:table/foo/index/idx_bar", 115 | "IndexName"=>"idx_bar", 116 | "KeySchema"=> 117 | [{"AttributeName"=>"id", "KeyType"=>"HASH"}, 118 | {"AttributeName"=>"val2", "KeyType"=>"RANGE"}], 119 | "Projection"=>{"ProjectionType"=>"ALL"}, 120 | "IndexSizeBytes"=>0, 121 | "ItemCount"=>0}]} 122 | ) 123 | end 124 | 125 | it 'create table with GSI' do 126 | ddbcli(<<-'EOS') 127 | CREATE TABLE `foo` ( 128 | `id` NUMBER HASH, 129 | `val` STRING RANGE, 130 | GLOBAL INDEX `idx_bar` (`val2` STRING) ALL read=1 write=1 131 | ) read=2 write=2 132 | EOS 133 | 134 | out = ddbcli('desc foo') 135 | out = JSON.parse(out) 136 | out.delete('CreationDateTime') 137 | 138 | expect(out).to eq( 139 | {"AttributeDefinitions"=> 140 | [{"AttributeName"=>"id", "AttributeType"=>"N"}, 141 | {"AttributeName"=>"val", "AttributeType"=>"S"}, 142 | {"AttributeName"=>"val2", "AttributeType"=>"S"}], 143 | "TableArn"=>"arn:aws:dynamodb:ddblocal:000000000000:table/foo", 144 | "TableName"=>"foo", 145 | "KeySchema"=> 146 | [{"AttributeName"=>"id", "KeyType"=>"HASH"}, 147 | {"AttributeName"=>"val", "KeyType"=>"RANGE"}], 148 | "TableStatus"=>"ACTIVE", 149 | "ProvisionedThroughput"=> 150 | {"LastIncreaseDateTime"=>0.0, 151 | "LastDecreaseDateTime"=>0.0, 152 | "NumberOfDecreasesToday"=>0, 153 | "ReadCapacityUnits"=>2, 154 | "WriteCapacityUnits"=>2}, 155 | "TableSizeBytes"=>0, 156 | "ItemCount"=>0, 157 | "GlobalSecondaryIndexes"=> 158 | [{"IndexArn"=>"arn:aws:dynamodb:ddblocal:000000000000:table/foo/index/idx_bar", 159 | "IndexName"=>"idx_bar", 160 | "KeySchema"=>[{"AttributeName"=>"val2", "KeyType"=>"HASH"}], 161 | "Projection"=>{"ProjectionType"=>"ALL"}, 162 | "IndexStatus"=>"ACTIVE", 163 | "ProvisionedThroughput"=>{"ReadCapacityUnits"=>1, "WriteCapacityUnits"=>1}, 164 | "IndexSizeBytes"=>0, 165 | "ItemCount"=>0}]} 166 | ) 167 | end 168 | 169 | it 'alter table' do 170 | ddbcli(<<-'EOS') 171 | CREATE TABLE `foo` ( 172 | `id` NUMBER HASH, 173 | `val` STRING RANGE, 174 | GLOBAL INDEX `idx_bar` (`val2` STRING) ALL read=1 write=1 175 | ) read=2 write=2 176 | EOS 177 | 178 | out = ddbcli('alter table foo read=4 write=4') 179 | 180 | out = ddbcli('desc foo') 181 | out = JSON.parse(out) 182 | out.delete('CreationDateTime') 183 | 184 | expect(out).to eq( 185 | {"AttributeDefinitions"=> 186 | [{"AttributeName"=>"id", "AttributeType"=>"N"}, 187 | {"AttributeName"=>"val", "AttributeType"=>"S"}, 188 | {"AttributeName"=>"val2", "AttributeType"=>"S"}], 189 | "TableArn"=>"arn:aws:dynamodb:ddblocal:000000000000:table/foo", 190 | "TableName"=>"foo", 191 | "KeySchema"=> 192 | [{"AttributeName"=>"id", "KeyType"=>"HASH"}, 193 | {"AttributeName"=>"val", "KeyType"=>"RANGE"}], 194 | "TableStatus"=>"ACTIVE", 195 | "ProvisionedThroughput"=> 196 | {"LastIncreaseDateTime"=>0.0, 197 | "LastDecreaseDateTime"=>0.0, 198 | "NumberOfDecreasesToday"=>0, 199 | "ReadCapacityUnits"=>4, 200 | "WriteCapacityUnits"=>4}, 201 | "TableSizeBytes"=>0, 202 | "ItemCount"=>0, 203 | "GlobalSecondaryIndexes"=> 204 | [{"IndexArn"=>"arn:aws:dynamodb:ddblocal:000000000000:table/foo/index/idx_bar", 205 | "IndexName"=>"idx_bar", 206 | "KeySchema"=>[{"AttributeName"=>"val2", "KeyType"=>"HASH"}], 207 | "Projection"=>{"ProjectionType"=>"ALL"}, 208 | "IndexStatus"=>"ACTIVE", 209 | "ProvisionedThroughput"=>{"ReadCapacityUnits"=>1, "WriteCapacityUnits"=>1}, 210 | "IndexSizeBytes"=>0, 211 | "ItemCount"=>0}]} 212 | ) 213 | end 214 | 215 | it 'create table like' do 216 | ddbcli(<<-'EOS') 217 | CREATE TABLE `foo` ( 218 | `id` NUMBER HASH, 219 | `val` STRING RANGE, 220 | GLOBAL INDEX `idx_bar` (`val2` STRING) ALL read=1 write=1 221 | ) read=2 write=2 222 | EOS 223 | 224 | ddbcli('create table foo2 like foo') 225 | 226 | out = ddbcli('desc foo2') 227 | out = JSON.parse(out) 228 | out.delete('CreationDateTime') 229 | 230 | expect(out).to eq( 231 | {"AttributeDefinitions"=> 232 | [{"AttributeName"=>"id", "AttributeType"=>"N"}, 233 | {"AttributeName"=>"val", "AttributeType"=>"S"}, 234 | {"AttributeName"=>"val2", "AttributeType"=>"S"}], 235 | "TableName"=>"foo2", 236 | "KeySchema"=> 237 | [{"AttributeName"=>"id", "KeyType"=>"HASH"}, 238 | {"AttributeName"=>"val", "KeyType"=>"RANGE"}], 239 | "TableStatus"=>"ACTIVE", 240 | "ProvisionedThroughput"=> 241 | {"LastIncreaseDateTime"=>0.0, 242 | "LastDecreaseDateTime"=>0.0, 243 | "NumberOfDecreasesToday"=>0, 244 | "ReadCapacityUnits"=>2, 245 | "WriteCapacityUnits"=>2}, 246 | "TableArn"=>"arn:aws:dynamodb:ddblocal:000000000000:table/foo2", 247 | "TableSizeBytes"=>0, 248 | "ItemCount"=>0, 249 | "GlobalSecondaryIndexes"=> 250 | [{"IndexArn"=>"arn:aws:dynamodb:ddblocal:000000000000:table/foo2/index/idx_bar", 251 | "IndexName"=>"idx_bar", 252 | "KeySchema"=>[{"AttributeName"=>"val2", "KeyType"=>"HASH"}], 253 | "Projection"=>{"ProjectionType"=>"ALL"}, 254 | "IndexStatus"=>"ACTIVE", 255 | "ProvisionedThroughput"=>{"ReadCapacityUnits"=>1, "WriteCapacityUnits"=>1}, 256 | "IndexSizeBytes"=>0, 257 | "ItemCount"=>0}]} 258 | ) 259 | end 260 | 261 | it 'drop table' do 262 | ddbcli(<<-'EOS') 263 | CREATE TABLE `foo` ( 264 | `id` NUMBER HASH, 265 | `val` STRING RANGE, 266 | GLOBAL INDEX `idx_bar` (`val2` STRING) ALL read=1 write=1 267 | ) read=2 write=2; 268 | 269 | CREATE TABLE `foo2` ( 270 | `id` NUMBER HASH, 271 | `val` STRING RANGE, 272 | GLOBAL INDEX `idx_bar` (`val2` STRING) ALL read=1 write=1 273 | ) read=2 write=2; 274 | EOS 275 | 276 | ddbcli('drop table foo') 277 | 278 | out = ddbcli('show tables') 279 | out = JSON.parse(out) 280 | expect(out).to eq(['foo2']) 281 | end 282 | 283 | it 'drop tables' do 284 | ddbcli(<<-'EOS') 285 | CREATE TABLE `foo` ( 286 | `id` NUMBER HASH, 287 | `val` STRING RANGE, 288 | GLOBAL INDEX `idx_bar` (`val2` STRING) ALL read=1 write=1 289 | ) read=2 write=2; 290 | 291 | CREATE TABLE `foo2` ( 292 | `id` NUMBER HASH, 293 | `val` STRING RANGE, 294 | GLOBAL INDEX `idx_bar` (`val2` STRING) ALL read=1 write=1 295 | ) read=2 write=2; 296 | EOS 297 | 298 | ddbcli('drop table foo, foo2') 299 | 300 | out = ddbcli('show tables') 301 | out = JSON.parse(out) 302 | expect(out).to eq([]) 303 | end 304 | end 305 | -------------------------------------------------------------------------------- /spec/delete_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'ddbcli' do 2 | context 'delete' do 3 | before do 4 | ddbcli(<<-'EOS') 5 | CREATE TABLE `employees` ( 6 | `emp_no` NUMBER HASH, 7 | `birth_date` STRING RANGE 8 | ) read=2 write=2; 9 | 10 | INSERT INTO `employees` 11 | (`birth_date`, `emp_no`, `first_name`, `gender`, `hire_date`, `last_name`) 12 | VALUES 13 | ("1956-05-15",2,"Cathie","M","1997-04-11","Keohane"), 14 | ("1957-09-16",4,"Aemilian","M","1990-09-25","Roccetti"), 15 | ("1959-07-01",4,"Dayanand","M","1989-09-01","Waterhouse"), 16 | ("1964-12-29",7,"Mack","F","1988-02-25","Hambrick"), 17 | ("1962-07-06",4,"Tristan","M","1985-07-20","Biran"), 18 | ("1964-04-30",2,"Akhilish","F","1985-03-21","Isaak"), 19 | ("1963-07-14",1,"Katsuyuki","F","1989-12-28","Weedon"), 20 | ("1961-10-19",2,"Collette","M","1993-02-26","Ghemri"), 21 | ("1955-04-26",4,"Zine","M","1991-06-19","Butner"), 22 | ("1961-04-28",4,"Selwyn","F","1994-08-12","Parascandalo"); 23 | EOS 24 | end 25 | 26 | it 'delete by pk' do 27 | ddbcli('delete from employees where emp_no = 4 and birth_date = "1961-04-28"') 28 | out = ddbcli('select all * from employees') 29 | out = JSON.parse(out) 30 | 31 | expect(out).to eq( 32 | [{"birth_date"=>"1956-05-15", 33 | "emp_no"=>2, 34 | "first_name"=>"Cathie", 35 | "gender"=>"M", 36 | "hire_date"=>"1997-04-11", 37 | "last_name"=>"Keohane"}, 38 | {"birth_date"=>"1961-10-19", 39 | "emp_no"=>2, 40 | "first_name"=>"Collette", 41 | "gender"=>"M", 42 | "hire_date"=>"1993-02-26", 43 | "last_name"=>"Ghemri"}, 44 | {"birth_date"=>"1964-04-30", 45 | "emp_no"=>2, 46 | "first_name"=>"Akhilish", 47 | "gender"=>"F", 48 | "hire_date"=>"1985-03-21", 49 | "last_name"=>"Isaak"}, 50 | {"birth_date"=>"1963-07-14", 51 | "emp_no"=>1, 52 | "first_name"=>"Katsuyuki", 53 | "gender"=>"F", 54 | "hire_date"=>"1989-12-28", 55 | "last_name"=>"Weedon"}, 56 | {"birth_date"=>"1964-12-29", 57 | "emp_no"=>7, 58 | "first_name"=>"Mack", 59 | "gender"=>"F", 60 | "hire_date"=>"1988-02-25", 61 | "last_name"=>"Hambrick"}, 62 | {"birth_date"=>"1955-04-26", 63 | "emp_no"=>4, 64 | "first_name"=>"Zine", 65 | "gender"=>"M", 66 | "hire_date"=>"1991-06-19", 67 | "last_name"=>"Butner"}, 68 | {"birth_date"=>"1957-09-16", 69 | "emp_no"=>4, 70 | "first_name"=>"Aemilian", 71 | "gender"=>"M", 72 | "hire_date"=>"1990-09-25", 73 | "last_name"=>"Roccetti"}, 74 | {"birth_date"=>"1959-07-01", 75 | "emp_no"=>4, 76 | "first_name"=>"Dayanand", 77 | "gender"=>"M", 78 | "hire_date"=>"1989-09-01", 79 | "last_name"=>"Waterhouse"}, 80 | {"birth_date"=>"1962-07-06", 81 | "emp_no"=>4, 82 | "first_name"=>"Tristan", 83 | "gender"=>"M", 84 | "hire_date"=>"1985-07-20", 85 | "last_name"=>"Biran"}] 86 | ) 87 | end 88 | 89 | it 'delete all' do 90 | ddbcli('delete all from employees where hire_date <= "1990-01-01"') 91 | out = ddbcli('select all * from employees') 92 | out = JSON.parse(out) 93 | 94 | expect(out).to eq( 95 | [{"birth_date"=>"1956-05-15", 96 | "emp_no"=>2, 97 | "first_name"=>"Cathie", 98 | "gender"=>"M", 99 | "hire_date"=>"1997-04-11", 100 | "last_name"=>"Keohane"}, 101 | {"birth_date"=>"1961-10-19", 102 | "emp_no"=>2, 103 | "first_name"=>"Collette", 104 | "gender"=>"M", 105 | "hire_date"=>"1993-02-26", 106 | "last_name"=>"Ghemri"}, 107 | {"birth_date"=>"1955-04-26", 108 | "emp_no"=>4, 109 | "first_name"=>"Zine", 110 | "gender"=>"M", 111 | "hire_date"=>"1991-06-19", 112 | "last_name"=>"Butner"}, 113 | {"birth_date"=>"1957-09-16", 114 | "emp_no"=>4, 115 | "first_name"=>"Aemilian", 116 | "gender"=>"M", 117 | "hire_date"=>"1990-09-25", 118 | "last_name"=>"Roccetti"}, 119 | {"birth_date"=>"1961-04-28", 120 | "emp_no"=>4, 121 | "first_name"=>"Selwyn", 122 | "gender"=>"F", 123 | "hire_date"=>"1994-08-12", 124 | "last_name"=>"Parascandalo"}] 125 | ) 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /spec/insert_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'ddbcli' do 2 | context 'insert' do 3 | before do 4 | ddbcli(<<-'EOS') 5 | CREATE TABLE `employees` ( 6 | `emp_no` NUMBER HASH, 7 | `birth_date` STRING RANGE 8 | ) read=2 write=2; 9 | EOS 10 | end 11 | 12 | it 'insert array' do 13 | ddbcli(<<-'EOS') 14 | insert into employees ( 15 | emp_no, 16 | birth_date, 17 | num, 18 | str, 19 | bin, 20 | num_array, 21 | str_array, 22 | bin_array 23 | ) values ( 24 | 1, 25 | '1977-11-11', 26 | 2, 27 | 'XXX', 28 | x'cafebabe', 29 | (1, 2, 3), 30 | ("A", "B", "C"), 31 | (x"aa", x"bb", x"cc") 32 | ) 33 | EOS 34 | 35 | out = ddbcli('select all * from employees') 36 | out = JSON.parse(out) 37 | 38 | expect(out).to eq( 39 | [{"bin"=>"yv66vg==", 40 | "bin_array"=>["qg==", "uw==", "zA=="], 41 | "birth_date"=>"1977-11-11", 42 | "emp_no"=>1, 43 | "num"=>2, 44 | "num_array"=>[1, 2, 3], 45 | "str"=>"XXX", 46 | "str_array"=>["A", "B", "C"]}] 47 | ) 48 | end 49 | 50 | it 'insert list/map/bool/null' do 51 | ddbcli(<<-'EOS') 52 | insert into employees ( 53 | emp_no, 54 | birth_date, 55 | bool1, 56 | bool2, 57 | null_val, 58 | list, 59 | map 60 | ) values ( 61 | 1, 62 | '1977-11-11', 63 | true, 64 | false, 65 | null, 66 | [1, "2", 3, ["FOO", "BAR"], {foo: "foo", bar: 100}], 67 | {foo: "foo", "bar": [1, 2, 3, {zoo: "zoo"}]} 68 | ) 69 | EOS 70 | 71 | out = ddbcli('select all * from employees') 72 | out = JSON.parse(out) 73 | 74 | expect(out).to eq( 75 | [{"birth_date"=>"1977-11-11", 76 | "bool1"=>true, 77 | "bool2"=>false, 78 | "emp_no"=>1, 79 | "list"=>[1, "2", 3, ["FOO", "BAR"], {"bar"=>100, "foo"=>"foo"}], 80 | "map"=>{"bar"=>[1, 2, 3, {"zoo"=>"zoo"}], "foo"=>"foo"}, 81 | "null_val"=>nil}] 82 | ) 83 | end 84 | 85 | it 'insert error (1)' do 86 | sql = <<-'EOS' 87 | insert into employees ( 88 | emp_no, 89 | birth_date, 90 | num, 91 | str 92 | ) values ( 93 | 1, 94 | '1977-11-11', 95 | 2 96 | ) 97 | EOS 98 | 99 | expect { 100 | ddbcli(sql) 101 | }.to raise_error(%|// number of attribute name and value are different: ["emp_no", "birth_date", "num", "str"] != [1, "1977-11-11", 2]\n\n|) 102 | end 103 | 104 | it 'insert error (2)' do 105 | sql = <<-'EOS' 106 | insert into employees ( 107 | emp_no, 108 | birth_date, 109 | num, 110 | str 111 | ) values ( 112 | 1, 113 | '1977-11-11', 114 | 2, 115 | 'XXX', 116 | 'YYY' 117 | ) 118 | EOS 119 | 120 | expect { 121 | ddbcli(sql) 122 | }.to raise_error(%|// number of attribute name and value are different: ["emp_no", "birth_date", "num", "str"] != [1, "1977-11-11", 2, "XXX", "YYY"]\n\n|) 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /spec/select_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'ddbcli' do 2 | context 'select' do 3 | before do 4 | ddbcli(<<-'EOS') 5 | CREATE TABLE `employees` ( 6 | `emp_no` NUMBER HASH, 7 | `birth_date` STRING RANGE, 8 | INDEX `local_idx` (`gender` STRING) ALL, 9 | GLOBAL INDEX `global_idx` (`gender` STRING, `hire_date` STRING) ALL 10 | ) read=2 write=2; 11 | 12 | INSERT INTO `employees` 13 | (`birth_date`, `emp_no`, `first_name`, `gender`, `hire_date`, `last_name`) 14 | VALUES 15 | ("1956-05-15",2,"Cathie","M","1997-04-11","Keohane"), 16 | ("1957-09-16",4,"Aemilian","M","1990-09-25","Roccetti"), 17 | ("1959-07-01",4,"Dayanand","M","1989-09-01","Waterhouse"), 18 | ("1964-12-29",7,"Mack","F","1988-02-25","Hambrick"), 19 | ("1962-07-06",4,"Tristan","M","1985-07-20","Biran"), 20 | ("1964-04-30",2,"Akhilish","F","1985-03-21","Isaak"), 21 | ("1963-07-14",1,"Katsuyuki","F","1989-12-28","Weedon"), 22 | ("1961-10-19",2,"Collette","M","1993-02-26","Ghemri"), 23 | ("1955-04-26",4,"Zine","M","1991-06-19","Butner"), 24 | ("1961-04-28",4,"Selwyn","F","1994-08-12","Parascandalo"); 25 | EOS 26 | end 27 | 28 | it 'select using GSI' do 29 | out = ddbcli('select * from employees use index (global_idx) where gender = "M" and hire_date < "1990-01-01"') 30 | out = JSON.parse(out) 31 | 32 | expect(out).to eq( 33 | [{"birth_date"=>"1962-07-06", 34 | "emp_no"=>4, 35 | "first_name"=>"Tristan", 36 | "gender"=>"M", 37 | "hire_date"=>"1985-07-20", 38 | "last_name"=>"Biran"}, 39 | {"birth_date"=>"1959-07-01", 40 | "emp_no"=>4, 41 | "first_name"=>"Dayanand", 42 | "gender"=>"M", 43 | "hire_date"=>"1989-09-01", 44 | "last_name"=>"Waterhouse"}] 45 | ) 46 | end 47 | 48 | it 'select using LSI' do 49 | out = ddbcli('select * from employees use index (local_idx) where emp_no = 4 and gender = "M"') 50 | out = JSON.parse(out) 51 | 52 | expect(out).to eq( 53 | [{"birth_date"=>"1962-07-06", 54 | "emp_no"=>4, 55 | "first_name"=>"Tristan", 56 | "gender"=>"M", 57 | "hire_date"=>"1985-07-20", 58 | "last_name"=>"Biran"}, 59 | {"birth_date"=>"1959-07-01", 60 | "emp_no"=>4, 61 | "first_name"=>"Dayanand", 62 | "gender"=>"M", 63 | "hire_date"=>"1989-09-01", 64 | "last_name"=>"Waterhouse"}, 65 | {"birth_date"=>"1957-09-16", 66 | "emp_no"=>4, 67 | "first_name"=>"Aemilian", 68 | "gender"=>"M", 69 | "hire_date"=>"1990-09-25", 70 | "last_name"=>"Roccetti"}, 71 | {"birth_date"=>"1955-04-26", 72 | "emp_no"=>4, 73 | "first_name"=>"Zine", 74 | "gender"=>"M", 75 | "hire_date"=>"1991-06-19", 76 | "last_name"=>"Butner"}] 77 | ) 78 | end 79 | 80 | it 'select by hash and range key' do 81 | out = ddbcli('select * from employees where emp_no = 4 and birth_date >= "1961-01-01"') 82 | out = JSON.parse(out) 83 | 84 | expect(out).to eq( 85 | [{"birth_date"=>"1961-04-28", 86 | "emp_no"=>4, 87 | "first_name"=>"Selwyn", 88 | "gender"=>"F", 89 | "hire_date"=>"1994-08-12", 90 | "last_name"=>"Parascandalo"}, 91 | {"birth_date"=>"1962-07-06", 92 | "emp_no"=>4, 93 | "first_name"=>"Tristan", 94 | "gender"=>"M", 95 | "hire_date"=>"1985-07-20", 96 | "last_name"=>"Biran"}] 97 | ) 98 | end 99 | 100 | it 'select by hash key' do 101 | out = ddbcli('select * from employees where emp_no = 4') 102 | out = JSON.parse(out) 103 | 104 | expect(out).to eq( 105 | [{"birth_date"=>"1955-04-26", 106 | "emp_no"=>4, 107 | "first_name"=>"Zine", 108 | "gender"=>"M", 109 | "hire_date"=>"1991-06-19", 110 | "last_name"=>"Butner"}, 111 | {"birth_date"=>"1957-09-16", 112 | "emp_no"=>4, 113 | "first_name"=>"Aemilian", 114 | "gender"=>"M", 115 | "hire_date"=>"1990-09-25", 116 | "last_name"=>"Roccetti"}, 117 | {"birth_date"=>"1959-07-01", 118 | "emp_no"=>4, 119 | "first_name"=>"Dayanand", 120 | "gender"=>"M", 121 | "hire_date"=>"1989-09-01", 122 | "last_name"=>"Waterhouse"}, 123 | {"birth_date"=>"1961-04-28", 124 | "emp_no"=>4, 125 | "first_name"=>"Selwyn", 126 | "gender"=>"F", 127 | "hire_date"=>"1994-08-12", 128 | "last_name"=>"Parascandalo"}, 129 | {"birth_date"=>"1962-07-06", 130 | "emp_no"=>4, 131 | "first_name"=>"Tristan", 132 | "gender"=>"M", 133 | "hire_date"=>"1985-07-20", 134 | "last_name"=>"Biran"}] 135 | ) 136 | end 137 | 138 | it 'scan all' do 139 | out = ddbcli('select all * from employees') 140 | out = JSON.parse(out) 141 | 142 | expect(out).to eq( 143 | [{"birth_date"=>"1956-05-15", 144 | "emp_no"=>2, 145 | "first_name"=>"Cathie", 146 | "gender"=>"M", 147 | "hire_date"=>"1997-04-11", 148 | "last_name"=>"Keohane"}, 149 | {"birth_date"=>"1961-10-19", 150 | "emp_no"=>2, 151 | "first_name"=>"Collette", 152 | "gender"=>"M", 153 | "hire_date"=>"1993-02-26", 154 | "last_name"=>"Ghemri"}, 155 | {"birth_date"=>"1964-04-30", 156 | "emp_no"=>2, 157 | "first_name"=>"Akhilish", 158 | "gender"=>"F", 159 | "hire_date"=>"1985-03-21", 160 | "last_name"=>"Isaak"}, 161 | {"birth_date"=>"1963-07-14", 162 | "emp_no"=>1, 163 | "first_name"=>"Katsuyuki", 164 | "gender"=>"F", 165 | "hire_date"=>"1989-12-28", 166 | "last_name"=>"Weedon"}, 167 | {"birth_date"=>"1964-12-29", 168 | "emp_no"=>7, 169 | "first_name"=>"Mack", 170 | "gender"=>"F", 171 | "hire_date"=>"1988-02-25", 172 | "last_name"=>"Hambrick"}, 173 | {"birth_date"=>"1955-04-26", 174 | "emp_no"=>4, 175 | "first_name"=>"Zine", 176 | "gender"=>"M", 177 | "hire_date"=>"1991-06-19", 178 | "last_name"=>"Butner"}, 179 | {"birth_date"=>"1957-09-16", 180 | "emp_no"=>4, 181 | "first_name"=>"Aemilian", 182 | "gender"=>"M", 183 | "hire_date"=>"1990-09-25", 184 | "last_name"=>"Roccetti"}, 185 | {"birth_date"=>"1959-07-01", 186 | "emp_no"=>4, 187 | "first_name"=>"Dayanand", 188 | "gender"=>"M", 189 | "hire_date"=>"1989-09-01", 190 | "last_name"=>"Waterhouse"}, 191 | {"birth_date"=>"1961-04-28", 192 | "emp_no"=>4, 193 | "first_name"=>"Selwyn", 194 | "gender"=>"F", 195 | "hire_date"=>"1994-08-12", 196 | "last_name"=>"Parascandalo"}, 197 | {"birth_date"=>"1962-07-06", 198 | "emp_no"=>4, 199 | "first_name"=>"Tristan", 200 | "gender"=>"M", 201 | "hire_date"=>"1985-07-20", 202 | "last_name"=>"Biran"}] 203 | ) 204 | end 205 | 206 | it 'scan which condition' do 207 | out = ddbcli('select all * from employees where hire_date >= "1990-01-01"') 208 | out = JSON.parse(out) 209 | 210 | expect(out).to eq( 211 | [{"birth_date"=>"1956-05-15", 212 | "emp_no"=>2, 213 | "first_name"=>"Cathie", 214 | "gender"=>"M", 215 | "hire_date"=>"1997-04-11", 216 | "last_name"=>"Keohane"}, 217 | {"birth_date"=>"1961-10-19", 218 | "emp_no"=>2, 219 | "first_name"=>"Collette", 220 | "gender"=>"M", 221 | "hire_date"=>"1993-02-26", 222 | "last_name"=>"Ghemri"}, 223 | {"birth_date"=>"1955-04-26", 224 | "emp_no"=>4, 225 | "first_name"=>"Zine", 226 | "gender"=>"M", 227 | "hire_date"=>"1991-06-19", 228 | "last_name"=>"Butner"}, 229 | {"birth_date"=>"1957-09-16", 230 | "emp_no"=>4, 231 | "first_name"=>"Aemilian", 232 | "gender"=>"M", 233 | "hire_date"=>"1990-09-25", 234 | "last_name"=>"Roccetti"}, 235 | {"birth_date"=>"1961-04-28", 236 | "emp_no"=>4, 237 | "first_name"=>"Selwyn", 238 | "gender"=>"F", 239 | "hire_date"=>"1994-08-12", 240 | "last_name"=>"Parascandalo"}] 241 | ) 242 | end 243 | end 244 | end 245 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'pp' 3 | require 'tempfile' 4 | 5 | %w(AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION).each do |name| 6 | ENV[name] = ENV["DDBCLI_TEST_#{name}"] || '(empty)' 7 | end 8 | 9 | def ddbcli(input = nil, args = []) 10 | tempfile = nil 11 | 12 | if input 13 | tempfile = Tempfile.open('ddbcli') 14 | tempfile << input 15 | tempfile.flush 16 | tempfile.rewind 17 | input = tempfile.path 18 | end 19 | 20 | cmd = File.expand_path(File.dirname(__FILE__) + '/../bin/ddbcli') 21 | out = nil 22 | 23 | args = ['--url', ENV['DYNAMODB_URL'] || 'localhost:8000'] + args 24 | 25 | if input 26 | out = `cat #{input} | #{cmd} #{args.join(' ')} 2>&1` 27 | tempfile.close if tempfile 28 | else 29 | out = `#{cmd} #{args.join(' ')} 2>&1` 30 | end 31 | 32 | raise out unless $?.success? 33 | 34 | out.strip 35 | end 36 | 37 | def clean_tables 38 | show_tables = lambda do 39 | out = ddbcli('show tables') 40 | JSON.parse(out) 41 | end 42 | 43 | show_tables.call.each do |name| 44 | ddbcli("drop table #{name}") 45 | end 46 | 47 | until show_tables.call.empty? 48 | sleep 1 49 | end 50 | end 51 | 52 | RSpec.configure do |config| 53 | config.before(:each) do 54 | clean_tables 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/update_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'ddbcli' do 2 | context 'update' do 3 | before do 4 | ddbcli(<<-'EOS') 5 | CREATE TABLE `employees` ( 6 | `emp_no` NUMBER HASH, 7 | `birth_date` STRING RANGE 8 | ) read=2 write=2; 9 | 10 | INSERT INTO `employees` 11 | (`birth_date`, `emp_no`, `first_name`, `gender`, `hire_date`, `last_name`) 12 | VALUES 13 | ("1956-05-15",2,"Cathie","M","1997-04-11","Keohane"), 14 | ("1957-09-16",4,"Aemilian","M","1990-09-25","Roccetti"), 15 | ("1959-07-01",4,"Dayanand","M","1989-09-01","Waterhouse"), 16 | ("1964-12-29",7,"Mack","F","1988-02-25","Hambrick"), 17 | ("1962-07-06",4,"Tristan","M","1985-07-20","Biran"), 18 | ("1964-04-30",2,"Akhilish","F","1985-03-21","Isaak"), 19 | ("1963-07-14",1,"Katsuyuki","F","1989-12-28","Weedon"), 20 | ("1961-10-19",2,"Collette","M","1993-02-26","Ghemri"), 21 | ("1955-04-26",4,"Zine","M","1991-06-19","Butner"), 22 | ("1961-04-28",4,"Selwyn","F","1994-08-12","Parascandalo"); 23 | EOS 24 | end 25 | 26 | it 'update by pk' do 27 | ddbcli('update employees set hire_date = "1977-11-11", foo = "bar" where emp_no = 4 and birth_date = "1961-04-28"') 28 | out = ddbcli('select all * from employees') 29 | out = JSON.parse(out) 30 | 31 | expect(out).to eq( 32 | [{"birth_date"=>"1956-05-15", 33 | "emp_no"=>2, 34 | "first_name"=>"Cathie", 35 | "gender"=>"M", 36 | "hire_date"=>"1997-04-11", 37 | "last_name"=>"Keohane"}, 38 | {"birth_date"=>"1961-10-19", 39 | "emp_no"=>2, 40 | "first_name"=>"Collette", 41 | "gender"=>"M", 42 | "hire_date"=>"1993-02-26", 43 | "last_name"=>"Ghemri"}, 44 | {"birth_date"=>"1964-04-30", 45 | "emp_no"=>2, 46 | "first_name"=>"Akhilish", 47 | "gender"=>"F", 48 | "hire_date"=>"1985-03-21", 49 | "last_name"=>"Isaak"}, 50 | {"birth_date"=>"1963-07-14", 51 | "emp_no"=>1, 52 | "first_name"=>"Katsuyuki", 53 | "gender"=>"F", 54 | "hire_date"=>"1989-12-28", 55 | "last_name"=>"Weedon"}, 56 | {"birth_date"=>"1964-12-29", 57 | "emp_no"=>7, 58 | "first_name"=>"Mack", 59 | "gender"=>"F", 60 | "hire_date"=>"1988-02-25", 61 | "last_name"=>"Hambrick"}, 62 | {"birth_date"=>"1955-04-26", 63 | "emp_no"=>4, 64 | "first_name"=>"Zine", 65 | "gender"=>"M", 66 | "hire_date"=>"1991-06-19", 67 | "last_name"=>"Butner"}, 68 | {"birth_date"=>"1957-09-16", 69 | "emp_no"=>4, 70 | "first_name"=>"Aemilian", 71 | "gender"=>"M", 72 | "hire_date"=>"1990-09-25", 73 | "last_name"=>"Roccetti"}, 74 | {"birth_date"=>"1959-07-01", 75 | "emp_no"=>4, 76 | "first_name"=>"Dayanand", 77 | "gender"=>"M", 78 | "hire_date"=>"1989-09-01", 79 | "last_name"=>"Waterhouse"}, 80 | {"birth_date"=>"1961-04-28", 81 | "emp_no"=>4, 82 | "first_name"=>"Selwyn", 83 | "foo"=>"bar", 84 | "gender"=>"F", 85 | "hire_date"=>"1977-11-11", 86 | "last_name"=>"Parascandalo"}, 87 | {"birth_date"=>"1962-07-06", 88 | "emp_no"=>4, 89 | "first_name"=>"Tristan", 90 | "gender"=>"M", 91 | "hire_date"=>"1985-07-20", 92 | "last_name"=>"Biran"}] 93 | ) 94 | end 95 | 96 | it 'update all' do 97 | ddbcli('update all employees set hire_date = "1977-11-11", foo = "bar" where gender = "F"') 98 | out = ddbcli('select all * from employees') 99 | out = JSON.parse(out) 100 | 101 | expect(out).to eq( 102 | [{"birth_date"=>"1956-05-15", 103 | "emp_no"=>2, 104 | "first_name"=>"Cathie", 105 | "gender"=>"M", 106 | "hire_date"=>"1997-04-11", 107 | "last_name"=>"Keohane"}, 108 | {"birth_date"=>"1961-10-19", 109 | "emp_no"=>2, 110 | "first_name"=>"Collette", 111 | "gender"=>"M", 112 | "hire_date"=>"1993-02-26", 113 | "last_name"=>"Ghemri"}, 114 | {"birth_date"=>"1964-04-30", 115 | "emp_no"=>2, 116 | "first_name"=>"Akhilish", 117 | "foo"=>"bar", 118 | "gender"=>"F", 119 | "hire_date"=>"1977-11-11", 120 | "last_name"=>"Isaak"}, 121 | {"birth_date"=>"1963-07-14", 122 | "emp_no"=>1, 123 | "first_name"=>"Katsuyuki", 124 | "foo"=>"bar", 125 | "gender"=>"F", 126 | "hire_date"=>"1977-11-11", 127 | "last_name"=>"Weedon"}, 128 | {"birth_date"=>"1964-12-29", 129 | "emp_no"=>7, 130 | "first_name"=>"Mack", 131 | "foo"=>"bar", 132 | "gender"=>"F", 133 | "hire_date"=>"1977-11-11", 134 | "last_name"=>"Hambrick"}, 135 | {"birth_date"=>"1955-04-26", 136 | "emp_no"=>4, 137 | "first_name"=>"Zine", 138 | "gender"=>"M", 139 | "hire_date"=>"1991-06-19", 140 | "last_name"=>"Butner"}, 141 | {"birth_date"=>"1957-09-16", 142 | "emp_no"=>4, 143 | "first_name"=>"Aemilian", 144 | "gender"=>"M", 145 | "hire_date"=>"1990-09-25", 146 | "last_name"=>"Roccetti"}, 147 | {"birth_date"=>"1959-07-01", 148 | "emp_no"=>4, 149 | "first_name"=>"Dayanand", 150 | "gender"=>"M", 151 | "hire_date"=>"1989-09-01", 152 | "last_name"=>"Waterhouse"}, 153 | {"birth_date"=>"1961-04-28", 154 | "emp_no"=>4, 155 | "first_name"=>"Selwyn", 156 | "foo"=>"bar", 157 | "gender"=>"F", 158 | "hire_date"=>"1977-11-11", 159 | "last_name"=>"Parascandalo"}, 160 | {"birth_date"=>"1962-07-06", 161 | "emp_no"=>4, 162 | "first_name"=>"Tristan", 163 | "gender"=>"M", 164 | "hire_date"=>"1985-07-20", 165 | "last_name"=>"Biran"}] 166 | ) 167 | end 168 | 169 | it 'update add' do 170 | ddbcli('update all employees set foo = 500') 171 | ddbcli('update all employees add foo = 1000 where gender = "F"') 172 | out = ddbcli('select all * from employees') 173 | out = JSON.parse(out) 174 | 175 | expect(out).to eq( 176 | [{"birth_date"=>"1956-05-15", 177 | "emp_no"=>2, 178 | "first_name"=>"Cathie", 179 | "foo"=>500, 180 | "gender"=>"M", 181 | "hire_date"=>"1997-04-11", 182 | "last_name"=>"Keohane"}, 183 | {"birth_date"=>"1961-10-19", 184 | "emp_no"=>2, 185 | "first_name"=>"Collette", 186 | "foo"=>500, 187 | "gender"=>"M", 188 | "hire_date"=>"1993-02-26", 189 | "last_name"=>"Ghemri"}, 190 | {"birth_date"=>"1964-04-30", 191 | "emp_no"=>2, 192 | "first_name"=>"Akhilish", 193 | "foo"=>1500, 194 | "gender"=>"F", 195 | "hire_date"=>"1985-03-21", 196 | "last_name"=>"Isaak"}, 197 | {"birth_date"=>"1963-07-14", 198 | "emp_no"=>1, 199 | "first_name"=>"Katsuyuki", 200 | "foo"=>1500, 201 | "gender"=>"F", 202 | "hire_date"=>"1989-12-28", 203 | "last_name"=>"Weedon"}, 204 | {"birth_date"=>"1964-12-29", 205 | "emp_no"=>7, 206 | "first_name"=>"Mack", 207 | "foo"=>1500, 208 | "gender"=>"F", 209 | "hire_date"=>"1988-02-25", 210 | "last_name"=>"Hambrick"}, 211 | {"birth_date"=>"1955-04-26", 212 | "emp_no"=>4, 213 | "first_name"=>"Zine", 214 | "foo"=>500, 215 | "gender"=>"M", 216 | "hire_date"=>"1991-06-19", 217 | "last_name"=>"Butner"}, 218 | {"birth_date"=>"1957-09-16", 219 | "emp_no"=>4, 220 | "first_name"=>"Aemilian", 221 | "foo"=>500, 222 | "gender"=>"M", 223 | "hire_date"=>"1990-09-25", 224 | "last_name"=>"Roccetti"}, 225 | {"birth_date"=>"1959-07-01", 226 | "emp_no"=>4, 227 | "first_name"=>"Dayanand", 228 | "foo"=>500, 229 | "gender"=>"M", 230 | "hire_date"=>"1989-09-01", 231 | "last_name"=>"Waterhouse"}, 232 | {"birth_date"=>"1961-04-28", 233 | "emp_no"=>4, 234 | "first_name"=>"Selwyn", 235 | "foo"=>1500, 236 | "gender"=>"F", 237 | "hire_date"=>"1994-08-12", 238 | "last_name"=>"Parascandalo"}, 239 | {"birth_date"=>"1962-07-06", 240 | "emp_no"=>4, 241 | "first_name"=>"Tristan", 242 | "foo"=>500, 243 | "gender"=>"M", 244 | "hire_date"=>"1985-07-20", 245 | "last_name"=>"Biran"}] 246 | ) 247 | end 248 | 249 | it 'update null' do 250 | ddbcli('update all employees set hire_date = null where gender = "M"') 251 | out = ddbcli('select all * from employees') 252 | out = JSON.parse(out) 253 | 254 | expect(out).to eq( 255 | [{"birth_date"=>"1956-05-15", 256 | "emp_no"=>2, 257 | "first_name"=>"Cathie", 258 | "gender"=>"M", 259 | "hire_date"=>nil, 260 | "last_name"=>"Keohane"}, 261 | {"birth_date"=>"1961-10-19", 262 | "emp_no"=>2, 263 | "first_name"=>"Collette", 264 | "gender"=>"M", 265 | "hire_date"=>nil, 266 | "last_name"=>"Ghemri"}, 267 | {"birth_date"=>"1964-04-30", 268 | "emp_no"=>2, 269 | "first_name"=>"Akhilish", 270 | "gender"=>"F", 271 | "hire_date"=>"1985-03-21", 272 | "last_name"=>"Isaak"}, 273 | {"birth_date"=>"1963-07-14", 274 | "emp_no"=>1, 275 | "first_name"=>"Katsuyuki", 276 | "gender"=>"F", 277 | "hire_date"=>"1989-12-28", 278 | "last_name"=>"Weedon"}, 279 | {"birth_date"=>"1964-12-29", 280 | "emp_no"=>7, 281 | "first_name"=>"Mack", 282 | "gender"=>"F", 283 | "hire_date"=>"1988-02-25", 284 | "last_name"=>"Hambrick"}, 285 | {"birth_date"=>"1955-04-26", 286 | "emp_no"=>4, 287 | "first_name"=>"Zine", 288 | "gender"=>"M", 289 | "hire_date"=>nil, 290 | "last_name"=>"Butner"}, 291 | {"birth_date"=>"1957-09-16", 292 | "emp_no"=>4, 293 | "first_name"=>"Aemilian", 294 | "gender"=>"M", 295 | "hire_date"=>nil, 296 | "last_name"=>"Roccetti"}, 297 | {"birth_date"=>"1959-07-01", 298 | "emp_no"=>4, 299 | "first_name"=>"Dayanand", 300 | "gender"=>"M", 301 | "hire_date"=>nil, 302 | "last_name"=>"Waterhouse"}, 303 | {"birth_date"=>"1961-04-28", 304 | "emp_no"=>4, 305 | "first_name"=>"Selwyn", 306 | "gender"=>"F", 307 | "hire_date"=>"1994-08-12", 308 | "last_name"=>"Parascandalo"}, 309 | {"birth_date"=>"1962-07-06", 310 | "emp_no"=>4, 311 | "first_name"=>"Tristan", 312 | "gender"=>"M", 313 | "hire_date"=>nil, 314 | "last_name"=>"Biran"}] 315 | ) 316 | end 317 | 318 | it 'update delete (1)' do 319 | ddbcli('update all employees delete hire_date where gender = "M"') 320 | out = ddbcli('select all * from employees') 321 | out = JSON.parse(out) 322 | 323 | expect(out).to eq( 324 | [{"birth_date"=>"1956-05-15", 325 | "emp_no"=>2, 326 | "first_name"=>"Cathie", 327 | "gender"=>"M", 328 | "last_name"=>"Keohane"}, 329 | {"birth_date"=>"1961-10-19", 330 | "emp_no"=>2, 331 | "first_name"=>"Collette", 332 | "gender"=>"M", 333 | "last_name"=>"Ghemri"}, 334 | {"birth_date"=>"1964-04-30", 335 | "emp_no"=>2, 336 | "first_name"=>"Akhilish", 337 | "gender"=>"F", 338 | "hire_date"=>"1985-03-21", 339 | "last_name"=>"Isaak"}, 340 | {"birth_date"=>"1963-07-14", 341 | "emp_no"=>1, 342 | "first_name"=>"Katsuyuki", 343 | "gender"=>"F", 344 | "hire_date"=>"1989-12-28", 345 | "last_name"=>"Weedon"}, 346 | {"birth_date"=>"1964-12-29", 347 | "emp_no"=>7, 348 | "first_name"=>"Mack", 349 | "gender"=>"F", 350 | "hire_date"=>"1988-02-25", 351 | "last_name"=>"Hambrick"}, 352 | {"birth_date"=>"1955-04-26", 353 | "emp_no"=>4, 354 | "first_name"=>"Zine", 355 | "gender"=>"M", 356 | "last_name"=>"Butner"}, 357 | {"birth_date"=>"1957-09-16", 358 | "emp_no"=>4, 359 | "first_name"=>"Aemilian", 360 | "gender"=>"M", 361 | "last_name"=>"Roccetti"}, 362 | {"birth_date"=>"1959-07-01", 363 | "emp_no"=>4, 364 | "first_name"=>"Dayanand", 365 | "gender"=>"M", 366 | "last_name"=>"Waterhouse"}, 367 | {"birth_date"=>"1961-04-28", 368 | "emp_no"=>4, 369 | "first_name"=>"Selwyn", 370 | "gender"=>"F", 371 | "hire_date"=>"1994-08-12", 372 | "last_name"=>"Parascandalo"}, 373 | {"birth_date"=>"1962-07-06", 374 | "emp_no"=>4, 375 | "first_name"=>"Tristan", 376 | "gender"=>"M", 377 | "last_name"=>"Biran"}] 378 | ) 379 | end 380 | 381 | it 'update delete (2)' do 382 | ddbcli('update all employees del hire_date where gender = "M"') 383 | out = ddbcli('select all * from employees') 384 | out = JSON.parse(out) 385 | 386 | expect(out).to eq( 387 | [{"birth_date"=>"1956-05-15", 388 | "emp_no"=>2, 389 | "first_name"=>"Cathie", 390 | "gender"=>"M", 391 | "last_name"=>"Keohane"}, 392 | {"birth_date"=>"1961-10-19", 393 | "emp_no"=>2, 394 | "first_name"=>"Collette", 395 | "gender"=>"M", 396 | "last_name"=>"Ghemri"}, 397 | {"birth_date"=>"1964-04-30", 398 | "emp_no"=>2, 399 | "first_name"=>"Akhilish", 400 | "gender"=>"F", 401 | "hire_date"=>"1985-03-21", 402 | "last_name"=>"Isaak"}, 403 | {"birth_date"=>"1963-07-14", 404 | "emp_no"=>1, 405 | "first_name"=>"Katsuyuki", 406 | "gender"=>"F", 407 | "hire_date"=>"1989-12-28", 408 | "last_name"=>"Weedon"}, 409 | {"birth_date"=>"1964-12-29", 410 | "emp_no"=>7, 411 | "first_name"=>"Mack", 412 | "gender"=>"F", 413 | "hire_date"=>"1988-02-25", 414 | "last_name"=>"Hambrick"}, 415 | {"birth_date"=>"1955-04-26", 416 | "emp_no"=>4, 417 | "first_name"=>"Zine", 418 | "gender"=>"M", 419 | "last_name"=>"Butner"}, 420 | {"birth_date"=>"1957-09-16", 421 | "emp_no"=>4, 422 | "first_name"=>"Aemilian", 423 | "gender"=>"M", 424 | "last_name"=>"Roccetti"}, 425 | {"birth_date"=>"1959-07-01", 426 | "emp_no"=>4, 427 | "first_name"=>"Dayanand", 428 | "gender"=>"M", 429 | "last_name"=>"Waterhouse"}, 430 | {"birth_date"=>"1961-04-28", 431 | "emp_no"=>4, 432 | "first_name"=>"Selwyn", 433 | "gender"=>"F", 434 | "hire_date"=>"1994-08-12", 435 | "last_name"=>"Parascandalo"}, 436 | {"birth_date"=>"1962-07-06", 437 | "emp_no"=>4, 438 | "first_name"=>"Tristan", 439 | "gender"=>"M", 440 | "last_name"=>"Biran"}] 441 | ) 442 | end 443 | 444 | it 'update true' do 445 | ddbcli('update all employees set hire_date = true where gender = "M"') 446 | out = ddbcli('select all * from employees') 447 | out = JSON.parse(out) 448 | 449 | expect(out).to eq( 450 | [{"birth_date"=>"1956-05-15", 451 | "emp_no"=>2, 452 | "first_name"=>"Cathie", 453 | "gender"=>"M", 454 | "hire_date"=>true, 455 | "last_name"=>"Keohane"}, 456 | {"birth_date"=>"1961-10-19", 457 | "emp_no"=>2, 458 | "first_name"=>"Collette", 459 | "gender"=>"M", 460 | "hire_date"=>true, 461 | "last_name"=>"Ghemri"}, 462 | {"birth_date"=>"1964-04-30", 463 | "emp_no"=>2, 464 | "first_name"=>"Akhilish", 465 | "gender"=>"F", 466 | "hire_date"=>"1985-03-21", 467 | "last_name"=>"Isaak"}, 468 | {"birth_date"=>"1963-07-14", 469 | "emp_no"=>1, 470 | "first_name"=>"Katsuyuki", 471 | "gender"=>"F", 472 | "hire_date"=>"1989-12-28", 473 | "last_name"=>"Weedon"}, 474 | {"birth_date"=>"1964-12-29", 475 | "emp_no"=>7, 476 | "first_name"=>"Mack", 477 | "gender"=>"F", 478 | "hire_date"=>"1988-02-25", 479 | "last_name"=>"Hambrick"}, 480 | {"birth_date"=>"1955-04-26", 481 | "emp_no"=>4, 482 | "first_name"=>"Zine", 483 | "gender"=>"M", 484 | "hire_date"=>true, 485 | "last_name"=>"Butner"}, 486 | {"birth_date"=>"1957-09-16", 487 | "emp_no"=>4, 488 | "first_name"=>"Aemilian", 489 | "gender"=>"M", 490 | "hire_date"=>true, 491 | "last_name"=>"Roccetti"}, 492 | {"birth_date"=>"1959-07-01", 493 | "emp_no"=>4, 494 | "first_name"=>"Dayanand", 495 | "gender"=>"M", 496 | "hire_date"=>true, 497 | "last_name"=>"Waterhouse"}, 498 | {"birth_date"=>"1961-04-28", 499 | "emp_no"=>4, 500 | "first_name"=>"Selwyn", 501 | "gender"=>"F", 502 | "hire_date"=>"1994-08-12", 503 | "last_name"=>"Parascandalo"}, 504 | {"birth_date"=>"1962-07-06", 505 | "emp_no"=>4, 506 | "first_name"=>"Tristan", 507 | "gender"=>"M", 508 | "hire_date"=>true, 509 | "last_name"=>"Biran"}] 510 | ) 511 | end 512 | 513 | it 'update false' do 514 | ddbcli('update all employees set hire_date = false where gender = "M"') 515 | out = ddbcli('select all * from employees') 516 | out = JSON.parse(out) 517 | 518 | expect(out).to eq( 519 | [{"birth_date"=>"1956-05-15", 520 | "emp_no"=>2, 521 | "first_name"=>"Cathie", 522 | "gender"=>"M", 523 | "hire_date"=>false, 524 | "last_name"=>"Keohane"}, 525 | {"birth_date"=>"1961-10-19", 526 | "emp_no"=>2, 527 | "first_name"=>"Collette", 528 | "gender"=>"M", 529 | "hire_date"=>false, 530 | "last_name"=>"Ghemri"}, 531 | {"birth_date"=>"1964-04-30", 532 | "emp_no"=>2, 533 | "first_name"=>"Akhilish", 534 | "gender"=>"F", 535 | "hire_date"=>"1985-03-21", 536 | "last_name"=>"Isaak"}, 537 | {"birth_date"=>"1963-07-14", 538 | "emp_no"=>1, 539 | "first_name"=>"Katsuyuki", 540 | "gender"=>"F", 541 | "hire_date"=>"1989-12-28", 542 | "last_name"=>"Weedon"}, 543 | {"birth_date"=>"1964-12-29", 544 | "emp_no"=>7, 545 | "first_name"=>"Mack", 546 | "gender"=>"F", 547 | "hire_date"=>"1988-02-25", 548 | "last_name"=>"Hambrick"}, 549 | {"birth_date"=>"1955-04-26", 550 | "emp_no"=>4, 551 | "first_name"=>"Zine", 552 | "gender"=>"M", 553 | "hire_date"=>false, 554 | "last_name"=>"Butner"}, 555 | {"birth_date"=>"1957-09-16", 556 | "emp_no"=>4, 557 | "first_name"=>"Aemilian", 558 | "gender"=>"M", 559 | "hire_date"=>false, 560 | "last_name"=>"Roccetti"}, 561 | {"birth_date"=>"1959-07-01", 562 | "emp_no"=>4, 563 | "first_name"=>"Dayanand", 564 | "gender"=>"M", 565 | "hire_date"=>false, 566 | "last_name"=>"Waterhouse"}, 567 | {"birth_date"=>"1961-04-28", 568 | "emp_no"=>4, 569 | "first_name"=>"Selwyn", 570 | "gender"=>"F", 571 | "hire_date"=>"1994-08-12", 572 | "last_name"=>"Parascandalo"}, 573 | {"birth_date"=>"1962-07-06", 574 | "emp_no"=>4, 575 | "first_name"=>"Tristan", 576 | "gender"=>"M", 577 | "hire_date"=>false, 578 | "last_name"=>"Biran"}] 579 | ) 580 | end 581 | 582 | it 'update list' do 583 | ddbcli('update all employees set hire_date = [1, 2, ["foo", "bar"]] where gender = "M"') 584 | out = ddbcli('select all * from employees') 585 | out = JSON.parse(out) 586 | 587 | expect(out).to eq( 588 | [{"birth_date"=>"1956-05-15", 589 | "emp_no"=>2, 590 | "first_name"=>"Cathie", 591 | "gender"=>"M", 592 | "hire_date"=>[1, 2, ["foo", "bar"]], 593 | "last_name"=>"Keohane"}, 594 | {"birth_date"=>"1961-10-19", 595 | "emp_no"=>2, 596 | "first_name"=>"Collette", 597 | "gender"=>"M", 598 | "hire_date"=>[1, 2, ["foo", "bar"]], 599 | "last_name"=>"Ghemri"}, 600 | {"birth_date"=>"1964-04-30", 601 | "emp_no"=>2, 602 | "first_name"=>"Akhilish", 603 | "gender"=>"F", 604 | "hire_date"=>"1985-03-21", 605 | "last_name"=>"Isaak"}, 606 | {"birth_date"=>"1963-07-14", 607 | "emp_no"=>1, 608 | "first_name"=>"Katsuyuki", 609 | "gender"=>"F", 610 | "hire_date"=>"1989-12-28", 611 | "last_name"=>"Weedon"}, 612 | {"birth_date"=>"1964-12-29", 613 | "emp_no"=>7, 614 | "first_name"=>"Mack", 615 | "gender"=>"F", 616 | "hire_date"=>"1988-02-25", 617 | "last_name"=>"Hambrick"}, 618 | {"birth_date"=>"1955-04-26", 619 | "emp_no"=>4, 620 | "first_name"=>"Zine", 621 | "gender"=>"M", 622 | "hire_date"=>[1, 2, ["foo", "bar"]], 623 | "last_name"=>"Butner"}, 624 | {"birth_date"=>"1957-09-16", 625 | "emp_no"=>4, 626 | "first_name"=>"Aemilian", 627 | "gender"=>"M", 628 | "hire_date"=>[1, 2, ["foo", "bar"]], 629 | "last_name"=>"Roccetti"}, 630 | {"birth_date"=>"1959-07-01", 631 | "emp_no"=>4, 632 | "first_name"=>"Dayanand", 633 | "gender"=>"M", 634 | "hire_date"=>[1, 2, ["foo", "bar"]], 635 | "last_name"=>"Waterhouse"}, 636 | {"birth_date"=>"1961-04-28", 637 | "emp_no"=>4, 638 | "first_name"=>"Selwyn", 639 | "gender"=>"F", 640 | "hire_date"=>"1994-08-12", 641 | "last_name"=>"Parascandalo"}, 642 | {"birth_date"=>"1962-07-06", 643 | "emp_no"=>4, 644 | "first_name"=>"Tristan", 645 | "gender"=>"M", 646 | "hire_date"=>[1, 2, ["foo", "bar"]], 647 | "last_name"=>"Biran"}] 648 | ) 649 | end 650 | 651 | it 'update map' do 652 | ddbcli('update all employees set hire_date = {foo:"bar", zoo:[1, 2, 3]} where gender = "M"') 653 | out = ddbcli('select all * from employees') 654 | out = JSON.parse(out) 655 | 656 | expect(out).to eq( 657 | [{"birth_date"=>"1956-05-15", 658 | "emp_no"=>2, 659 | "first_name"=>"Cathie", 660 | "gender"=>"M", 661 | "hire_date"=>{"foo"=>"bar", "zoo"=>[1, 2, 3]}, 662 | "last_name"=>"Keohane"}, 663 | {"birth_date"=>"1961-10-19", 664 | "emp_no"=>2, 665 | "first_name"=>"Collette", 666 | "gender"=>"M", 667 | "hire_date"=>{"foo"=>"bar", "zoo"=>[1, 2, 3]}, 668 | "last_name"=>"Ghemri"}, 669 | {"birth_date"=>"1964-04-30", 670 | "emp_no"=>2, 671 | "first_name"=>"Akhilish", 672 | "gender"=>"F", 673 | "hire_date"=>"1985-03-21", 674 | "last_name"=>"Isaak"}, 675 | {"birth_date"=>"1963-07-14", 676 | "emp_no"=>1, 677 | "first_name"=>"Katsuyuki", 678 | "gender"=>"F", 679 | "hire_date"=>"1989-12-28", 680 | "last_name"=>"Weedon"}, 681 | {"birth_date"=>"1964-12-29", 682 | "emp_no"=>7, 683 | "first_name"=>"Mack", 684 | "gender"=>"F", 685 | "hire_date"=>"1988-02-25", 686 | "last_name"=>"Hambrick"}, 687 | {"birth_date"=>"1955-04-26", 688 | "emp_no"=>4, 689 | "first_name"=>"Zine", 690 | "gender"=>"M", 691 | "hire_date"=>{"foo"=>"bar", "zoo"=>[1, 2, 3]}, 692 | "last_name"=>"Butner"}, 693 | {"birth_date"=>"1957-09-16", 694 | "emp_no"=>4, 695 | "first_name"=>"Aemilian", 696 | "gender"=>"M", 697 | "hire_date"=>{"foo"=>"bar", "zoo"=>[1, 2, 3]}, 698 | "last_name"=>"Roccetti"}, 699 | {"birth_date"=>"1959-07-01", 700 | "emp_no"=>4, 701 | "first_name"=>"Dayanand", 702 | "gender"=>"M", 703 | "hire_date"=>{"foo"=>"bar", "zoo"=>[1, 2, 3]}, 704 | "last_name"=>"Waterhouse"}, 705 | {"birth_date"=>"1961-04-28", 706 | "emp_no"=>4, 707 | "first_name"=>"Selwyn", 708 | "gender"=>"F", 709 | "hire_date"=>"1994-08-12", 710 | "last_name"=>"Parascandalo"}, 711 | {"birth_date"=>"1962-07-06", 712 | "emp_no"=>4, 713 | "first_name"=>"Tristan", 714 | "gender"=>"M", 715 | "hire_date"=>{"foo"=>"bar", "zoo"=>[1, 2, 3]}, 716 | "last_name"=>"Biran"}] 717 | ) 718 | end 719 | end 720 | end 721 | --------------------------------------------------------------------------------