diff --git a/CHANGES.txt b/CHANGES.txt index 269338886a..f2b0d3d6c3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.0-rc2 + * Fix cqlsh DESC TYPE with non-ascii character in the identifier (CASSANDRA-16400) * Fix cqlsh encoding error with unicode in multi-line statement (CASSANDRA-16539) * Fix race in fat client removal (CASSANDRA-16238) * Test org.apache.cassandra.net.AsyncPromiseTest FAILED (CASSANDRA-16596) diff --git a/bin/cqlsh.py b/bin/cqlsh.py index f964fc9834..102a416f07 100755 --- a/bin/cqlsh.py +++ b/bin/cqlsh.py @@ -622,37 +622,37 @@ class Shell(cmd.Cmd): self.connection_versions = vers def get_keyspace_names(self): - return list(map(str, list(self.conn.metadata.keyspaces.keys()))) + return list(self.conn.metadata.keyspaces) def get_columnfamily_names(self, ksname=None): if ksname is None: ksname = self.current_keyspace - return list(map(str, list(self.get_keyspace_meta(ksname).tables.keys()))) + return list(self.get_keyspace_meta(ksname).tables) def get_materialized_view_names(self, ksname=None): if ksname is None: ksname = self.current_keyspace - return list(map(str, list(self.get_keyspace_meta(ksname).views.keys()))) + return list(self.get_keyspace_meta(ksname).views) def get_index_names(self, ksname=None): if ksname is None: ksname = self.current_keyspace - return list(map(str, list(self.get_keyspace_meta(ksname).indexes.keys()))) + return list(self.get_keyspace_meta(ksname).indexes) def get_column_names(self, ksname, cfname): if ksname is None: ksname = self.current_keyspace layout = self.get_table_meta(ksname, cfname) - return [str(col) for col in layout.columns] + return list(layout.columns) def get_usertype_names(self, ksname=None): if ksname is None: ksname = self.current_keyspace - return list(self.get_keyspace_meta(ksname).user_types.keys()) + return list(self.get_keyspace_meta(ksname).user_types) def get_usertype_layout(self, ksname, typename): if ksname is None: @@ -1404,9 +1404,7 @@ class Shell(cmd.Cmd): """ Print the output for a DESCRIBE KEYSPACES query """ - names = list() - for row in rows: - names.append(str(row['name'])) + names = [ensure_str(r['name']) for r in rows] print('') cmd.Cmd.columnize(self, names) @@ -1426,7 +1424,7 @@ class Shell(cmd.Cmd): keyspace = row['keyspace_name'] names = list() - names.append(str(row['name'])) + names.append(ensure_str(row['name'])) if keyspace is not None: self.print_keyspace_element_names(keyspace, names) diff --git a/pylib/cqlshlib/copyutil.py b/pylib/cqlshlib/copyutil.py index 1056c52a4e..d22bf8ba2d 100644 --- a/pylib/cqlshlib/copyutil.py +++ b/pylib/cqlshlib/copyutil.py @@ -560,7 +560,7 @@ class ExportWriter(object): if self.header: writer = csv.writer(self.current_dest.output, **self.options.dialect) - writer.writerow(self.columns) + writer.writerow([ensure_str(c) for c in self.columns]) return True diff --git a/pylib/cqlshlib/test/test_cqlsh_completion.py b/pylib/cqlshlib/test/test_cqlsh_completion.py index 8b296b8f9b..c898cbed04 100644 --- a/pylib/cqlshlib/test/test_cqlsh_completion.py +++ b/pylib/cqlshlib/test/test_cqlsh_completion.py @@ -696,10 +696,7 @@ class TestCqlshCompletion(CqlshCompletionCase): self.trycompletions('CREATE TA', immediate='BLE ') self.create_columnfamily_table_template('TABLE') - def test_complete_in_describe(self): - """ - Tests for Cassandra-10733 - """ + def test_complete_in_describe(self): # Cassandra-10733 self.trycompletions('DES', immediate='C') # quoted_keyspace = '"' + self.cqlsh.keyspace + '"' self.trycompletions('DESCR', immediate='IBE ') diff --git a/pylib/cqlshlib/test/test_unicode.py b/pylib/cqlshlib/test/test_unicode.py index b31e81c991..869cdb3c11 100644 --- a/pylib/cqlshlib/test/test_unicode.py +++ b/pylib/cqlshlib/test/test_unicode.py @@ -56,10 +56,43 @@ class TestCqlshUnicode(BaseTestCase): output = c.cmd_and_response('SELECT * FROM t;') self.assertIn(col_name, output) - def test_multiline_input(self): # CASSANDRA-16539 + def test_unicode_multiline_input(self): # CASSANDRA-16400 with testrun_cqlsh(tty=True, env=self.default_env) as c: value = '値' c.send("INSERT INTO t(k, v) VALUES (1, \n'%s');\n" % (value,)) c.read_to_next_prompt() output = c.cmd_and_response('SELECT v FROM t;') self.assertIn(value, output) + + def test_unicode_desc(self): # CASSANDRA-16539 + with testrun_cqlsh(tty=True, env=self.default_env) as c: + v1 = 'ࠑ' + v2 = 'Ξ' + output = c.cmd_and_response('CREATE TYPE "%s" ( "%s" int );' % (v1, v2)) + output = c.cmd_and_response('DESC TYPES;') + self.assertIn(v1, output) + output = c.cmd_and_response('DESC TYPE "%s";' %(v1,)) + self.assertIn(v2, output) + + def test_unicode_copy_roundtrip(self): # CASSANDRA-16539 + with testrun_cqlsh(tty=True, env=self.default_env) as c: + v1 = 'ࠑ' + v2 = 'Ξ' + c.cmd_and_response('CREATE TABLE table_unicode_col (k int PRIMARY KEY, "%s" text );' % (v1,)) + # Sending and reading separately to bypass the echo assert in cmd_and_response. + # For some reason when running in Python2 pty is emitting an extra "cursor up" escape sequence for this command only. + c.send('INSERT INTO table_unicode_col (k, "%s") VALUES (0, \'%s\');\n' % (v1, v2,)) + c.read_to_next_prompt() + + result1 = c.cmd_and_response('SELECT * FROM table_unicode_col;') + for v in (v1, v2, '1 rows'): + self.assertIn(v, result1) + + c.cmd_and_response('COPY table_unicode_col TO \'tmp.txt\' WITH HEADER=true AND NUMPROCESSES=1;') + c.cmd_and_response('TRUNCATE table_unicode_col;') + output = c.cmd_and_response('SELECT * FROM table_unicode_col;') + self.assertIn('0 rows', output) + c.cmd_and_response('COPY table_unicode_col FROM \'tmp.txt\' WITH HEADER=true AND NUMPROCESSES=1;') + + result2 = c.cmd_and_response('SELECT * FROM table_unicode_col;') + self.assertEqual(result1, result2)