# # directory_extension.py # # This source file is part of the FoundationDB open source project # # Copyright 2013-2018 Apple Inc. and the FoundationDB project authors # # 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. # import traceback import sys import fdb import fdb.directory_impl from fdb import six ops_that_create_dirs = [ six.u('DIRECTORY_CREATE_SUBSPACE'), six.u('DIRECTORY_CREATE_LAYER'), six.u('DIRECTORY_CREATE_OR_OPEN'), six.u('DIRECTORY_CREATE'), six.u('DIRECTORY_OPEN'), six.u('DIRECTORY_MOVE'), six.u('DIRECTORY_MOVE_TO'), six.u('DIRECTORY_OPEN_SUBSPACE'), ] log_all = False log_instructions = False log_ops = False log_dirs = False log_errors = False def log_op(msg, force=False): if log_ops or log_all or force: print(msg) class DirectoryExtension(): def __init__(self): self.dir_list = [fdb.directory] self.dir_index = 0 self.error_index = 0 def pop_tuples(self, stack, num=None): actual_num = num if actual_num is None: actual_num = 1 tuples = tuple([tuple(stack.pop(stack.pop())) for i in range(actual_num)]) if num is None: return tuples[0] return tuples def append_dir(self, inst, dir): if log_dirs or log_all: print('pushed %s at %d (op=%s)' % (dir.__class__.__name__, len(self.dir_list), inst.op)) self.dir_list.append(dir) def process_instruction(self, inst): try: if log_all or log_instructions: print("%d. %s" % (inst.index, inst.op)) directory = self.dir_list[self.dir_index] if inst.op == six.u('DIRECTORY_CREATE_SUBSPACE'): path = self.pop_tuples(inst.stack) raw_prefix = inst.pop() log_op('created subspace at %r: %r' % (path, raw_prefix)) self.append_dir(inst, fdb.Subspace(path, raw_prefix)) elif inst.op == six.u('DIRECTORY_CREATE_LAYER'): index1, index2, allow_manual_prefixes = inst.pop(3) if self.dir_list[index1] is None or self.dir_list[index2] is None: log_op('create directory layer: None') self.append_dir(inst, None) else: log_op('create directory layer: node_subspace (%d) = %r, content_subspace (%d) = %r, allow_manual_prefixes = %d' % (index1, self.dir_list[index1].rawPrefix, index2, self.dir_list[index2].rawPrefix, allow_manual_prefixes)) self.append_dir(inst, fdb.DirectoryLayer(self.dir_list[index1], self.dir_list[index2], allow_manual_prefixes == 1)) elif inst.op == six.u('DIRECTORY_CHANGE'): self.dir_index = inst.pop() if not self.dir_list[self.dir_index]: self.dir_index = self.error_index if log_dirs or log_all: new_dir = self.dir_list[self.dir_index] clazz = new_dir.__class__.__name__ new_path = repr(new_dir._path) if hasattr(new_dir, '_path') else "" print('changed directory to %d (%s @%r)' % (self.dir_index, clazz, new_path)) elif inst.op == six.u('DIRECTORY_SET_ERROR_INDEX'): self.error_index = inst.pop() elif inst.op == six.u('DIRECTORY_CREATE_OR_OPEN'): path = self.pop_tuples(inst.stack) layer = inst.pop() log_op('create_or_open %r: layer=%r' % (directory.get_path() + path, layer)) d = directory.create_or_open(inst.tr, path, layer or b'') self.append_dir(inst, d) elif inst.op == six.u('DIRECTORY_CREATE'): path = self.pop_tuples(inst.stack) layer, prefix = inst.pop(2) log_op('create %r: layer=%r, prefix=%r' % (directory.get_path() + path, layer, prefix)) self.append_dir(inst, directory.create(inst.tr, path, layer or b'', prefix)) elif inst.op == six.u('DIRECTORY_OPEN'): path = self.pop_tuples(inst.stack) layer = inst.pop() log_op('open %r: layer=%r' % (directory.get_path() + path, layer)) self.append_dir(inst, directory.open(inst.tr, path, layer or b'')) elif inst.op == six.u('DIRECTORY_MOVE'): old_path, new_path = self.pop_tuples(inst.stack, 2) log_op('move %r to %r' % (directory.get_path() + old_path, directory.get_path() + new_path)) self.append_dir(inst, directory.move(inst.tr, old_path, new_path)) elif inst.op == six.u('DIRECTORY_MOVE_TO'): new_absolute_path = self.pop_tuples(inst.stack) log_op('move %r to %r' % (directory.get_path(), new_absolute_path)) self.append_dir(inst, directory.move_to(inst.tr, new_absolute_path)) elif inst.op == six.u('DIRECTORY_REMOVE'): count = inst.pop() if count == 0: log_op('remove %r' % (directory.get_path(),)) directory.remove(inst.tr) else: path = self.pop_tuples(inst.stack) log_op('remove %r' % (directory.get_path() + path,)) directory.remove(inst.tr, path) elif inst.op == six.u('DIRECTORY_REMOVE_IF_EXISTS'): count = inst.pop() if count == 0: log_op('remove_if_exists %r' % (directory.get_path(),)) directory.remove_if_exists(inst.tr) else: path = self.pop_tuples(inst.stack) log_op('remove_if_exists %r' % (directory.get_path() + path,)) directory.remove_if_exists(inst.tr, path) elif inst.op == six.u('DIRECTORY_LIST'): count = inst.pop() if count == 0: result = directory.list(inst.tr) log_op('list %r' % (directory.get_path(),)) else: path = self.pop_tuples(inst.stack) result = directory.list(inst.tr, path) log_op('list %r' % (directory.get_path() + path,)) inst.push(fdb.tuple.pack(tuple(result))) elif inst.op == six.u('DIRECTORY_EXISTS'): count = inst.pop() if count == 0: result = directory.exists(inst.tr) log_op('exists %r: %d' % (directory.get_path(), result)) else: path = self.pop_tuples(inst.stack) result = directory.exists(inst.tr, path) log_op('exists %r: %d' % (directory.get_path() + path, result)) if result: inst.push(1) else: inst.push(0) elif inst.op == six.u('DIRECTORY_PACK_KEY'): key_tuple = self.pop_tuples(inst.stack) inst.push(directory.pack(key_tuple)) elif inst.op == six.u('DIRECTORY_UNPACK_KEY'): key = inst.pop() log_op('unpack %r in subspace with prefix %r' % (key, directory.rawPrefix)) tup = directory.unpack(key) for t in tup: inst.push(t) elif inst.op == six.u('DIRECTORY_RANGE'): tup = self.pop_tuples(inst.stack) rng = directory.range(tup) inst.push(rng.start) inst.push(rng.stop) elif inst.op == six.u('DIRECTORY_CONTAINS'): key = inst.pop() result = directory.contains(key) if result: inst.push(1) else: inst.push(0) elif inst.op == six.u('DIRECTORY_OPEN_SUBSPACE'): path = self.pop_tuples(inst.stack) log_op('open_subspace %r (at %r)' % (path, directory.key())) self.append_dir(inst, directory.subspace(path)) elif inst.op == six.u('DIRECTORY_LOG_SUBSPACE'): prefix = inst.pop() inst.tr[prefix + fdb.tuple.pack((self.dir_index,))] = directory.key() elif inst.op == six.u('DIRECTORY_LOG_DIRECTORY'): prefix = inst.pop() exists = directory.exists(inst.tr) if exists: children = tuple(directory.list(inst.tr)) else: children = () logSubspace = fdb.Subspace((self.dir_index,), prefix) inst.tr[logSubspace[six.u('path')]] = fdb.tuple.pack(directory.get_path()) inst.tr[logSubspace[six.u('layer')]] = fdb.tuple.pack((directory.get_layer(),)) inst.tr[logSubspace[six.u('exists')]] = fdb.tuple.pack((int(exists),)) inst.tr[logSubspace[six.u('children')]] = fdb.tuple.pack(children) elif inst.op == six.u('DIRECTORY_STRIP_PREFIX'): s = inst.pop() if not s.startswith(directory.key()): raise Exception('String %r does not start with raw prefix %r' % (s, directory.key())) inst.push(s[len(directory.key()):]) else: raise Exception('Unknown op: %s' % inst.op) except Exception as e: if log_all or log_errors: print(e) # traceback.print_exc(file=sys.stdout) if inst.op in ops_that_create_dirs: self.append_dir(inst, None) inst.push(b'DIRECTORY_ERROR')