Skip to content

Commit d38a17e

Browse files
committed
Merged metadata.py
1 parent 69a870c commit d38a17e

File tree

1 file changed

+112
-16
lines changed

1 file changed

+112
-16
lines changed

cassandra/metadata.py

Lines changed: 112 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
from collections import defaultdict
1818
from functools import total_ordering
1919
from hashlib import md5
20-
from itertools import islice, cycle
2120
import json
2221
import logging
2322
import re
@@ -50,10 +49,10 @@
5049
cql_keywords = set((
5150
'add', 'aggregate', 'all', 'allow', 'alter', 'and', 'apply', 'as', 'asc', 'ascii', 'authorize', 'batch', 'begin',
5251
'bigint', 'blob', 'boolean', 'by', 'called', 'clustering', 'columnfamily', 'compact', 'contains', 'count',
53-
'counter', 'create', 'custom', 'date', 'decimal', 'delete', 'desc', 'describe', 'distinct', 'double', 'drop',
52+
'counter', 'create', 'custom', 'date', 'decimal', 'delete', 'desc', 'describe', 'deterministic', 'distinct', 'double', 'drop',
5453
'entries', 'execute', 'exists', 'filtering', 'finalfunc', 'float', 'from', 'frozen', 'full', 'function',
5554
'functions', 'grant', 'if', 'in', 'index', 'inet', 'infinity', 'initcond', 'input', 'insert', 'int', 'into', 'is', 'json',
56-
'key', 'keys', 'keyspace', 'keyspaces', 'language', 'limit', 'list', 'login', 'map', 'materialized', 'modify', 'nan', 'nologin',
55+
'key', 'keys', 'keyspace', 'keyspaces', 'language', 'limit', 'list', 'login', 'map', 'materialized', 'modify', 'monotonic', 'nan', 'nologin',
5756
'norecursive', 'nosuperuser', 'not', 'null', 'of', 'on', 'options', 'or', 'order', 'password', 'permission',
5857
'permissions', 'primary', 'rename', 'replace', 'returns', 'revoke', 'role', 'roles', 'schema', 'select', 'set',
5958
'sfunc', 'smallint', 'static', 'storage', 'stype', 'superuser', 'table', 'text', 'time', 'timestamp', 'timeuuid',
@@ -68,9 +67,9 @@
6867

6968
cql_keywords_unreserved = set((
7069
'aggregate', 'all', 'as', 'ascii', 'bigint', 'blob', 'boolean', 'called', 'clustering', 'compact', 'contains',
71-
'count', 'counter', 'custom', 'date', 'decimal', 'distinct', 'double', 'exists', 'filtering', 'finalfunc', 'float',
70+
'count', 'counter', 'custom', 'date', 'decimal', 'deterministic', 'distinct', 'double', 'exists', 'filtering', 'finalfunc', 'float',
7271
'frozen', 'function', 'functions', 'inet', 'initcond', 'input', 'int', 'json', 'key', 'keys', 'keyspaces',
73-
'language', 'list', 'login', 'map', 'nologin', 'nosuperuser', 'options', 'password', 'permission', 'permissions',
72+
'language', 'list', 'login', 'map', 'monotonic', 'nologin', 'nosuperuser', 'options', 'password', 'permission', 'permissions',
7473
'returns', 'role', 'roles', 'sfunc', 'smallint', 'static', 'storage', 'stype', 'superuser', 'text', 'time',
7574
'timestamp', 'timeuuid', 'tinyint', 'trigger', 'ttl', 'tuple', 'type', 'user', 'users', 'uuid', 'values', 'varchar',
7675
'varint', 'writetime'
@@ -125,7 +124,8 @@ def export_schema_as_string(self):
125124
def refresh(self, connection, timeout, target_type=None, change_type=None, **kwargs):
126125

127126
server_version = self.get_host(connection.endpoint).release_version
128-
parser = get_schema_parser(connection, server_version, timeout)
127+
dse_version = self.get_host(connection.endpoint).dse_version
128+
parser = get_schema_parser(connection, server_version, dse_version, timeout)
129129

130130
if not target_type:
131131
self._rebuild_all(parser)
@@ -661,7 +661,7 @@ class KeyspaceMetadata(object):
661661
virtual = False
662662
"""
663663
A boolean indicating if this is a virtual keyspace or not. Always ``False``
664-
for clusters running pre-4.0 versions of Cassandra.
664+
for clusters running Cassandra pre-4.0 and DSE pre-6.7 versions.
665665
666666
.. versionadded:: 3.15
667667
"""
@@ -893,8 +893,15 @@ class Aggregate(object):
893893
Type of the aggregate state
894894
"""
895895

896+
deterministic = None
897+
"""
898+
Flag indicating if this function is guaranteed to produce the same result
899+
for a particular input and state. This is available only with DSE >=6.0.
900+
"""
901+
896902
def __init__(self, keyspace, name, argument_types, state_func,
897-
state_type, final_func, initial_condition, return_type):
903+
state_type, final_func, initial_condition, return_type,
904+
deterministic):
898905
self.keyspace = keyspace
899906
self.name = name
900907
self.argument_types = argument_types
@@ -903,6 +910,7 @@ def __init__(self, keyspace, name, argument_types, state_func,
903910
self.final_func = final_func
904911
self.initial_condition = initial_condition
905912
self.return_type = return_type
913+
self.deterministic = deterministic
906914

907915
def as_cql_query(self, formatted=False):
908916
"""
@@ -923,6 +931,7 @@ def as_cql_query(self, formatted=False):
923931

924932
ret += ''.join((sep, 'FINALFUNC ', protect_name(self.final_func))) if self.final_func else ''
925933
ret += ''.join((sep, 'INITCOND ', self.initial_condition)) if self.initial_condition is not None else ''
934+
ret += '{}DETERMINISTIC'.format(sep) if self.deterministic else ''
926935

927936
return ret
928937

@@ -984,8 +993,27 @@ class Function(object):
984993
(convenience function to avoid handling nulls explicitly if the result will just be null)
985994
"""
986995

996+
deterministic = None
997+
"""
998+
Flag indicating if this function is guaranteed to produce the same result
999+
for a particular input. This is available only for DSE >=6.0.
1000+
"""
1001+
1002+
monotonic = None
1003+
"""
1004+
Flag indicating if this function is guaranteed to increase or decrease
1005+
monotonically on any of its arguments. This is available only for DSE >=6.0.
1006+
"""
1007+
1008+
monotonic_on = None
1009+
"""
1010+
A list containing the argument or arguments over which this function is
1011+
monotonic. This is available only for DSE >=6.0.
1012+
"""
1013+
9871014
def __init__(self, keyspace, name, argument_types, argument_names,
988-
return_type, language, body, called_on_null_input):
1015+
return_type, language, body, called_on_null_input,
1016+
deterministic, monotonic, monotonic_on):
9891017
self.keyspace = keyspace
9901018
self.name = name
9911019
self.argument_types = argument_types
@@ -996,6 +1024,9 @@ def __init__(self, keyspace, name, argument_types, argument_names,
9961024
self.language = language
9971025
self.body = body
9981026
self.called_on_null_input = called_on_null_input
1027+
self.deterministic = deterministic
1028+
self.monotonic = monotonic
1029+
self.monotonic_on = monotonic_on
9991030

10001031
def as_cql_query(self, formatted=False):
10011032
"""
@@ -1012,10 +1043,25 @@ def as_cql_query(self, formatted=False):
10121043
lang = self.language
10131044
body = self.body
10141045
on_null = "CALLED" if self.called_on_null_input else "RETURNS NULL"
1046+
deterministic_token = ('DETERMINISTIC{}'.format(sep)
1047+
if self.deterministic else
1048+
'')
1049+
monotonic_tokens = '' # default for nonmonotonic function
1050+
if self.monotonic:
1051+
# monotonic on all arguments; ignore self.monotonic_on
1052+
monotonic_tokens = 'MONOTONIC{}'.format(sep)
1053+
elif self.monotonic_on:
1054+
# if monotonic == False and monotonic_on is nonempty, we know that
1055+
# monotonicity was specified with MONOTONIC ON <arg>, so there's
1056+
# exactly 1 value there
1057+
monotonic_tokens = 'MONOTONIC ON {}{}'.format(self.monotonic_on[0],
1058+
sep)
10151059

10161060
return "CREATE FUNCTION %(keyspace)s.%(name)s(%(arg_list)s)%(sep)s" \
10171061
"%(on_null)s ON NULL INPUT%(sep)s" \
10181062
"RETURNS %(typ)s%(sep)s" \
1063+
"%(deterministic_token)s" \
1064+
"%(monotonic_tokens)s" \
10191065
"LANGUAGE %(lang)s%(sep)s" \
10201066
"AS $$%(body)s$$" % locals()
10211067

@@ -1102,7 +1148,7 @@ def primary_key(self):
11021148
virtual = False
11031149
"""
11041150
A boolean indicating if this is a virtual table or not. Always ``False``
1105-
for clusters running pre-4.0 versions of Cassandra.
1151+
for clusters running Cassandra pre-4.0 and DSE pre-6.7 versions.
11061152
11071153
.. versionadded:: 3.15
11081154
"""
@@ -1733,6 +1779,9 @@ def _query_build_rows(self, query_string, build_func):
17331779

17341780

17351781
class SchemaParserV22(_SchemaParser):
1782+
"""
1783+
For C* 2.2+
1784+
"""
17361785
_SELECT_KEYSPACES = "SELECT * FROM system.schema_keyspaces"
17371786
_SELECT_COLUMN_FAMILIES = "SELECT * FROM system.schema_columnfamilies"
17381787
_SELECT_COLUMNS = "SELECT * FROM system.schema_columns"
@@ -1884,10 +1933,14 @@ def _build_user_type(cls, usertype_row):
18841933
@classmethod
18851934
def _build_function(cls, function_row):
18861935
return_type = cls._schema_type_to_cql(function_row['return_type'])
1936+
deterministic = function_row.get('deterministic', False)
1937+
monotonic = function_row.get('monotonic', False)
1938+
monotonic_on = function_row.get('monotonic_on', ())
18871939
return Function(function_row['keyspace_name'], function_row['function_name'],
18881940
function_row[cls._function_agg_arument_type_col], function_row['argument_names'],
18891941
return_type, function_row['language'], function_row['body'],
1890-
function_row['called_on_null_input'])
1942+
function_row['called_on_null_input'],
1943+
deterministic, monotonic, monotonic_on)
18911944

18921945
@classmethod
18931946
def _build_aggregate(cls, aggregate_row):
@@ -1899,7 +1952,8 @@ def _build_aggregate(cls, aggregate_row):
18991952
return_type = cls._schema_type_to_cql(aggregate_row['return_type'])
19001953
return Aggregate(aggregate_row['keyspace_name'], aggregate_row['aggregate_name'],
19011954
aggregate_row['signature'], aggregate_row['state_func'], state_type,
1902-
aggregate_row['final_func'], initial_condition, return_type)
1955+
aggregate_row['final_func'], initial_condition, return_type,
1956+
aggregate_row.get('deterministic', False))
19031957

19041958
def _build_table_metadata(self, row, col_rows=None, trigger_rows=None):
19051959
keyspace_name = row["keyspace_name"]
@@ -2230,6 +2284,9 @@ def _schema_type_to_cql(type_string):
22302284

22312285

22322286
class SchemaParserV3(SchemaParserV22):
2287+
"""
2288+
For C* 3.0+
2289+
"""
22332290
_SELECT_KEYSPACES = "SELECT * FROM system_schema.keyspaces"
22342291
_SELECT_TABLES = "SELECT * FROM system_schema.tables"
22352292
_SELECT_COLUMNS = "SELECT * FROM system_schema.columns"
@@ -2316,7 +2373,8 @@ def _build_keyspace_metadata_internal(row):
23162373
def _build_aggregate(aggregate_row):
23172374
return Aggregate(aggregate_row['keyspace_name'], aggregate_row['aggregate_name'],
23182375
aggregate_row['argument_types'], aggregate_row['state_func'], aggregate_row['state_type'],
2319-
aggregate_row['final_func'], aggregate_row['initcond'], aggregate_row['return_type'])
2376+
aggregate_row['final_func'], aggregate_row['initcond'], aggregate_row['return_type'],
2377+
aggregate_row.get('deterministic', False))
23202378

23212379
def _build_table_metadata(self, row, col_rows=None, trigger_rows=None, index_rows=None, virtual=False):
23222380
keyspace_name = row["keyspace_name"]
@@ -2498,6 +2556,14 @@ def _schema_type_to_cql(type_string):
24982556
return type_string
24992557

25002558

2559+
class SchemaParserDSE60(SchemaParserV3):
2560+
"""
2561+
For DSE 6.0+
2562+
"""
2563+
recognized_table_options = (SchemaParserV3.recognized_table_options +
2564+
("nodesync",))
2565+
2566+
25012567
class SchemaParserV4(SchemaParserV3):
25022568

25032569
recognized_table_options = tuple(
@@ -2629,10 +2695,25 @@ def _build_keyspace_metadata_internal(row):
26292695
return super(SchemaParserV4, SchemaParserV4)._build_keyspace_metadata_internal(row)
26302696

26312697

2698+
class SchemaParserDSE67(SchemaParserV4):
2699+
"""
2700+
For DSE 6.7+
2701+
"""
2702+
recognized_table_options = (SchemaParserV4.recognized_table_options +
2703+
("nodesync",))
2704+
2705+
26322706
class TableMetadataV3(TableMetadata):
2707+
"""
2708+
For C* 3.0+. `option_maps` take a superset of map names, so if nothing
2709+
changes structurally, new option maps can just be appended to the list.
2710+
"""
26332711
compaction_options = {}
26342712

2635-
option_maps = ['compaction', 'compression', 'caching']
2713+
option_maps = [
2714+
'compaction', 'compression', 'caching',
2715+
'nodesync' # added DSE 6.0
2716+
]
26362717

26372718
@property
26382719
def is_cql_compatible(self):
@@ -2768,11 +2849,18 @@ def export_as_string(self):
27682849
return self.as_cql_query(formatted=True) + ";"
27692850

27702851

2771-
def get_schema_parser(connection, server_version, timeout):
2852+
def get_schema_parser(connection, server_version, dse_version, timeout):
27722853
version = Version(server_version)
2854+
if dse_version:
2855+
v = Version(dse_version)
2856+
if v >= Version('6.7.0'):
2857+
return SchemaParserDSE67(connection, timeout)
2858+
elif v >= Version('6.0.0'):
2859+
return SchemaParserDSE60(connection, timeout)
2860+
27732861
if version >= Version('4.0.0'):
27742862
return SchemaParserV4(connection, timeout)
2775-
if version >= Version('3.0.0'):
2863+
elif version >= Version('3.0.0'):
27762864
return SchemaParserV3(connection, timeout)
27772865
else:
27782866
# we could further specialize by version. Right now just refactoring the
@@ -2791,6 +2879,14 @@ def _cql_from_cass_type(cass_type):
27912879
return cass_type.cql_parameterized_type()
27922880

27932881

2882+
class RLACTableExtension(RegisteredTableExtension):
2883+
name = "DSE_RLACA"
2884+
2885+
@classmethod
2886+
def after_table_cql(cls, table_meta, ext_key, ext_blob):
2887+
return "RESTRICT ROWS ON %s.%s USING %s;" % (protect_name(table_meta.keyspace_name),
2888+
protect_name(table_meta.name),
2889+
protect_name(ext_blob.decode('utf-8')))
27942890
NO_VALID_REPLICA = object()
27952891

27962892

0 commit comments

Comments
 (0)