MTableEntity ImportWithIfMatch(ITableEntity passedEntity, string ifMatch)
        {
            MTableEntity newEntity = ChainTableUtils.CopyEntity <MTableEntity>(passedEntity);

            newEntity.ETag = ifMatch;
            return(newEntity);
        }
Exemplo n.º 2
0
        public SortedDictionary <PrimaryKey, DynamicTableEntity> Dump()
        {
            var dump = new SortedDictionary <PrimaryKey, DynamicTableEntity>();

            foreach (KeyValuePair <PrimaryKey, DynamicTableEntity> kvp in table)
            {
                dump.Add(kvp.Key, ChainTableUtils.CopyEntity <DynamicTableEntity>(kvp.Value));
            }
            return(dump);
        }
Exemplo n.º 3
0
            public Task <TElement> ReadRowAsync()
            {
                CheckDisposed();
                if (enumerator == null)
                {
                    return(Task.FromResult(default(TElement)));
                }
                TElement entity = ChainTableUtils.CopyEntity <TElement>(enumerator.Current.Value);

                MoveNext();
                return(Task.FromResult(entity));
            }
Exemplo n.º 4
0
            public Task <TElement> ReadRowAsync()
            {
                if (continuationKey == null)
                {
                    return(Task.FromResult(default(TElement)));
                }
                List <DynamicTableEntity> possibleRows = outer.GetValidStreamReadRows(startRevision, filterExpr, continuationKey);
                int choiceIndex           = PSharpNondeterminism.Choice(possibleRows.Count);
                DynamicTableEntity choice = possibleRows[choiceIndex];

                Console.WriteLine("NondeterministicQueryStream: possibleRows {0}, choiceIndex {1}",
                                  BetterComparer.ToString(possibleRows), choiceIndex);
                continuationKey = (choice == null) ? null : ChainTableUtils.NextValidPrimaryKeyAfter(choice.GetPrimaryKey());
                return(Task.FromResult((choice == null) ? default(TElement) : ChainTableUtils.CopyEntity <TElement>(choice)));
            }
Exemplo n.º 5
0
        public override Task <IList <TElement> > ExecuteQueryAtomicAsync <TElement>(
            TableQuery <TElement> query, TableRequestOptions requestOptions = null, OperationContext operationContext = null)
        {
            FilterExpression filterExpr = ChainTableUtils.ParseFilterString(query.FilterString);

            ChainTableUtils.GetSingleTargetedPartitionKey(filterExpr);  // validation, ignore result
            if (query.SelectColumns != null)
            {
                throw new NotImplementedException("select");
            }
            if (query.TakeCount != null)
            {
                throw new NotImplementedException("top");
            }
            return(Task.FromResult(
                       (IList <TElement>)(from kvp in table where filterExpr.Evaluate(kvp.Value)
                                          select ChainTableUtils.CopyEntity <TElement>(kvp.Value)).ToList()));
        }
Exemplo n.º 6
0
        public override Task <IList <TableResult> > ExecuteMirrorBatchAsync(
            TableBatchOperation originalBatch, IList <TableResult> originalResponse,
            TableRequestOptions requestOptions = null, OperationContext operationContext = null)
        {
            ChainTableUtils.GetBatchPartitionKey(originalBatch);  // For validation; result ignored

            // Copy the table.  Entities are aliased to the original table, so don't mutate them.
            var tmpTable    = new SortedDictionary <PrimaryKey, DynamicTableEntity>(table);
            int tmpNextEtag = nextEtag;
            var results     = new List <TableResult>();

            for (int i = 0; i < originalBatch.Count; i++)
            {
                TableOperation     op           = originalBatch[i];
                TableOperationType opType       = op.GetOperationType();
                ITableEntity       passedEntity = op.GetEntity();
                PrimaryKey         key          = passedEntity.GetPrimaryKey();
                DynamicTableEntity oldEntity    = tmpTable.GetValueOrDefault(key);

                DynamicTableEntity newEntity  = null;
                HttpStatusCode     statusCode = HttpStatusCode.NoContent;

                if (opType == TableOperationType.Insert)
                {
                    if (oldEntity != null)
                    {
                        throw ChainTableUtils.GenerateBatchException(HttpStatusCode.Conflict, i);
                    }
                    else
                    {
                        newEntity  = ChainTableUtils.CopyEntity <DynamicTableEntity>(passedEntity);
                        statusCode = HttpStatusCode.Created;
                    }
                }
                else if (opType == TableOperationType.InsertOrReplace)
                {
                    newEntity = ChainTableUtils.CopyEntity <DynamicTableEntity>(passedEntity);
                }
                else if (opType == TableOperationType.InsertOrMerge)
                {
                    if (oldEntity == null)
                    {
                        newEntity = ChainTableUtils.CopyEntity <DynamicTableEntity>(passedEntity);
                    }
                    else
                    {
                        newEntity = ChainTableUtils.CopyEntity <DynamicTableEntity>(oldEntity);
                        Merge(newEntity, passedEntity);
                    }
                }
                else if (opType == TableOperationType.Delete &&
                         passedEntity.ETag == ChainTable2Constants.ETAG_DELETE_IF_EXISTS)
                {
                    tmpTable.Remove(key);
                }
                else if (oldEntity == null)
                {
                    throw ChainTableUtils.GenerateBatchException(HttpStatusCode.NotFound, i);
                }
                else if (string.IsNullOrEmpty(passedEntity.ETag))
                {
                    // Enforce this because real Azure table will.
                    // XXX Ideally do this up front.
                    throw new ArgumentException(string.Format("Operation {0} requires an explicit ETag.", i));
                }
                else if (passedEntity.ETag != ChainTable2Constants.ETAG_ANY &&
                         oldEntity.ETag != passedEntity.ETag)
                {
                    throw ChainTableUtils.GenerateBatchException(HttpStatusCode.PreconditionFailed, i);
                }
                else if (opType == TableOperationType.Delete)
                {
                    tmpTable.Remove(key);
                }
                else if (opType == TableOperationType.Replace)
                {
                    newEntity = ChainTableUtils.CopyEntity <DynamicTableEntity>(passedEntity);
                }
                else if (opType == TableOperationType.Merge)
                {
                    newEntity = ChainTableUtils.CopyEntity <DynamicTableEntity>(oldEntity);
                    Merge(newEntity, passedEntity);
                }
                else
                {
                    // IChainTable2 does not allow Retrieve in a batch.
                    throw new NotImplementedException();
                }

                if (newEntity != null)
                {
                    newEntity.ETag      = (originalResponse != null) ? originalResponse[i].Etag : (tmpNextEtag++).ToString();
                    newEntity.Timestamp = DateTimeOffset.MinValue;  // Arbitrary, deterministic
                    tmpTable[key]       = newEntity;
                }
                results.Add(new TableResult {
                    Result         = passedEntity,
                    HttpStatusCode = (int)statusCode,
                    Etag           = (newEntity != null) ? newEntity.ETag : null,
                });
            }

            // If we got here, commit.
            table    = tmpTable;
            nextEtag = tmpNextEtag;
            for (int i = 0; i < originalBatch.Count; i++)
            {
                if (results[i].Etag != null)  // not delete
                {
                    originalBatch[i].GetEntity().ETag = results[i].Etag;
                }
            }
            return(Task.FromResult((IList <TableResult>)results));
        }
        void TranslateOperationForNewTable(
            TableOperation op, MTableEntity existingEntity, bool leaveTombstones,
            ref TableOperation newOp, ref HttpStatusCode?errorCode)
        {
            ITableEntity       passedEntity = op.GetEntity();
            TableOperationType opType       = op.GetOperationType();

            switch (opType)
            {
            case TableOperationType.Insert:
                if (existingEntity == null)
                {
                    newOp = TableOperation.Insert(ChainTableUtils.CopyEntity <MTableEntity>(passedEntity));
                }
                else if (existingEntity.deleted)
                {
                    newOp = TableOperation.Replace(ImportWithIfMatch(passedEntity, existingEntity.ETag));
                }
                else
                {
                    errorCode = HttpStatusCode.Conflict;
                }
                break;

            case TableOperationType.Replace:
                if ((errorCode = CheckExistingEntity(passedEntity, existingEntity)) == null)
                {
                    newOp = TableOperation.Replace(ImportWithIfMatch(passedEntity, existingEntity.ETag));
                }
                break;

            case TableOperationType.Merge:
                if ((errorCode = CheckExistingEntity(passedEntity, existingEntity)) == null)
                {
                    newOp = TableOperation.Merge(ImportWithIfMatch(passedEntity, existingEntity.ETag));
                }
                break;

            case TableOperationType.Delete:
                string buggablePartitionKey, buggableRowKey;
                if (IsBugEnabled(MTableOptionalBug.DeletePrimaryKey))
                {
                    buggablePartitionKey = buggableRowKey = null;
                }
                else
                {
                    buggablePartitionKey = passedEntity.PartitionKey;
                    buggableRowKey       = passedEntity.RowKey;
                }
                if (leaveTombstones)
                {
                    if (passedEntity.ETag == ChainTable2Constants.ETAG_DELETE_IF_EXISTS)
                    {
                        newOp = TableOperation.InsertOrReplace(new MTableEntity {
                            PartitionKey = buggablePartitionKey, RowKey = buggableRowKey, deleted = true
                        });
                    }
                    else if ((errorCode = CheckExistingEntity(passedEntity, existingEntity)) == null)
                    {
                        newOp = TableOperation.Replace(new MTableEntity {
                            PartitionKey = buggablePartitionKey, RowKey = buggableRowKey,
                            deleted      = true, ETag = existingEntity.ETag
                        });
                    }
                }
                else
                {
                    if (passedEntity.ETag == ChainTable2Constants.ETAG_DELETE_IF_EXISTS)
                    {
                        if (existingEntity != null)
                        {
                            newOp = TableOperation.Delete(new MTableEntity {
                                PartitionKey = buggablePartitionKey, RowKey = buggableRowKey,
                                // It's OK to delete the entity and return success whether or not
                                // the entity is a tombstone by the time it is actually deleted.
                                ETag = IsBugEnabled(MTableOptionalBug.DeleteNoLeaveTombstonesETag) ? null : ChainTable2Constants.ETAG_ANY
                            });
                        }
                        // Otherwise generate nothing.
                        // FIXME: This is not linearizable!  It can also generate empty batches.
                    }
                    else if ((errorCode = CheckExistingEntity(passedEntity, existingEntity)) == null)
                    {
                        // Another client in USE_NEW_WITH_TOMBSTONES could concurrently replace the
                        // entity with a tombstone, in which case we need to return 404 to the caller,
                        // hence this needs to be conditioned on the existing ETag.
                        newOp = TableOperation.Delete(new MTableEntity {
                            PartitionKey = buggablePartitionKey, RowKey = buggableRowKey,
                            ETag         = IsBugEnabled(MTableOptionalBug.DeleteNoLeaveTombstonesETag) ? null : existingEntity.ETag
                        });
                    }
                }
                break;

            case TableOperationType.InsertOrReplace:
                newOp = TableOperation.InsertOrReplace(ChainTableUtils.CopyEntity <MTableEntity>(passedEntity));
                break;

            case TableOperationType.InsertOrMerge:
                newOp = TableOperation.InsertOrMerge(ChainTableUtils.CopyEntity <MTableEntity>(passedEntity));
                break;

            default:
                throw new NotImplementedException();
            }
        }