diff --git a/features/basics/schema_metadata.feature b/features/basics/schema_metadata.feature index dabd45d66..eb1502b5f 100644 --- a/features/basics/schema_metadata.feature +++ b/features/basics/schema_metadata.feature @@ -46,13 +46,12 @@ Feature: Schema Metadata ) """ - @cassandra-version-specific @cassandra-version-less-3.0 Scenario: Getting index metadata Given the following schema: """cql CREATE KEYSPACE simplex WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3}; - CREATE TABLE simplex.test_table (f1 int primary key, f2 int); - CREATE INDEX ind1 ON simplex.test_table (f2); + CREATE TABLE simplex.test_table (a int primary key, b int); + CREATE INDEX ind1 ON simplex.test_table (b); """ And the following example: """ruby @@ -60,23 +59,33 @@ Feature: Schema Metadata cluster = Cassandra.cluster - cluster.keyspace('simplex').table('test_table').each_index do |index| - puts index.to_cql - end + index = cluster.keyspace('simplex').table('test_table').index('ind1') + puts index.to_cql + + puts "" + puts "Name: #{index.name}" + puts "Table name: #{index.table.name}" + puts "Kind: #{index.kind}" + puts "Target: #{index.target}" """ When it is executed Then its output should contain: """cql - CREATE INDEX "ind1" ON simplex.test_table ("f2"); + CREATE INDEX "ind1" ON simplex.test_table (b); + + Name: ind1 + Table name: test_table + Kind: composites + Target: b """ @cassandra-version-specific @cassandra-version-3.0 - Scenario: Getting index metadata on 3.0 + Scenario: Getting index metadata on full collections Given the following schema: """cql CREATE KEYSPACE simplex WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3}; - CREATE TABLE simplex.test_table (f1 int primary key, f2 int); - CREATE INDEX ind1 ON simplex.test_table (f2); + CREATE TABLE simplex.test_table (a int PRIMARY KEY, b frozen>); + CREATE INDEX ind1 ON simplex.test_table (full(b)); """ And the following example: """ruby @@ -84,25 +93,34 @@ Feature: Schema Metadata cluster = Cassandra.cluster - cluster.keyspace('simplex').table('test_table').each_index do |index| - puts index.to_cql - end + index = cluster.keyspace('simplex').table('test_table').index('ind1') + puts index.to_cql + + puts "" + puts "Name: #{index.name}" + puts "Table name: #{index.table.name}" + puts "Kind: #{index.kind}" + puts "Target: #{index.target}" """ When it is executed Then its output should contain: """cql - CREATE INDEX "ind1" ON simplex.test_table (f2); + CREATE INDEX "ind1" ON simplex.test_table (full(b)); + + Name: ind1 + Table name: test_table + Kind: composites + Target: full(b) """ @cassandra-version-specific @cassandra-version-3.0 - Scenario: Getting materialized view metadata + Scenario: Getting multiple index metadata on same column Given the following schema: """cql CREATE KEYSPACE simplex WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3}; - CREATE TABLE simplex.test_table (f1 int PRIMARY KEY, f2 int, f3 int) ; - CREATE MATERIALIZED VIEW simplex.test_view AS - SELECT f1, f2 FROM simplex.test_table WHERE f2 IS NOT NULL - PRIMARY KEY (f1, f2); + CREATE TABLE simplex.test_table (a int PRIMARY KEY, b map); + CREATE INDEX ind1 ON simplex.test_table (keys(b)); + CREATE INDEX ind2 ON simplex.test_table (values(b)); """ And the following example: """ruby @@ -110,31 +128,33 @@ Feature: Schema Metadata cluster = Cassandra.cluster - view = cluster.keyspace('simplex').materialized_view('test_view') - puts view.to_cql + index = cluster.keyspace('simplex').table('test_table').index('ind1') + puts index.to_cql + + puts "" + puts "Name: #{index.name}" + puts "Target: #{index.target}" + + puts "" + index = cluster.keyspace('simplex').table('test_table').index('ind2') + puts index.to_cql + + puts "" + puts "Name: #{index.name}" + puts "Target: #{index.target}" """ When it is executed Then its output should contain: """cql - CREATE MATERIALIZED VIEW simplex.test_view AS - SELECT "f1", "f2" - FROM simplex.test_table - WHERE f2 IS NOT NULL - PRIMARY KEY (("f1"), "f2") - WITH bloom_filter_fp_chance = 0.01 - AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'} - AND comment = '' - AND compaction = {'class': 'SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'} - AND compression = {'chunk_length_in_kb': '64', 'class': 'LZ4Compressor'} - AND crc_check_chance = 1.0 - AND dclocal_read_repair_chance = 0.1 - AND default_time_to_live = 0 - AND gc_grace_seconds = 864000 - AND max_index_interval = 2048 - AND memtable_flush_period_in_ms = 0 - AND min_index_interval = 128 - AND read_repair_chance = 0.0 - AND speculative_retry = '99PERCENTILE'; + CREATE INDEX "ind1" ON simplex.test_table (keys(b)); + + Name: ind1 + Target: keys(b) + + CREATE INDEX "ind2" ON simplex.test_table (values(b)); + + Name: ind2 + Target: values(b) """ @cassandra-version-specific @cassandra-version-2.1 @@ -276,3 +296,46 @@ Feature: Schema Metadata State function: avgstate Final function: avgfinal """ + + @cassandra-version-specific @cassandra-version-3.0 + Scenario: Getting materialized view metadata + Given the following schema: + """cql + CREATE KEYSPACE simplex WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3}; + CREATE TABLE simplex.test_table (f1 int PRIMARY KEY, f2 int, f3 int) ; + CREATE MATERIALIZED VIEW simplex.test_view AS + SELECT f1, f2 FROM simplex.test_table WHERE f2 IS NOT NULL + PRIMARY KEY (f1, f2); + """ + And the following example: + """ruby + require 'cassandra' + + cluster = Cassandra.cluster + + view = cluster.keyspace('simplex').materialized_view('test_view') + puts view.to_cql + """ + When it is executed + Then its output should contain: + """cql + CREATE MATERIALIZED VIEW simplex.test_view AS + SELECT "f1", "f2" + FROM simplex.test_table + WHERE f2 IS NOT NULL + PRIMARY KEY (("f1"), "f2") + WITH bloom_filter_fp_chance = 0.01 + AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'} + AND comment = '' + AND compaction = {'class': 'SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'} + AND compression = {'chunk_length_in_kb': '64', 'class': 'LZ4Compressor'} + AND crc_check_chance = 1.0 + AND dclocal_read_repair_chance = 0.1 + AND default_time_to_live = 0 + AND gc_grace_seconds = 864000 + AND max_index_interval = 2048 + AND memtable_flush_period_in_ms = 0 + AND min_index_interval = 128 + AND read_repair_chance = 0.0 + AND speculative_retry = '99PERCENTILE'; + """ diff --git a/integration/functions/user_defined_aggregate_test.rb b/integration/functions/user_defined_aggregate_test.rb index 1fe63ace5..88a75b0e4 100644 --- a/integration/functions/user_defined_aggregate_test.rb +++ b/integration/functions/user_defined_aggregate_test.rb @@ -17,7 +17,6 @@ #++ require File.dirname(__FILE__) + '/../integration_test_case.rb' -require_relative 'schema_change_listener' # noinspection RubyInstanceMethodNamingConvention class UserDefinedAggregateTest < IntegrationTestCase diff --git a/integration/functions/user_defined_function_test.rb b/integration/functions/user_defined_function_test.rb index 84ec47131..6010d7a2b 100644 --- a/integration/functions/user_defined_function_test.rb +++ b/integration/functions/user_defined_function_test.rb @@ -17,7 +17,6 @@ #++ require File.dirname(__FILE__) + '/../integration_test_case.rb' -require_relative 'schema_change_listener' # noinspection RubyInstanceMethodNamingConvention class UserDefinedFunctionTest < IntegrationTestCase diff --git a/integration/indexes/indexes_test.rb b/integration/indexes/indexes_test.rb new file mode 100644 index 000000000..3b961562c --- /dev/null +++ b/integration/indexes/indexes_test.rb @@ -0,0 +1,178 @@ +# encoding: utf-8 + +#-- +# Copyright 2013-2016 DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#++ + +require File.dirname(__FILE__) + '/../integration_test_case.rb' + +class IndexesTest < IntegrationTestCase + + def setup + @@ccm_cluster.setup_schema("CREATE KEYSPACE simplex WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}") + + @cluster = Cassandra.cluster( + schema_refresh_delay: 0.1, + schema_refresh_timeout: 0.1 + ) + @listener = SchemaChangeListener.new(@cluster) + @session = @cluster.connect('simplex') + end + + def teardown + @cluster && @cluster.close + end + + # Test for creating indexes + # + # test_can_create_index tests that indexes can be created using the driver. It first creates a simple table and a + # simplex index on that table. It then verifies that the index metadata is being properly retrieved. + # + # @since 3.0.0 + # @jira_ticket RUBY-178 + # @expected_result index should be created and its metadata should be retrieved. + # + # @test_category indexes + # + def test_can_create_index + @session.execute("CREATE TABLE simplex.test (a text PRIMARY KEY, b text)") + @session.execute("CREATE INDEX b_index ON simplex.test (b)") + + @listener.wait_for_index('simplex', 'test', 'b_index') + + assert @cluster.keyspace('simplex').table('test').has_index?('b_index') + index = @cluster.keyspace('simplex').table('test').index('b_index') + assert_equal 'b_index', index.name + assert_equal 'test', index.table.name + assert_equal :composites, index.kind + assert_equal 'b', index.target + end + + # Test for creating indexes on partial collections + # + # test_can_create_index_on_partial_collections tests that indexes can be created on partial (non-frozen) collections. + # The indexes can be created on either the keys or the values, but not both at the same time. It first creates a table + # and an index on the keys of the collection, verifying the metadata once created. It then drops the original index and + # creates another index, but this time on the values of the collection. + # + # @since 3.0.0 + # @jira_ticket RUBY-178 + # @expected_result partial collection indexes should be created and their metadata should be retrieved. + # + # @test_category indexes + # + def test_can_create_index_on_partial_collections + skip("Secondary index on partial collections were introduced in Cassandra 2.1") if CCM.cassandra_version < '2.1.0' + + @session.execute("CREATE TABLE simplex.collection_test (a int PRIMARY KEY, b map)") + @session.execute("CREATE INDEX b_index ON simplex.collection_test (keys(b))") + + @listener.wait_for_index('simplex', 'collection_test', 'b_index') + + assert @cluster.keyspace('simplex').table('collection_test').has_index?('b_index') + index = @cluster.keyspace('simplex').table('collection_test').index('b_index') + assert_equal 'b_index', index.name + assert_equal 'collection_test', index.table.name + assert_equal :composites, index.kind + assert_equal 'keys(b)', index.target + + @session.execute("DROP INDEX b_index") + @session.execute("CREATE INDEX b_index ON simplex.collection_test (b)") + + @listener.wait_for_index('simplex', 'collection_test', 'b_index') + + assert @cluster.keyspace('simplex').table('collection_test').has_index?('b_index') + index = @cluster.keyspace('simplex').table('collection_test').index('b_index') + assert_equal 'b_index', index.name + assert_equal 'collection_test', index.table.name + assert_equal :composites, index.kind + if CCM.cassandra_version < '3.0.0' + assert_equal 'b', index.target + else + assert_equal 'values(b)', index.target + end + end + + # Test for creating indexes on full collections + # + # test_can_create_index_on_full_collections tests that indexes can be created on full (frozen) collections. It first + # creates a table which includes a frozen collection. It then creates a full index on the collection, verifying that + # the metadata is properly retrieved. + # + # @since 3.0.0 + # @jira_ticket RUBY-178 + # @expected_result full collection indexes should be created and their metadata should be retrieved. + # + # @test_category indexes + # + def test_can_create_index_on_full_collections + skip("Secondary index on full collections were introduced in Cassandra 2.1.3") if CCM.cassandra_version.to_i < '2.1.3'.to_i + + @session.execute("CREATE TABLE simplex.collection_test (a int PRIMARY KEY, b frozen>)") + @session.execute("CREATE INDEX b_index ON simplex.collection_test (full(b))") + + @listener.wait_for_index('simplex', 'collection_test', 'b_index') + + assert @cluster.keyspace('simplex').table('collection_test').has_index?('b_index') + index = @cluster.keyspace('simplex').table('collection_test').index('b_index') + assert_equal 'b_index', index.name + assert_equal 'collection_test', index.table.name + assert_equal :composites, index.kind + if CCM.cassandra_version < '3.0.0' + assert_equal 'b', index.target + else + assert_equal 'full(b)', index.target + end + end + + # Test for creating multiple indexes on the same column + # + # test_can_create_multiple_indexes_same_column tests that multiple indexes can be created on the same column. It first + # creates a table which includes a non-frozen collection. It then creates two indexes: one for the key and another for + # the value of the collection. It then verifies the metadata associated with each index. + # + # @since 3.0.0 + # @jira_ticket RUBY-178 + # @expected_result multiple indexes should be created and their metadata should be retrieved. + # + # @test_category indexes + # + def test_can_create_multiple_indexes_same_column + skip("Multiple indexes on same column were introduced in Cassandra 3.0.0") if CCM.cassandra_version < '3.0.0' + + @session.execute("CREATE TABLE simplex.multi_index_test (a int PRIMARY KEY, b map)") + @session.execute("CREATE INDEX key_index ON simplex.multi_index_test (keys(b))") + @session.execute("CREATE INDEX value_index ON simplex.multi_index_test (values(b))") + + @listener.wait_for_index('simplex', 'multi_index_test', 'key_index') + @listener.wait_for_index('simplex', 'multi_index_test', 'value_index') + + assert @cluster.keyspace('simplex').table('multi_index_test').has_index?('key_index') + assert @cluster.keyspace('simplex').table('multi_index_test').has_index?('value_index') + + key_index = @cluster.keyspace('simplex').table('multi_index_test').index('key_index') + assert_equal 'key_index', key_index.name + assert_equal 'multi_index_test', key_index.table.name + assert_equal :composites, key_index.kind + assert_equal 'keys(b)', key_index.target + + value_index = @cluster.keyspace('simplex').table('multi_index_test').index('value_index') + assert_equal 'value_index', value_index.name + assert_equal 'multi_index_test', value_index.table.name + assert_equal :composites, value_index.kind + assert_equal 'values(b)', value_index.target + end + +end diff --git a/integration/integration_test_case.rb b/integration/integration_test_case.rb index b8b1737b3..28a0d0bc9 100644 --- a/integration/integration_test_case.rb +++ b/integration/integration_test_case.rb @@ -18,6 +18,7 @@ require File.dirname(__FILE__) + '/../support/ccm.rb' require File.dirname(__FILE__) + '/../support/retry.rb' +require File.dirname(__FILE__) + '/schema_change_listener.rb' require 'minitest/unit' require 'minitest/autorun' require 'cassandra' diff --git a/integration/functions/schema_change_listener.rb b/integration/schema_change_listener.rb similarity index 90% rename from integration/functions/schema_change_listener.rb rename to integration/schema_change_listener.rb index f03d5a925..1e5b9899f 100644 --- a/integration/functions/schema_change_listener.rb +++ b/integration/schema_change_listener.rb @@ -54,6 +54,12 @@ def wait_for_aggregate(keyspace_name, aggregate_name, *args) end end + def wait_for_index(keyspace_name, table_name, index_name, *args) + wait_for_change(keyspace_name, 2) do |ks| + ks.table(table_name).has_index?(index_name, *args) + end + end + def keyspace_changed(keyspace) # This looks a little strange, but here's the idea: if we don't have # Condition's for this keyspace, immediately return. Otherwise, for each