/// <summary>Remove an existing node from its parents</summary> /// <returns>True if the parent node was found, otherwise false</returns> private async Task<bool> RemoveFromParent(IFdbTransaction tr, IFdbTuple path) { Contract.Requires(tr != null && path != null); var parent = await FindAsync(tr, path.Substring(0, path.Count - 1)).ConfigureAwait(false); if (parent.Exists) { if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Removing path {0} from its parent folder at {1}", path, parent.Subspace.Key); tr.Clear(GetSubDirKey(parent.Subspace, path.Get<string>(-1))); return true; } return false; }
/// <summary>Finds a node subspace, given its path, by walking the tree from the root.</summary> /// <returns>Node if it was found, or null</returns> private async Task<Node> FindAsync(IFdbReadOnlyTransaction tr, IFdbTuple path) { Contract.Requires(tr != null && path != null); // look for the node by traversing from the root down. Stop when crossing a partition... var n = this.RootNode; int i = 0; Slice layer = Slice.Nil; while (i < path.Count) { if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Looking for child {0} under node {1}...", path.Get<string>(i), n.Key); n = NodeWithPrefix(await tr.GetAsync(GetSubDirKey(n, path.Get<string>(i))).ConfigureAwait(false)); if (n == null) { return new Node(null, path.Substring(0, i + 1), path, Slice.Empty); } if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Reading Layer value for subfolder {0} found at {1}", path, n.Key); layer = await tr.GetAsync(n.Pack(LayerSuffix)).ConfigureAwait(false); if (layer == FdbDirectoryPartition.LayerId) { // stop when reaching a partition return new Node(n, path.Substring(0, i + 1), path, FdbDirectoryPartition.LayerId); } ++i; } return new Node(n, path, path, layer); }
/// <summary>Remove an existing node from its parents</summary> /// <returns>True if the parent node was found, otherwise false</returns> private async Task<bool> RemoveFromParent(IFdbTransaction tr, IFdbTuple path) { Contract.Requires(tr != null && path != null); var parent = await FindAsync(tr, path.Substring(0, path.Count - 1)).ConfigureAwait(false); if (parent.Exists) { tr.Clear(GetSubDirKey(parent.Subspace, path.Get<string>(-1))); return true; } return false; }
internal async Task<FdbDirectorySubspace> MoveInternalAsync(IFdbTransaction trans, IFdbTuple oldPath, IFdbTuple newPath, bool throwOnError) { Contract.Requires(trans != null && oldPath != null && newPath != null); if (oldPath.Count == 0) { throw new InvalidOperationException("The root directory may not be moved."); } if (newPath.Count == 0) { throw new InvalidOperationException("The root directory cannot be overwritten."); } if (newPath.StartsWith(oldPath)) { throw new InvalidOperationException(string.Format("The destination directory({0}) cannot be a subdirectory of the source directory({1}).", newPath, oldPath)); } await CheckWriteVersionAsync(trans).ConfigureAwait(false); var oldNode = await FindAsync(trans, oldPath).ConfigureAwait(false); if (!oldNode.Exists) { if (throwOnError) throw new InvalidOperationException(string.Format("The source directory '{0}' does not exist.", oldPath)); return null; } var newNode = await FindAsync(trans, newPath).ConfigureAwait(false); // we have already checked that old and new are under this partition path, but one of them (or both?) could be under a sub-partition.. if (oldNode.IsInPartition(false) || newNode.IsInPartition(false)) { if (!oldNode.IsInPartition(false) || !newNode.IsInPartition(false) || !FdbTuple.Equals(oldNode.Path, newNode.Path)) { throw new InvalidOperationException("Cannot move between partitions."); } // both nodes are in the same sub-partition, delegate to it return await GetPartitionForNode(newNode).DirectoryLayer.MoveInternalAsync(trans, oldNode.PartitionSubPath, newNode.PartitionSubPath, throwOnError).ConfigureAwait(false); } if (newNode.Exists) { if (throwOnError) throw new InvalidOperationException(string.Format("The destination directory '{0}' already exists. Remove it first.", newPath)); return null; } var parentNode = await FindAsync(trans, newPath.Substring(0, newPath.Count - 1)).ConfigureAwait(false); if (!parentNode.Exists) { if (throwOnError) throw new InvalidOperationException(string.Format("The parent of the destination directory '{0}' does not exist. Create it first.", newPath)); return null; } trans.Set(GetSubDirKey(parentNode.Subspace, newPath.Get<string>(-1)), this.NodeSubspace.UnpackSingle<Slice>(oldNode.Subspace.Key)); await RemoveFromParent(trans, oldPath).ConfigureAwait(false); return ContentsOfNode(oldNode.Subspace, newPath, oldNode.Layer); }
internal async Task<FdbDirectorySubspace> CreateOrOpenInternalAsync(IFdbReadOnlyTransaction readTrans, IFdbTransaction trans, IFdbTuple path, Slice layer, Slice prefix, bool allowCreate, bool allowOpen, bool throwOnError) { Contract.Requires(readTrans != null || trans != null, "Need at least one transaction"); Contract.Requires(path != null, "Path must be specified"); Contract.Requires(readTrans == null || trans == null || object.ReferenceEquals(readTrans, trans), "The write transaction should be the same as the read transaction"); if (path.Count == 0) { // Root directory contains node metadata and so may not be opened. throw new InvalidOperationException("The root directory may not be opened."); } // to open an existing directory, we only need the read transaction // if none was specified, we can use the writeable transaction if (readTrans == null) readTrans = trans; await CheckReadVersionAsync(readTrans).ConfigureAwait(false); if (prefix.HasValue && this.Path.Count > 0) throw new InvalidOperationException("Cannot specify a prefix in a partition."); var existingNode = await FindAsync(readTrans, path).ConfigureAwait(false); if (existingNode.Exists) { if (existingNode.IsInPartition(false)) { var subpath = existingNode.PartitionSubPath; var dl = GetPartitionForNode(existingNode).DirectoryLayer; return await dl.CreateOrOpenInternalAsync(readTrans, trans, subpath, layer, prefix, allowCreate, allowOpen, throwOnError).ConfigureAwait(false); } if (!allowOpen) { if (throwOnError) throw new InvalidOperationException(string.Format("The directory {0} already exists.", path)); return null; } if (layer.IsPresent && layer != existingNode.Layer) { throw new InvalidOperationException(String.Format("The directory {0} was created with incompatible layer {1} instead of expected {2}.", path, layer.ToAsciiOrHexaString(), existingNode.Layer.ToAsciiOrHexaString())); } return ContentsOfNode(existingNode.Subspace, path, existingNode.Layer); } if (!allowCreate) { if (throwOnError) throw new InvalidOperationException(string.Format("The directory {0} does not exist.", path)); return null; } // from there, we actually do need a wrtieable transaction if (trans == null) throw new InvalidOperationException("A writeable transaction is needed to create a new directory"); await CheckWriteVersionAsync(trans).ConfigureAwait(false); if (prefix == null) { // automatically allocate a new prefix inside the ContentSubspace long id = await this.Allocator.AllocateAsync(trans).ConfigureAwait(false); prefix = this.ContentSubspace.Pack(id); // ensure that there is no data already present under this prefix if (await trans.GetRange(FdbKeyRange.StartsWith(prefix)).AnyAsync().ConfigureAwait(false)) { throw new InvalidOperationException(String.Format("The database has keys stored at the prefix chosen by the automatic prefix allocator: {0}", prefix.ToAsciiOrHexaString())); } // ensure that the prefix has not already been allocated if (!(await IsPrefixFree(trans.Snapshot, prefix).ConfigureAwait(false))) { throw new InvalidOperationException("The directory layer has manually allocated prefixes that conflict with the automatic prefix allocator."); } } else { // ensure that the prefix has not already been allocated if (!(await IsPrefixFree(trans, prefix).ConfigureAwait(false))) { throw new InvalidOperationException("The given prefix is already in use."); } } // we need to recursively create any missing parents FdbSubspace parentNode; if (path.Count > 1) { var parentSubspace = await CreateOrOpenInternalAsync(readTrans, trans, path.Substring(0, path.Count - 1), Slice.Nil, Slice.Nil, true, true, true).ConfigureAwait(false); parentNode = NodeWithPrefix(parentSubspace.Key); } else { parentNode = this.RootNode; } if (parentNode == null) throw new InvalidOperationException(string.Format("The parent directory of {0} doesn't exist.", path)); // initialize the metadata for this new directory var node = NodeWithPrefix(prefix); trans.Set(GetSubDirKey(parentNode, path.Get<string>(-1)), prefix); SetLayer(trans, node, layer); return ContentsOfNode(node, path, layer); }
/// <summary>Maps an absolute path to a relative path within this directory layer</summary> internal IFdbTuple ToRelativePath(IFdbTuple path) { if (path == null) throw new ArgumentNullException("path"); if (!path.StartsWith(this.Location)) throw new InvalidOperationException("The path cannot be outside of this partition"); return path.Substring(this.Location.Count); }