From 3a72c4374455bd0e1a9c2f06ff3a8cdce2337eb6 Mon Sep 17 00:00:00 2001 From: Ben Ouattara Date: Wed, 17 May 2017 10:31:03 -0400 Subject: [PATCH 1/8] implemented and tested column write time date --- lib/cassava/client.rb | 14 ++++++++++++++ test/cassava/client_test.rb | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/cassava/client.rb b/lib/cassava/client.rb index 67e00be..02e6f67 100644 --- a/lib/cassava/client.rb +++ b/lib/cassava/client.rb @@ -50,6 +50,11 @@ def select_ttl(table, target_attr, where_arguments) executor.execute(prepared_statement, :arguments => where_arguments.values).rows.first["ttl(#{target_attr})"] end + def select_writetime(table, target_attr, where_arguments) + prepared_statement = select_writetime_statement(table, target_attr, where_arguments) + executor.execute(prepared_statement, :arguments => where_arguments.values).rows.first["writetime(#{target_attr})"] + end + # @param table [Symbol] the table name # @param columns [Array 'i' }) + assert_match /SELECT WRITETIME/, statement.cql + end + + should 'correctly fetch the timestamp of a given column' do + item = { :id => 'i', :a => 1, :b => 'b', :c => "item", :d => 1 } + @client.insert(:test, item) + + timestamp = @client.select_writetime(:test, :d, { :id => 'i' }) + assert timestamp + end + end + context 'delete' do setup do @client.insert(:test, :id => 'i', :a => 2, :b => 'a', :c => '1', :d => 1) From 6c76f900f0015d2bad89aa2218e1c47ab1cceaa8 Mon Sep 17 00:00:00 2001 From: Ben Ouattara Date: Wed, 17 May 2017 10:31:36 -0400 Subject: [PATCH 2/8] increase version --- lib/cassava/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cassava/version.rb b/lib/cassava/version.rb index a17a101..00d3bac 100644 --- a/lib/cassava/version.rb +++ b/lib/cassava/version.rb @@ -1,3 +1,3 @@ module Cassava - VERSION = "0.1.4" + VERSION = "0.1.5" end From 151d59af646fe6e9f2fdc0f37612df1863754ae8 Mon Sep 17 00:00:00 2001 From: Ben Ouattara Date: Thu, 18 May 2017 15:46:59 -0400 Subject: [PATCH 3/8] added support for timestamp overwrite --- lib/cassava/client.rb | 15 +++++++++++---- test/cassava/client_test.rb | 12 +++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/cassava/client.rb b/lib/cassava/client.rb index 02e6f67..55d01ac 100644 --- a/lib/cassava/client.rb +++ b/lib/cassava/client.rb @@ -13,8 +13,11 @@ def initialize(session, opts = {}) # @see #insert def insert_async(table, data) ttl = data.delete(:ttl) + timestamp = data.delete(:timestamp) consistency = data.delete(:consistency) - statement = insert_statement(table, data, ttl) + + statement = insert_statement(table, data, ttl, timestamp) + if consistency.nil? executor.execute_async(statement, :arguments => data.values) else @@ -26,8 +29,11 @@ def insert_async(table, data) # @param data [Hash] A hash of column names to data, which will be inserted into the table def insert(table, data) ttl = data.delete(:ttl) + timestamp = data.delete(:timestamp) consistency = data.delete(:consistency) - statement = insert_statement(table, data, ttl) + + statement = insert_statement(table, data, ttl, timestamp) + if consistency.nil? executor.execute(statement, :arguments => data.values) else @@ -78,10 +84,11 @@ def execute(statement, opts = {}) private - def insert_statement(table, data, ttl = nil) + def insert_statement(table, data, ttl = nil, timestamp = nil) column_names = data.keys statement_cql = "INSERT INTO #{table} (#{column_names.join(', ')}) VALUES (#{column_names.map { |x| '?' }.join(',')})" - statement_cql += " USING TTL #{ttl}" if ttl + statement_cql += " USING TTL #{ttl} " if ttl + statement_cql += " USING TIMESTAMP #{timestamp}" if timestamp executor.prepare(statement_cql) end diff --git a/test/cassava/client_test.rb b/test/cassava/client_test.rb index 4161c24..f25ac3c 100644 --- a/test/cassava/client_test.rb +++ b/test/cassava/client_test.rb @@ -47,9 +47,19 @@ def string_keys(hash) item = { :id => 'i', :a => 1, :b => 'b', :c => "'\"item(", :d => 1, :ttl => ttl } @client.insert(:test, item) - assert @client.send(:insert_statement, :test, item, ttl).cql =~ /\sUSING\sTTL\s#{ttl}$/ + assert @client.send(:insert_statement, :test, item, ttl).cql =~ /\sUSING\sTTL\s#{ttl}/ assert_equal string_keys(item), @client.select(:test).execute.first end + + should 'allow the insertion with a timestamp' do + timestamp = Time.now.to_i + item = { :id => 'i', :a => 1, :b => 'b', :c => "'\"item(", :d => 1, :timestamp => timestamp } + @client.insert(:test, item) + + assert @client.send(:insert_statement, :test, item, nil, timestamp).cql =~ /\sUSING\sTIMESTAMP\s#{timestamp}/ + saved_timestamp = @client.select_writetime(:test, :d, { :id => 'i' }) + assert_equal timestamp, saved_timestamp + end end context 'select' do From faa3c79d6a7bcc2545ced93ac0f8107986165669 Mon Sep 17 00:00:00 2001 From: Ben Ouattara Date: Thu, 18 May 2017 16:37:14 -0400 Subject: [PATCH 4/8] fix insertion of ttl and timestamp --- lib/cassava/client.rb | 10 ++++++++-- test/cassava/client_test.rb | 12 ++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/cassava/client.rb b/lib/cassava/client.rb index 55d01ac..9e48c1f 100644 --- a/lib/cassava/client.rb +++ b/lib/cassava/client.rb @@ -87,8 +87,14 @@ def execute(statement, opts = {}) def insert_statement(table, data, ttl = nil, timestamp = nil) column_names = data.keys statement_cql = "INSERT INTO #{table} (#{column_names.join(', ')}) VALUES (#{column_names.map { |x| '?' }.join(',')})" - statement_cql += " USING TTL #{ttl} " if ttl - statement_cql += " USING TIMESTAMP #{timestamp}" if timestamp + + if ttl && timestamp + statement_cql += " USING TTL #{ttl} AND TIMESTAMP #{timestamp}" + elsif ttl + statement_cql += " USING TTL #{ttl} " + elsif timestamp + statement_cql += " USING TIMESTAMP #{timestamp}" + end executor.prepare(statement_cql) end diff --git a/test/cassava/client_test.rb b/test/cassava/client_test.rb index f25ac3c..c9ec8aa 100644 --- a/test/cassava/client_test.rb +++ b/test/cassava/client_test.rb @@ -60,6 +60,18 @@ def string_keys(hash) saved_timestamp = @client.select_writetime(:test, :d, { :id => 'i' }) assert_equal timestamp, saved_timestamp end + + should 'allow the insertion of a ttl and a timestamp' do + ttl = 12345 + timestamp = Time.now.to_i + item = { :id => 'i', :a => 1, :b => 'b', :c => "'\"item(", :d => 1, :ttl => ttl, :timestamp => timestamp } + @client.insert(:test, item) + + assert @client.send(:insert_statement, :test, item, ttl, timestamp).cql =~ /\sUSING\sTTL\s#{ttl}\sAND\sTIMESTAMP\s#{timestamp}/ + assert_equal string_keys(item), @client.select(:test).execute.first + saved_timestamp = @client.select_writetime(:test, :d, { :id => 'i' }) + assert_equal timestamp, saved_timestamp + end end context 'select' do From 835f6994d408641e692d26a6ebc1a7803c125e1d Mon Sep 17 00:00:00 2001 From: Ben Ouattara Date: Thu, 18 May 2017 18:00:57 -0400 Subject: [PATCH 5/8] fixed name conflict --- lib/cassava/client.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/cassava/client.rb b/lib/cassava/client.rb index 9e48c1f..4548867 100644 --- a/lib/cassava/client.rb +++ b/lib/cassava/client.rb @@ -13,10 +13,10 @@ def initialize(session, opts = {}) # @see #insert def insert_async(table, data) ttl = data.delete(:ttl) - timestamp = data.delete(:timestamp) + optional_timestamp = data.delete(:optional_timestamp) consistency = data.delete(:consistency) - statement = insert_statement(table, data, ttl, timestamp) + statement = insert_statement(table, data, ttl, optional_timestamp) if consistency.nil? executor.execute_async(statement, :arguments => data.values) @@ -29,10 +29,10 @@ def insert_async(table, data) # @param data [Hash] A hash of column names to data, which will be inserted into the table def insert(table, data) ttl = data.delete(:ttl) - timestamp = data.delete(:timestamp) + optional_timestamp = data.delete(:optional_timestamp) consistency = data.delete(:consistency) - statement = insert_statement(table, data, ttl, timestamp) + statement = insert_statement(table, data, ttl, optional_timestamp) if consistency.nil? executor.execute(statement, :arguments => data.values) @@ -84,16 +84,16 @@ def execute(statement, opts = {}) private - def insert_statement(table, data, ttl = nil, timestamp = nil) + def insert_statement(table, data, ttl = nil, optional_timestamp = nil) column_names = data.keys statement_cql = "INSERT INTO #{table} (#{column_names.join(', ')}) VALUES (#{column_names.map { |x| '?' }.join(',')})" - if ttl && timestamp - statement_cql += " USING TTL #{ttl} AND TIMESTAMP #{timestamp}" + if ttl && optional_timestamp + statement_cql += " USING TTL #{ttl} AND TIMESTAMP #{optional_timestamp}" elsif ttl statement_cql += " USING TTL #{ttl} " - elsif timestamp - statement_cql += " USING TIMESTAMP #{timestamp}" + elsif optional_timestamp + statement_cql += " USING TIMESTAMP #{optional_timestamp}" end executor.prepare(statement_cql) From d1b669e4c69eed9fbf676b6db36d085807edccaa Mon Sep 17 00:00:00 2001 From: Johnny Zheng Date: Wed, 28 Jun 2017 13:43:40 -0400 Subject: [PATCH 6/8] added ability to use timestamps with delete --- lib/cassava/client.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/cassava/client.rb b/lib/cassava/client.rb index 4548867..4e2f33b 100644 --- a/lib/cassava/client.rb +++ b/lib/cassava/client.rb @@ -64,8 +64,8 @@ def select_writetime(table, target_attr, where_arguments) # @param table [Symbol] the table name # @param columns [Array] Columns to delete -- defaults to all. # @return [StatementBuilder] - def delete(table, columns = nil) - add_clause(DeleteClause.new(table, columns), :main) + def delete(table, columns = nil, timestamp=nil) + add_clause(DeleteClause.new(table, columns, timestamp), :main) end # Condition the query based on a condition @@ -242,10 +242,14 @@ def to_s end end - DeleteClause = Struct.new(:table, :columns) do + DeleteClause = Struct.new(:table, :columns, :timestamp) do def to_s - if columns + if columns && timestamp + "DELETE #{columns.join(', ')} from #{table} USING TIMESTAMP #{timestamp}" + elsif columns "DELETE #{columns.join(', ')} from #{table}" + elsif timestamp + "DELETE from #{table} USING TIMESTAMP #{timestamp}" else "DELETE from #{table}" end From 8dd114af8bd26decaf282f0f73ef6d86edbbdaa1 Mon Sep 17 00:00:00 2001 From: Johnny Zheng Date: Thu, 29 Jun 2017 09:45:31 -0400 Subject: [PATCH 7/8] added test for delete with timestamp --- test/cassava/client_test.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/cassava/client_test.rb b/test/cassava/client_test.rb index c9ec8aa..1004584 100644 --- a/test/cassava/client_test.rb +++ b/test/cassava/client_test.rb @@ -53,7 +53,7 @@ def string_keys(hash) should 'allow the insertion with a timestamp' do timestamp = Time.now.to_i - item = { :id => 'i', :a => 1, :b => 'b', :c => "'\"item(", :d => 1, :timestamp => timestamp } + item = { :id => 'i', :a => 1, :b => 'b', :c => "'\"item(", :d => 1, :optional_timestamp => timestamp } @client.insert(:test, item) assert @client.send(:insert_statement, :test, item, nil, timestamp).cql =~ /\sUSING\sTIMESTAMP\s#{timestamp}/ @@ -64,7 +64,7 @@ def string_keys(hash) should 'allow the insertion of a ttl and a timestamp' do ttl = 12345 timestamp = Time.now.to_i - item = { :id => 'i', :a => 1, :b => 'b', :c => "'\"item(", :d => 1, :ttl => ttl, :timestamp => timestamp } + item = { :id => 'i', :a => 1, :b => 'b', :c => "'\"item(", :d => 1, :ttl => ttl, :optional_timestamp => timestamp } @client.insert(:test, item) assert @client.send(:insert_statement, :test, item, ttl, timestamp).cql =~ /\sUSING\sTTL\s#{ttl}\sAND\sTIMESTAMP\s#{timestamp}/ @@ -242,6 +242,15 @@ def string_keys(hash) assert_nil items.first['d'] end + should 'delete with a timestamp' do + timestamp = Time.now.to_i * 1000000 + Time.now.usec + assert @client.delete(:test, [:c, :d], timestamp).where(:id => 'i', :a => 2, :b => 'a').statement =~ /\sUSING\sTIMESTAMP\s#{timestamp}/ + @client.delete(:test, [:c, :d]).where(:id => 'i', :a => 2, :b => 'a').execute + items = @client.select(:test).where(:id => 'i', :a => 2).execute + assert_nil items.first['c'] + assert_nil items.first['d'] + end + context 'hash arguments' do should 'allow single and double quotes in the value' do # no error raised From 569202c75d85cb2ac00a623a43c43e9724508221 Mon Sep 17 00:00:00 2001 From: Johnny Zheng Date: Thu, 29 Jun 2017 09:49:15 -0400 Subject: [PATCH 8/8] timestamp should be in microseconds --- test/cassava/client_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cassava/client_test.rb b/test/cassava/client_test.rb index 1004584..af3a17b 100644 --- a/test/cassava/client_test.rb +++ b/test/cassava/client_test.rb @@ -52,7 +52,7 @@ def string_keys(hash) end should 'allow the insertion with a timestamp' do - timestamp = Time.now.to_i + timestamp = Time.now.to_i * 1000000 + Time.now.usec item = { :id => 'i', :a => 1, :b => 'b', :c => "'\"item(", :d => 1, :optional_timestamp => timestamp } @client.insert(:test, item) @@ -63,7 +63,7 @@ def string_keys(hash) should 'allow the insertion of a ttl and a timestamp' do ttl = 12345 - timestamp = Time.now.to_i + timestamp = Time.now.to_i * 1000000 + Time.now.usec item = { :id => 'i', :a => 1, :b => 'b', :c => "'\"item(", :d => 1, :ttl => ttl, :optional_timestamp => timestamp } @client.insert(:test, item)