Skip to content

Commit 590f150

Browse files
authored
Merge pull request datastax#986 from datastax/python-1020_fix-vtables-against-dse-6.0.X
PYTHON-1020 fix vtables against dse 6.0.x
2 parents c75fd19 + efa4e84 commit 590f150

File tree

4 files changed

+63
-17
lines changed

4 files changed

+63
-17
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Bug Fixes
55
---------
66
* Improve and fix socket error-catching code in nonblocking-socket reactors (PYTHON-1024)
77
* Non-ASCII characters in schema break CQL string generation (PYTHON-1008)
8+
* Fix OSS driver's virtual table support against DSE 6.0.X and future server releases (PYTHON-1020)
89

910
Other
1011
-----

build.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ cassandra:
118118
- '3.0'
119119
- '3.11'
120120
- 'test-dse'
121+
- 'dse-6.7'
121122

122123
env:
123124
CYTHON:
@@ -159,6 +160,13 @@ build:
159160
exit 0
160161
fi
161162
163+
if [[ $CCM_IS_DSE == 'true' ]]; then
164+
# We only use a DSE version for unreleased DSE versions, so we only need to run the smoke tests here
165+
echo "CCM_IS_DSE: $CCM_IS_DSE"
166+
echo "==========RUNNING SMOKE TESTS==========="
167+
EVENT_LOOP_MANAGER=$EVENT_LOOP_MANAGER CCM_ARGS="$CCM_ARGS" CASSANDRA_VERSION=$CCM_CASSANDRA_VERSION DSE_VERSION='6.7.0' MAPPED_CASSANDRA_VERSION=$MAPPED_CASSANDRA_VERSION VERIFY_CYTHON=$FORCE_CYTHON nosetests -s -v --logging-format="[%(levelname)s] %(asctime)s %(thread)d: %(message)s" --with-ignore-docstrings --with-xunit --xunit-file=standard_results.xml tests/integration/standard/test_dse.py || true
168+
exit 0
169+
fi
162170
163171
# Run the unit tests, this is not done in travis because
164172
# it takes too much time for the whole matrix to build with cython

cassandra/metadata.py

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,8 +1667,30 @@ def __init__(self, connection, timeout):
16671667
self.connection = connection
16681668
self.timeout = timeout
16691669

1670-
def _handle_results(self, success, result):
1671-
if success:
1670+
def _handle_results(self, success, result, expected_failures=tuple()):
1671+
"""
1672+
Given a bool and a ResultSet (the form returned per result from
1673+
Connection.wait_for_responses), return a dictionary containing the
1674+
results. Used to process results from asynchronous queries to system
1675+
tables.
1676+
1677+
``expected_failures`` will usually be used to allow callers to ignore
1678+
``InvalidRequest`` errors caused by a missing system keyspace. For
1679+
example, some DSE versions report a 4.X server version, but do not have
1680+
virtual tables. Thus, running against 4.X servers, SchemaParserV4 uses
1681+
expected_failures to make a best-effort attempt to read those
1682+
keyspaces, but treat them as empty if they're not found.
1683+
1684+
:param success: A boolean representing whether or not the query
1685+
succeeded
1686+
:param result: The resultset in question.
1687+
:expected_failures: An Exception class or an iterable thereof. If the
1688+
query failed, but raised an instance of an expected failure class, this
1689+
will ignore the failure and return an empty list.
1690+
"""
1691+
if not success and isinstance(result, expected_failures):
1692+
return []
1693+
elif success:
16721694
return dict_factory(*result.results) if result else []
16731695
else:
16741696
raise result
@@ -1784,11 +1806,9 @@ def get_table(self, keyspaces, keyspace, table):
17841806
table_result = self._handle_results(cf_success, cf_result)
17851807
col_result = self._handle_results(col_success, col_result)
17861808

1787-
# handle the triggers table not existing in Cassandra 1.2
1788-
if not triggers_success and isinstance(triggers_result, InvalidRequest):
1789-
triggers_result = []
1790-
else:
1791-
triggers_result = self._handle_results(triggers_success, triggers_result)
1809+
# the triggers table doesn't exist in C* 1.2
1810+
triggers_result = self._handle_results(triggers_success, triggers_result,
1811+
expected_failures=InvalidRequest)
17921812

17931813
if table_result:
17941814
return self._build_table_metadata(table_result[0], col_result, triggers_result)
@@ -2531,12 +2551,21 @@ def _query_all(self):
25312551
self.indexes_result = self._handle_results(indexes_success, indexes_result)
25322552
self.views_result = self._handle_results(views_success, views_result)
25332553
# V4-only results
2534-
self.virtual_keyspaces_result = self._handle_results(virtual_ks_success,
2535-
virtual_ks_result)
2536-
self.virtual_tables_result = self._handle_results(virtual_table_success,
2537-
virtual_table_result)
2538-
self.virtual_columns_result = self._handle_results(virtual_column_success,
2539-
virtual_column_result)
2554+
# These tables don't exist in some DSE versions reporting 4.X so we can
2555+
# ignore them if we got an error
2556+
self.virtual_keyspaces_result = self._handle_results(
2557+
virtual_ks_success, virtual_ks_result,
2558+
expected_failures=InvalidRequest
2559+
)
2560+
self.virtual_tables_result = self._handle_results(
2561+
virtual_table_success, virtual_table_result,
2562+
expected_failures=InvalidRequest
2563+
)
2564+
self.virtual_columns_result = self._handle_results(
2565+
virtual_column_success, virtual_column_result,
2566+
expected_failures=InvalidRequest
2567+
)
2568+
25402569
self._aggregate_results()
25412570

25422571
def _aggregate_results(self):
@@ -2722,9 +2751,7 @@ def export_as_string(self):
27222751

27232752
def get_schema_parser(connection, server_version, timeout):
27242753
server_major_version = int(server_version.split('.')[0])
2725-
# check for DSE version
2726-
has_build_version = len(server_version.split('.')) > 3
2727-
if server_major_version >= 4 and not has_build_version:
2754+
if server_major_version >= 4:
27282755
return SchemaParserV4(connection, timeout)
27292756
if server_major_version >= 3:
27302757
return SchemaParserV3(connection, timeout)

tests/integration/standard/test_dse.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from cassandra.cluster import Cluster
2020
from tests import notwindows
21+
from tests.unit.cython.utils import notcython
2122
from tests.integration import (execute_until_pass,
2223
execute_with_long_wait_retry, use_cluster)
2324

@@ -27,8 +28,12 @@
2728
import unittest # noqa
2829

2930

31+
CCM_IS_DSE = (os.environ.get('CCM_IS_DSE', None) == 'true')
32+
33+
3034
@unittest.skipIf(os.environ.get('CCM_ARGS', None), 'environment has custom CCM_ARGS; skipping')
3135
@notwindows
36+
@notcython # no need to double up on this test; also __default__ setting doesn't work
3237
class DseCCMClusterTest(unittest.TestCase):
3338
"""
3439
This class can be executed setting the DSE_VERSION variable, for example:
@@ -42,6 +47,10 @@ def test_dse_5x(self):
4247
def test_dse_60(self):
4348
self._test_basic(Version('6.0.2'))
4449

50+
@unittest.skipUnless(CCM_IS_DSE, 'DSE version unavailable')
51+
def test_dse_67(self):
52+
self._test_basic(Version('6.7.0'))
53+
4554
def _test_basic(self, dse_version):
4655
"""
4756
Test basic connection and usage
@@ -52,7 +61,8 @@ def _test_basic(self, dse_version):
5261
use_cluster(cluster_name=cluster_name, nodes=[3],
5362
dse_cluster=True, dse_options={}, dse_version=dse_version)
5463

55-
cluster = Cluster()
64+
cluster = Cluster(
65+
allow_beta_protocol_version=(dse_version >= Version('6.7.0')))
5666
session = cluster.connect()
5767
result = execute_until_pass(
5868
session,

0 commit comments

Comments
 (0)