aws 2.3.34 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,6 +32,8 @@ rescue LoadError => e
32
32
  exit
33
33
  end
34
34
 
35
+ puts 'WARNING: ActiveSdb is deprecated, please use SimpleRecord at https://github.com/appoxy/simple_record/ instead.'
36
+
35
37
  module Aws
36
38
 
37
39
  # = Aws::ActiveSdb -- RightScale SDB interface (alpha release)
@@ -0,0 +1,886 @@
1
+ #
2
+ # Copyright (c) 2008 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ require "aws"
25
+
26
+ module Aws
27
+
28
+ class SdbInterface < AwsBase
29
+
30
+ include AwsBaseInterface
31
+
32
+ DEFAULT_HOST = 'sdb.amazonaws.com'
33
+ DEFAULT_PORT = 443
34
+ DEFAULT_PROTOCOL = 'https'
35
+ DEFAULT_SERVICE = '/'
36
+ API_VERSION = '2009-04-15'
37
+ DEFAULT_NIL_REPRESENTATION = 'nil'
38
+
39
+ def self.connection_name
40
+ :s3_connection
41
+ end
42
+
43
+ @@bench = AwsBenchmarkingBlock.new
44
+
45
+ def self.bench
46
+ @@bench
47
+ end
48
+
49
+ def self.bench_xml;
50
+ @@bench.xml;
51
+ end
52
+
53
+ def self.bench_sdb;
54
+ @@bench.service;
55
+ end
56
+
57
+ attr_reader :last_query_expression
58
+
59
+ # Creates new RightSdb instance.
60
+ #
61
+ # Params:
62
+ # { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
63
+ # :port => 443 # Amazon service port: 80(default) or 443
64
+ # :protocol => 'https' # Amazon service protocol: 'http'(default) or 'https'
65
+ # :signature_version => '2' # The signature version : '0', '1' or '2' (default)
66
+ # DEPRECATED :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
67
+ # :connection_mode => :default # options are :default (will use best known option, may change in the future)
68
+ # :per_request (opens and closes a connection on every request to SDB)
69
+ # :single - one connection shared across app (same as old multi_thread=>false)
70
+ # :per_thread - one connection per ruby thread (same as old multi_thread=>true)
71
+ # :pool (uses a connection pool with a maximum number of connections - NOT IMPLEMENTED YET)
72
+ # :logger => Logger Object # Logger instance: logs to STDOUT if omitted
73
+ # :nil_representation => 'mynil'} # interpret Ruby nil as this string value; i.e. use this string in SDB to represent Ruby nils (default is the string 'nil')
74
+ # :service => '/' # Set this to /mdb/request.mgwsi for usage with M/DB #
75
+ #
76
+ # Example:
77
+ #
78
+ # sdb = Aws::SdbInterface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:connection_mode => :per_request, :logger => Logger.new('/tmp/x.log')}) #=> #<RightSdb:0xa6b8c27c>
79
+ #
80
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/
81
+ #
82
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
83
+ @nil_rep = params[:nil_representation] ? params[:nil_representation] : DEFAULT_NIL_REPRESENTATION
84
+ params.delete(:nil_representation)
85
+ init({:name => 'SDB',
86
+ :default_host => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).host : DEFAULT_HOST,
87
+ :default_port => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).port : DEFAULT_PORT,
88
+ :default_protocol => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).scheme : DEFAULT_PROTOCOL,
89
+ :default_service => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).path : DEFAULT_SERVICE},
90
+ # :service_endpoint => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).path : DEFAULT_ENDPOINT },
91
+ aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'],
92
+ aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
93
+ params)
94
+ end
95
+
96
+ #-----------------------------------------------------------------
97
+ # Requests
98
+ #-----------------------------------------------------------------
99
+ def generate_request(action, params={}, options={}) #:nodoc:
100
+ generate_request2(@aws_access_key_id, @aws_secret_access_key, action, API_VERSION, @params, params, options)
101
+ end
102
+
103
+
104
+ # Sends request to Amazon and parses the response
105
+ # Raises AwsError if any banana happened
106
+ def request_info(request, parser, options={}) #:nodoc:
107
+ # request_info2(request, parser, :sdb_connection)
108
+ request_info2(request, parser, @params, :sdb_connection, @logger, @@bench, options)
109
+
110
+ end
111
+
112
+ # Prepare attributes for putting.
113
+ # (used by put_attributes)
114
+ def pack_attributes(attributes, replace = false, key_prefix = "") #:nodoc:
115
+ result = {}
116
+ if attributes
117
+ idx = 0
118
+ skip_values = attributes.is_a?(Array)
119
+ attributes.each do |attribute, values|
120
+ # set replacement attribute
121
+ result["#{key_prefix}Attribute.#{idx}.Replace"] = 'true' if replace
122
+ # pack Name/Value
123
+ unless values.nil?
124
+ Array(values).each do |value|
125
+ result["#{key_prefix}Attribute.#{idx}.Name"] = attribute
126
+ result["#{key_prefix}Attribute.#{idx}.Value"] = ruby_to_sdb(value) unless skip_values
127
+ idx += 1
128
+ end
129
+ else
130
+ result["#{key_prefix}Attribute.#{idx}.Name"] = attribute
131
+ result["#{key_prefix}Attribute.#{idx}.Value"] = ruby_to_sdb(nil) unless skip_values
132
+ idx += 1
133
+ end
134
+ end
135
+ end
136
+ result
137
+ end
138
+
139
+
140
+ # Use this helper to manually escape the fields in the query expressions.
141
+ # To escape the single quotes and backslashes and to wrap the string into the single quotes.
142
+ #
143
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API.html
144
+ #
145
+ def escape(value)
146
+ %Q{'#{value.to_s.gsub(/(['\\])/) { "\\#{$1}" }}'} if value
147
+ end
148
+
149
+ # Convert a Ruby language value to a SDB value by replacing Ruby nil with the user's chosen string representation of nil.
150
+ # Non-nil values are unaffected by this filter.
151
+ def ruby_to_sdb(value)
152
+ # puts "value #{value} is frozen? #{value.frozen?}"
153
+ # value.nil? ? @nil_rep : ((value.frozen? || !value.is_a?(String)) ? value : value.force_encoding("UTF-8"))
154
+ value.nil? ? @nil_rep : value
155
+ end
156
+
157
+ # Convert a SDB value to a Ruby language value by replacing the user's chosen string representation of nil with Ruby nil.
158
+ # Values are unaffected by this filter unless they match the nil representation exactly.
159
+ def sdb_to_ruby(value)
160
+ value.eql?(@nil_rep) ? nil : value
161
+ end
162
+
163
+ # Convert select and query_with_attributes responses to a Ruby language values by replacing the user's chosen string representation of nil with Ruby nil.
164
+ # (This method affects on a passed response value)
165
+ def select_response_to_ruby(response) #:nodoc:
166
+ response[:items].each_with_index do |item, idx|
167
+ item.each do |key, attributes|
168
+ attributes.each do |name, values|
169
+ values.collect! { |value| sdb_to_ruby(value) }
170
+ end
171
+ end
172
+ end
173
+ response
174
+ end
175
+
176
+ # Create query expression from an array.
177
+ # (similar to ActiveRecord::Base#find using :conditions => ['query', param1, .., paramN])
178
+ #
179
+ def query_expression_from_array(params) #:nodoc:
180
+ return '' if params.blank?
181
+ query = params[0].to_s
182
+ i = 1
183
+ query.gsub(/(\\)?(\?)/) do
184
+ if $1 # if escaped '\?' is found - replace it by '?' without backslash
185
+ "?"
186
+ else # well, if no backslash precedes '?' then replace it by next param from the list
187
+ ret = escape(params[i])
188
+ i +=1
189
+ ret
190
+ end
191
+ end
192
+ end
193
+
194
+ def query_expression_from_hash(hash)
195
+ return '' if hash.blank?
196
+ expression = []
197
+ hash.each do |key, value|
198
+ expression << "#{key}=#{escape(value)}"
199
+ end
200
+ expression.join(' AND ')
201
+ end
202
+
203
+ # Retrieve a list of SDB domains from Amazon.
204
+ #
205
+ # Returns a hash:
206
+ # { :domains => [domain1, ..., domainN],
207
+ # :next_token => string || nil,
208
+ # :box_usage => string,
209
+ # :request_id => string }
210
+ #
211
+ # Example:
212
+ #
213
+ # sdb = Aws::SdbInterface.new
214
+ # sdb.list_domains #=> { :box_usage => "0.0000071759",
215
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982",
216
+ # :domains => ["toys", "dolls"]}
217
+ #
218
+ # If a block is given, this method yields to it. If the block returns true, list_domains will continue looping the request. If the block returns false,
219
+ # list_domains will end.
220
+ #
221
+ # sdb.list_domains(10) do |result| # list by 10 domains per iteration
222
+ # puts result.inspect
223
+ # true
224
+ # end
225
+ #
226
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_ListDomains.html
227
+ #
228
+ def list_domains(max_number_of_domains = nil, next_token = nil)
229
+ request_params = {'MaxNumberOfDomains' => max_number_of_domains,
230
+ 'NextToken' => next_token}
231
+ link = generate_request("ListDomains", request_params)
232
+ result = request_info(link, QSdbListDomainParser.new)
233
+ # return result if no block given
234
+ return result unless block_given?
235
+ # loop if block if given
236
+ begin
237
+ # the block must return true if it wanna continue
238
+ break unless yield(result) && result[:next_token]
239
+ # make new request
240
+ request_params['NextToken'] = result[:next_token]
241
+ link = generate_request("ListDomains", request_params)
242
+ result = request_info(link, QSdbListDomainParser.new)
243
+ end while true
244
+ rescue Exception
245
+ on_exception
246
+ end
247
+
248
+ # Retrieve a list of SDB domains from Amazon.
249
+ #
250
+ # Returns a hash:
251
+ # { :domains => [domain1, ..., domainN],
252
+ # :next_token => string || nil,
253
+ # :box_usage => string,
254
+ # :request_id => string }
255
+ #
256
+ # Example:
257
+ #
258
+ # sdb = Aws::SdbInterface.new
259
+ # sdb.list_domains #=> { :box_usage => "0.0000071759",
260
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982",
261
+ # :domains => ["toys", "dolls"]}
262
+ #
263
+ # If a block is given, this method yields to it. If the block returns true, list_domains will continue looping the request. If the block returns false,
264
+ # list_domains will end.
265
+ #
266
+ # sdb.list_domains(10) do |result| # list by 10 domains per iteration
267
+ # puts result.inspect
268
+ # true
269
+ # end
270
+ #
271
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_ListDomains.html
272
+ #
273
+ def domain_metadata(domain_name)
274
+ link = generate_request("DomainMetadata", 'DomainName' => domain_name)
275
+ result = request_info(link, QSdbDomainMetadataParser.new)
276
+ return result
277
+ rescue Exception
278
+ on_exception
279
+ end
280
+
281
+
282
+ # Create new SDB domain at Amazon.
283
+ #
284
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
285
+ # (Amazon raises no errors if the domain already exists).
286
+ #
287
+ # Example:
288
+ #
289
+ # sdb = Aws::SdbInterface.new
290
+ # sdb.create_domain('toys') # => { :box_usage => "0.0000071759",
291
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982" }
292
+ #
293
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_CreateDomain.html
294
+ def create_domain(domain_name)
295
+ link = generate_request("CreateDomain",
296
+ 'DomainName' => domain_name)
297
+ request_info(link, QSdbSimpleParser.new)
298
+ rescue Exception
299
+ on_exception
300
+ end
301
+
302
+ # Delete SDB domain at Amazon.
303
+ #
304
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
305
+ # (Amazon raises no errors if the domain does not exist).
306
+ #
307
+ # Example:
308
+ #
309
+ # sdb = Aws::SdbInterface.new
310
+ # sdb.delete_domain('toys') # => { :box_usage => "0.0000071759",
311
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982" }
312
+ #
313
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_DeleteDomain.html
314
+ #
315
+ def delete_domain(domain_name)
316
+ link = generate_request("DeleteDomain",
317
+ 'DomainName' => domain_name)
318
+ request_info(link, QSdbSimpleParser.new)
319
+ rescue Exception
320
+ on_exception
321
+ end
322
+
323
+ # Add/Replace item attributes.
324
+ #
325
+ # Params:
326
+ # domain_name = DomainName
327
+ # item_name = ItemName
328
+ # attributes = {
329
+ # 'nameA' => [valueA1,..., valueAN],
330
+ # ...
331
+ # 'nameZ' => [valueZ1,..., valueZN]
332
+ # }
333
+ # replace = :replace | any other value to skip replacement
334
+ # options:
335
+ # :create_domain => If true and domain does not exist, it will be created. Default is false.
336
+ #
337
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
338
+ # (Amazon raises no errors if the attribute was not overridden, as when the :replace param is unset).
339
+ #
340
+ # Example:
341
+ #
342
+ # sdb = Aws::SdbInterface.new
343
+ # sdb.create_domain 'family'
344
+ #
345
+ # attributes = {}
346
+ # # create attributes for Jon and Silvia
347
+ # attributes['Jon'] = %w{ car beer }
348
+ # attributes['Silvia'] = %w{ beetle rolling_pin kids }
349
+ # sdb.put_attributes 'family', 'toys', attributes #=> ok
350
+ # # now: Jon=>[car, beer], Silvia=>[beetle, rolling_pin, kids]
351
+ #
352
+ # # add attributes to Jon
353
+ # attributes.delete('Silvia')
354
+ # attributes['Jon'] = %w{ girls pub }
355
+ # sdb.put_attributes 'family', 'toys', attributes #=> ok
356
+ # # now: Jon=>[car, beer, girls, pub], Silvia=>[beetle, rolling_pin, kids]
357
+ #
358
+ # # replace attributes for Jon and add to a cat (the cat had no attributes before)
359
+ # attributes['Jon'] = %w{ vacuum_cleaner hammer spade }
360
+ # attributes['cat'] = %w{ mouse clew Jons_socks }
361
+ # sdb.put_attributes 'family', 'toys', attributes, :replace #=> ok
362
+ # # now: Jon=>[vacuum_cleaner, hammer, spade], Silvia=>[beetle, rolling_pin, kids], cat=>[mouse, clew, Jons_socks]
363
+ #
364
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_PutAttributes.html
365
+ #
366
+ def put_attributes(domain_name, item_name, attributes, replace = false, options={})
367
+ params = {'DomainName' => domain_name,
368
+ 'ItemName' => item_name}.merge(pack_attributes(attributes, replace))
369
+ logger.debug 'PUT=' + params.inspect
370
+ link = generate_request("PutAttributes", params)
371
+ begin
372
+ request_info(link, QSdbSimpleParser.new, options)
373
+ rescue Aws::AwsError => ex
374
+ # puts "RESCUED in put_attributes: " + $!
375
+ if options[:create_domain] && create_domain_if_not_exist(ex, domain_name)
376
+ options.delete(:create_domain)
377
+ put_attributes(domain_name, item_name, attributes, replace, options)
378
+ else
379
+ raise ex
380
+ end
381
+ end
382
+ rescue Exception
383
+ on_exception
384
+ end
385
+
386
+ def create_domain_if_not_exist(ex, domain_name)
387
+ if ex.message().index("NoSuchDomain")
388
+ puts "Creating domain: #{domain_name}"
389
+ create_domain(domain_name)
390
+ return true
391
+ end
392
+ return false
393
+ end
394
+
395
+ #
396
+ # items is an array of Aws::SdbInterface::Item.new(o.id, o.attributes, true)
397
+ def batch_put_attributes(domain_name, items, options={})
398
+ params = {'DomainName' => domain_name}
399
+ i = 0
400
+ items.each do |item|
401
+ prefix = "Item." + i.to_s + "."
402
+ params[prefix + "ItemName"] = item.item_name
403
+ params.merge!(pack_attributes(item.attributes, item.replace, prefix))
404
+ i += 1
405
+ end
406
+ link = generate_request("BatchPutAttributes", params)
407
+ begin
408
+ request_info(link, QSdbSimpleParser.new, options)
409
+ rescue Aws::AwsError => ex
410
+ # puts "RESCUED in batch_put_attributes: " + $!
411
+ if options[:create_domain] && create_domain_if_not_exist(ex, domain_name)
412
+ options.delete(:create_domain)
413
+ batch_put_attributes(domain_name, items, options)
414
+ else
415
+ raise ex
416
+ end
417
+ end
418
+ rescue Exception
419
+ on_exception
420
+ end
421
+
422
+ #
423
+ # items is an array item_name's or Aws::SdbInterface::Item.new(o.id, o.attributes, true)
424
+ def batch_delete_attributes(domain_name, items)
425
+ params = {'DomainName' => domain_name}
426
+ i = 0
427
+ items.each do |item|
428
+ prefix = "Item." + i.to_s + "."
429
+ if item.is_a?(String)
430
+ params[prefix + "ItemName"] = item
431
+ else
432
+ params[prefix + "ItemName"] = item.item_name
433
+ params.merge!(pack_attributes(item.attributes, item.replace, prefix))
434
+ end
435
+ i += 1
436
+ end
437
+ link = generate_request("BatchDeleteAttributes", params)
438
+ request_info(link, QSdbSimpleParser.new)
439
+ rescue Exception
440
+ on_exception
441
+ end
442
+
443
+ # Retrieve SDB item's attribute(s).
444
+ #
445
+ # Returns a hash:
446
+ # { :box_usage => string,
447
+ # :request_id => string,
448
+ # :attributes => { 'nameA' => [valueA1,..., valueAN],
449
+ # ... ,
450
+ # 'nameZ' => [valueZ1,..., valueZN] } }
451
+ #
452
+ # Example:
453
+ # # request all attributes
454
+ # sdb.get_attributes('family', 'toys') # => { :attributes => {"cat" => ["clew", "Jons_socks", "mouse"] },
455
+ # "Silvia" => ["beetle", "rolling_pin", "kids"],
456
+ # "Jon" => ["vacuum_cleaner", "hammer", "spade"]},
457
+ # :box_usage => "0.0000093222",
458
+ # :request_id => "81273d21-000-1111-b3f9-512d91d29ac8" }
459
+ #
460
+ # # request cat's attributes only
461
+ # sdb.get_attributes('family', 'toys', 'cat') # => { :attributes => {"cat" => ["clew", "Jons_socks", "mouse"] },
462
+ # :box_usage => "0.0000093222",
463
+ # :request_id => "81273d21-001-1111-b3f9-512d91d29ac8" }
464
+ #
465
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_GetAttributes.html
466
+ #
467
+ def get_attributes(domain_name, item_name, attribute_name=nil, consistent_read = nil)
468
+ link = generate_request("GetAttributes", 'DomainName' => domain_name,
469
+ 'ItemName' => item_name,
470
+ 'AttributeName' => attribute_name,
471
+ 'ConsistentRead' => consistent_read)
472
+ res = request_info(link, QSdbGetAttributesParser.new)
473
+ res[:attributes].each_value do |values|
474
+ values.collect! { |e| sdb_to_ruby(e) }
475
+ end
476
+ res
477
+ rescue Exception
478
+ on_exception
479
+ end
480
+
481
+ # Delete value, attribute or item.
482
+ #
483
+ # Example:
484
+ # # delete 'vodka' and 'girls' from 'Jon' and 'mice' from 'cat'.
485
+ # sdb.delete_attributes 'family', 'toys', { 'Jon' => ['vodka', 'girls'], 'cat' => ['mice'] }
486
+ #
487
+ # # delete the all the values from attributes (i.e. delete the attributes)
488
+ # sdb.delete_attributes 'family', 'toys', { 'Jon' => [], 'cat' => [] }
489
+ # # or
490
+ # sdb.delete_attributes 'family', 'toys', [ 'Jon', 'cat' ]
491
+ #
492
+ # # delete all the attributes from item 'toys' (i.e. delete the item)
493
+ # sdb.delete_attributes 'family', 'toys'
494
+ #
495
+ # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_DeleteAttributes.html
496
+ #
497
+ def delete_attributes(domain_name, item_name, attributes = nil)
498
+ params = {'DomainName' => domain_name,
499
+ 'ItemName' => item_name}.merge(pack_attributes(attributes))
500
+ link = generate_request("DeleteAttributes", params)
501
+ request_info(link, QSdbSimpleParser.new)
502
+ rescue Exception
503
+ on_exception
504
+ end
505
+
506
+
507
+ # QUERY:
508
+
509
+ # Perform a query on SDB.
510
+ #
511
+ # Returns a hash:
512
+ # { :box_usage => string,
513
+ # :request_id => string,
514
+ # :next_token => string,
515
+ # :items => [ItemName1,..., ItemNameN] }
516
+ #
517
+ # Example:
518
+ #
519
+ # query = "['cat' = 'clew']"
520
+ # sdb.query('family', query) #=> hash of data
521
+ # sdb.query('family', query, 10) #=> hash of data with max of 10 items
522
+ #
523
+ # If a block is given, query will iteratively yield results to it as long as the block continues to return true.
524
+ #
525
+ # # List 10 items per iteration. Don't
526
+ # # forget to escape single quotes and backslashes and wrap all the items in single quotes.
527
+ # query = "['cat'='clew'] union ['dog'='Jon\\'s boot']"
528
+ # sdb.query('family', query, 10) do |result|
529
+ # puts result.inspect
530
+ # true
531
+ # end
532
+ #
533
+ # # Same query using automatic escaping...to use the auto escape, pass the query and its params as an array:
534
+ # query = [ "['cat'=?] union ['dog'=?]", "clew", "Jon's boot" ]
535
+ # sdb.query('family', query)
536
+ #
537
+ # query = [ "['cat'=?] union ['dog'=?] sort 'cat' desc", "clew", "Jon's boot" ]
538
+ # sdb.query('family', query)
539
+ #
540
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_Query.html
541
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SortingData.html
542
+ #
543
+ def query(domain_name, query_expression = nil, max_number_of_items = nil, next_token = nil, consistent_read = nil)
544
+ query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)
545
+ @last_query_expression = query_expression
546
+ #
547
+ request_params = {'DomainName' => domain_name,
548
+ 'QueryExpression' => query_expression,
549
+ 'MaxNumberOfItems' => max_number_of_items,
550
+ 'NextToken' => next_token,
551
+ 'ConsistentRead' => consistent_read}
552
+ link = generate_request("Query", request_params)
553
+ result = request_info(link, QSdbQueryParser.new)
554
+ # return result if no block given
555
+ return result unless block_given?
556
+ # loop if block if given
557
+ begin
558
+ # the block must return true if it wanna continue
559
+ break unless yield(result) && result[:next_token]
560
+ # make new request
561
+ request_params['NextToken'] = result[:next_token]
562
+ link = generate_request("Query", request_params)
563
+ result = request_info(link, QSdbQueryParser.new)
564
+ end while true
565
+ rescue Exception
566
+ on_exception
567
+ end
568
+
569
+ # Perform a query and fetch specified attributes.
570
+ # If attributes are not specified then fetches the whole list of attributes.
571
+ #
572
+ #
573
+ # Returns a hash:
574
+ # { :box_usage => string,
575
+ # :request_id => string,
576
+ # :next_token => string,
577
+ # :items => [ { ItemName1 => { attribute1 => value1, ... attributeM => valueM } },
578
+ # { ItemName2 => {...}}, ... ]
579
+ #
580
+ # Example:
581
+ #
582
+ # sdb.query_with_attributes(domain, ['hobby', 'country'], "['gender'='female'] intersection ['name' starts-with ''] sort 'name'") #=>
583
+ # { :request_id => "06057228-70d0-4487-89fb-fd9c028580d3",
584
+ # :items =>
585
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
586
+ # { "hobby" => ["cooking", "flowers", "cats"],
587
+ # "country" => ["Russia"]}},
588
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
589
+ # { "hobby" => ["patchwork", "bundle jumping"],
590
+ # "country" => ["USA"]}}, ... ],
591
+ # :box_usage=>"0.0000504786"}
592
+ #
593
+ # sdb.query_with_attributes(domain, [], "['gender'='female'] intersection ['name' starts-with ''] sort 'name'") #=>
594
+ # { :request_id => "75bb19db-a529-4f69-b86f-5e3800f79a45",
595
+ # :items =>
596
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
597
+ # { "hobby" => ["cooking", "flowers", "cats"],
598
+ # "name" => ["Mary"],
599
+ # "country" => ["Russia"],
600
+ # "gender" => ["female"],
601
+ # "id" => ["035f1ba8-dbd8-11dd-80bd-001bfc466dd7"]}},
602
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
603
+ # { "hobby" => ["patchwork", "bundle jumping"],
604
+ # "name" => ["Mary"],
605
+ # "country" => ["USA"],
606
+ # "gender" => ["female"],
607
+ # "id" => ["0327614a-dbd8-11dd-80bd-001bfc466dd7"]}}, ... ],
608
+ # :box_usage=>"0.0000506668"}
609
+ #
610
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_QueryWithAttributes.html
611
+ #
612
+ def query_with_attributes(domain_name, attributes=[], query_expression = nil, max_number_of_items = nil, next_token = nil, consistent_read = nil)
613
+ attributes = attributes.to_a
614
+ query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)
615
+ @last_query_expression = query_expression
616
+ #
617
+ request_params = {'DomainName' => domain_name,
618
+ 'QueryExpression' => query_expression,
619
+ 'MaxNumberOfItems' => max_number_of_items,
620
+ 'NextToken' => next_token,
621
+ 'ConsistentRead' => consistent_read}
622
+ attributes.each_with_index do |attribute, idx|
623
+ request_params["AttributeName.#{idx+1}"] = attribute
624
+ end
625
+ link = generate_request("QueryWithAttributes", request_params)
626
+ result = select_response_to_ruby(request_info(link, QSdbQueryWithAttributesParser.new))
627
+ # return result if no block given
628
+ return result unless block_given?
629
+ # loop if block if given
630
+ begin
631
+ # the block must return true if it wanna continue
632
+ break unless yield(result) && result[:next_token]
633
+ # make new request
634
+ request_params['NextToken'] = result[:next_token]
635
+ link = generate_request("QueryWithAttributes", request_params)
636
+ result = select_response_to_ruby(request_info(link, QSdbQueryWithAttributesParser.new))
637
+ end while true
638
+ rescue Exception
639
+ on_exception
640
+ end
641
+
642
+ # Perform SQL-like select and fetch attributes.
643
+ # Attribute values must be quoted with a single or double quote. If a quote appears within the attribute value, it must be escaped with the same quote symbol as shown in the following example.
644
+ # (Use array to pass select_expression params to avoid manual escaping).
645
+ #
646
+ # sdb.select(["select * from my_domain where gender=?", 'female']) #=>
647
+ # {:request_id =>"8241b843-0fb9-4d66-9100-effae12249ec",
648
+ # :items =>
649
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
650
+ # {"hobby" => ["cooking", "flowers", "cats"],
651
+ # "name" => ["Mary"],
652
+ # "country" => ["Russia"],
653
+ # "gender" => ["female"],
654
+ # "id" => ["035f1ba8-dbd8-11dd-80bd-001bfc466dd7"]}},
655
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
656
+ # {"hobby" => ["patchwork", "bundle jumping"],
657
+ # "name" => ["Mary"],
658
+ # "country" => ["USA"],
659
+ # "gender" => ["female"],
660
+ # "id" => ["0327614a-dbd8-11dd-80bd-001bfc466dd7"]}}, ... ]
661
+ # :box_usage =>"0.0000506197"}
662
+ #
663
+ # sdb.select('select country, name from my_domain') #=>
664
+ # {:request_id=>"b1600198-c317-413f-a8dc-4e7f864a940a",
665
+ # :items=>
666
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["Russia"]} },
667
+ # { "376d2e00-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Putin"], "country"=>["Russia"]} },
668
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["USA"]} },
669
+ # { "372ebbd4-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Bush"], "country"=>["USA"]} },
670
+ # { "37a4e552-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Medvedev"], "country"=>["Russia"]} },
671
+ # { "38278dfe-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["Russia"]} },
672
+ # { "37df6c36-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["USA"]} } ],
673
+ # :box_usage=>"0.0000777663"}
674
+ #
675
+ # options:
676
+ # :next_token
677
+ # :consistent_read
678
+ # :retries => maximum number of times to retry this query on an error response.
679
+ #
680
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_Select.html
681
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingSelect.html
682
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDBLimits.html
683
+ #
684
+ def select(select_expression, next_token = nil, consistent_read = nil)
685
+ select_expression = query_expression_from_array(select_expression) if select_expression.is_a?(Array)
686
+ @last_query_expression = select_expression
687
+
688
+ options = {}
689
+ if next_token.is_a?(Hash)
690
+ options = next_token
691
+ next_token = options[:next_token]
692
+ consistent_read = options[:consistent_read]
693
+ end
694
+
695
+ #
696
+ request_params = {'SelectExpression' => select_expression,
697
+ 'NextToken' => next_token,
698
+ 'ConsistentRead' => consistent_read}
699
+ link = generate_request("Select", request_params, options)
700
+ result = select_response_to_ruby(request_info(link, QSdbSelectParser.new, options))
701
+ return result unless block_given?
702
+ # loop if block if given
703
+ begin
704
+ # the block must return true if it wanna continue
705
+ break unless yield(result) && result[:next_token]
706
+ # make new request
707
+ request_params['NextToken'] = result[:next_token]
708
+ link = generate_request("Select", request_params)
709
+ result = select_response_to_ruby(request_info(link, QSdbSelectParser.new, options))
710
+ end while true
711
+ rescue Exception
712
+ on_exception
713
+ end
714
+
715
+ class Item
716
+ attr_accessor :item_name, :attributes, :replace
717
+
718
+ def initialize(item_name, attributes, replace = false)
719
+ @item_name = item_name
720
+ @attributes = attributes
721
+ @replace = replace
722
+ end
723
+ end
724
+
725
+ #-----------------------------------------------------------------
726
+ # PARSERS:
727
+ #-----------------------------------------------------------------
728
+ class QSdbListDomainParser < AwsParser #:nodoc:
729
+ def reset
730
+ @result = {:domains => []}
731
+ end
732
+
733
+ def tagend(name)
734
+ case name
735
+ when 'NextToken' then
736
+ @result[:next_token] = @text
737
+ when 'DomainName' then
738
+ @result[:domains] << @text
739
+ when 'BoxUsage' then
740
+ @result[:box_usage] = @text
741
+ when 'RequestId' then
742
+ @result[:request_id] = @text
743
+ end
744
+ end
745
+ end
746
+
747
+ class QSdbDomainMetadataParser < AwsParser #:nodoc:
748
+ def reset
749
+ @result = {}
750
+ end
751
+
752
+ def tagend(name)
753
+ case name
754
+ when 'Timestamp' then
755
+ @result[:timestamp] = @text
756
+ when 'ItemCount' then
757
+ @result[:item_count] = @text.to_i
758
+ when 'AttributeValueCount' then
759
+ @result[:attribute_value_count] = @text.to_i
760
+ when 'AttributeNameCount' then
761
+ @result[:attribute_name_acount] = @text.to_i
762
+ when 'ItemNamesSizeBytes' then
763
+ @result[:item_names_size_bytes] = @text.to_i
764
+ when 'AttributeValuesSizeBytes' then
765
+ @result[:attributes_values_size_bytes] = @text.to_i
766
+ when 'AttributeNamesSizeBytes' then
767
+ @result[:attributes_names_size_bytes] = @text.to_i
768
+
769
+ end
770
+ end
771
+ end
772
+
773
+
774
+ class QSdbSimpleParser < AwsParser #:nodoc:
775
+ def reset
776
+ @result = {}
777
+ end
778
+
779
+ def tagend(name)
780
+ case name
781
+ when 'BoxUsage' then
782
+ @result[:box_usage] = @text
783
+ when 'RequestId' then
784
+ @result[:request_id] = @text
785
+ end
786
+ end
787
+ end
788
+
789
+ class QSdbGetAttributesParser < AwsParser #:nodoc:
790
+ def reset
791
+ @last_attribute_name = nil
792
+ @result = {:attributes => {}}
793
+ end
794
+
795
+ def tagend(name)
796
+ case name
797
+ when 'Name' then
798
+ @last_attribute_name = @text
799
+ when 'Value' then
800
+ (@result[:attributes][@last_attribute_name] ||= []) << @text
801
+ when 'BoxUsage' then
802
+ @result[:box_usage] = @text
803
+ when 'RequestId' then
804
+ @result[:request_id] = @text
805
+ end
806
+ end
807
+ end
808
+
809
+ class QSdbQueryParser < AwsParser #:nodoc:
810
+ def reset
811
+ @result = {:items => []}
812
+ end
813
+
814
+ def tagend(name)
815
+ case name
816
+ when 'ItemName' then
817
+ @result[:items] << @text
818
+ when 'BoxUsage' then
819
+ @result[:box_usage] = @text
820
+ when 'RequestId' then
821
+ @result[:request_id] = @text
822
+ when 'NextToken' then
823
+ @result[:next_token] = @text
824
+ end
825
+ end
826
+ end
827
+
828
+ class QSdbQueryWithAttributesParser < AwsParser #:nodoc:
829
+ def reset
830
+ @result = {:items => []}
831
+ end
832
+
833
+ def tagend(name)
834
+ case name
835
+ when 'Name'
836
+ case @xmlpath
837
+ when 'QueryWithAttributesResponse/QueryWithAttributesResult/Item'
838
+ @item = @text
839
+ @result[:items] << {@item => {}}
840
+ when 'QueryWithAttributesResponse/QueryWithAttributesResult/Item/Attribute'
841
+ @attribute = @text
842
+ @result[:items].last[@item][@attribute] ||= []
843
+ end
844
+ when 'RequestId' then
845
+ @result[:request_id] = @text
846
+ when 'BoxUsage' then
847
+ @result[:box_usage] = @text
848
+ when 'NextToken' then
849
+ @result[:next_token] = @text
850
+ when 'Value' then
851
+ @result[:items].last[@item][@attribute] << @text
852
+ end
853
+ end
854
+ end
855
+
856
+ class QSdbSelectParser < AwsParser #:nodoc:
857
+ def reset
858
+ @result = {:items => []}
859
+ end
860
+
861
+ def tagend(name)
862
+ case name
863
+ when 'Name'
864
+ case @xmlpath
865
+ when 'SelectResponse/SelectResult/Item'
866
+ @item = @text
867
+ @result[:items] << {@item => {}}
868
+ when 'SelectResponse/SelectResult/Item/Attribute'
869
+ @attribute = @text
870
+ @result[:items].last[@item][@attribute] ||= []
871
+ end
872
+ when 'RequestId' then
873
+ @result[:request_id] = @text
874
+ when 'BoxUsage' then
875
+ @result[:box_usage] = @text
876
+ when 'NextToken' then
877
+ @result[:next_token] = @text
878
+ when 'Value' then
879
+ @result[:items].last[@item][@attribute] << @text
880
+ end
881
+ end
882
+ end
883
+
884
+ end
885
+
886
+ end