private static void CompileInsertUpdateClauses(
            IStorageDriver storageDriver, DataContainerDescriptor containerDescriptor, RequestExecutionContextCacheInfo cacheInfo, DriverChangeType changeType)
        {
            var updates = cacheInfo.ParsedRequest.Modify.UpdateAssignments;
            var clauses = cacheInfo.ParsedRequest.Modify.InsertUpdateSetClauses;
            var fields  = cacheInfo.ParsedRequest.Modify.ModifiedFields;

            if (clauses.Count != fields.Count)
            {
                throw new Exception(string.Format("Internal error: insert/update clauses count ({0}) does not match count of modified fields ({1})",
                                                  clauses.Count, fields.Count));
            }

            // compile field assignment clauses (SET clauses or value inserts)
            for (var ordinal = 0; ordinal < clauses.Count; ordinal++)
            {
                var clause = clauses[ordinal];
                var field  = fields[ordinal];

                // for bulk requests, primary key is there but it is only used to lookup the record
                // for non-bulk requests, primary key should not be in the list of UPDATE clauses
                if (changeType == DriverChangeType.Update && !cacheInfo.ParsedRequest.IsBulk)
                {
                    if (!storageDriver.CanUpdateField(field.FieldId))
                    {
                        throw new Exception(string.Format("Cannot update field {0}/{1} on entity {2}", field.FieldId, field.Name, cacheInfo.ParsedRequest.TargetEntity.Name));
                    }
                }

                // prepare Action compilation context
                var compilerState = QueryParser.PrepareCompilerState(containerDescriptor, cacheInfo, null);
                compilerState.CompileToAction = true;

                // extractor has signature like Func<ClauseEvaluationContext, T>
                var extractor = clause == null
                                    ? QueryParser.CompileFieldValueExtractorClause(compilerState, field, containerDescriptor, cacheInfo, MakeNullableType(field.DbType))
                                    : QueryParser.CompileClause(compilerState, clause, containerDescriptor, cacheInfo, MakeNullableType(field.DbType));

                // get the value into local variable, to prevent multiple invokations when row writer checks for null
                var extractedValue = Expression.Variable(extractor.Type);

                // now take the extractor and create another method, that will take the value and then put it into the changebuffer's data
                var changeBufferData = Expression.Field(Expression.Field(compilerState.Context, "ChangeBuffer"), "Data");
                var blockBody        = Expression.Block(
                    new[] { extractedValue },
                    Expression.Assign(extractedValue, extractor),
                    DriverRowData.CreateWriteAccessor(extractedValue, changeBufferData, field.DbType, ordinal));

                updates.Add(
                    new ParsedRequest.FieldAssignment
                {
                    Field = field,
                    CompiledExpression = (Action <ClauseEvaluationContext>)QueryParser.CompileClause(blockBody, compilerState)
                });
            }
        }
        private static void CompileInsertUpdateClauses(
            IStorageDriver storageDriver, DataContainerDescriptor containerDescriptor, RequestExecutionContextCacheInfo cacheInfo, DriverChangeType changeType)
        {
            var updates = cacheInfo.ParsedRequest.Modify.UpdateAssignments;
            var clauses = cacheInfo.ParsedRequest.Modify.InsertUpdateSetClauses;
            var fields = cacheInfo.ParsedRequest.Modify.ModifiedFields;

            if (clauses.Count != fields.Count)
            {
                throw new Exception(string.Format("Internal error: insert/update clauses count ({0}) does not match count of modified fields ({1})",
                    clauses.Count, fields.Count));
            }

            // compile field assignment clauses (SET clauses or value inserts)
            for (var ordinal = 0; ordinal < clauses.Count; ordinal++)
            {
                var clause = clauses[ordinal];
                var field = fields[ordinal];

                // for bulk requests, primary key is there but it is only used to lookup the record
                // for non-bulk requests, primary key should not be in the list of UPDATE clauses
                if (changeType == DriverChangeType.Update && !cacheInfo.ParsedRequest.IsBulk)
                {
                    if (!storageDriver.CanUpdateField(field.FieldId))
                    {
                        throw new Exception(string.Format("Cannot update field {0}/{1} on entity {2}", field.FieldId, field.Name, cacheInfo.ParsedRequest.TargetEntity.Name));
                    }
                }

                // prepare Action compilation context
                var compilerState = QueryParser.PrepareCompilerState(containerDescriptor, cacheInfo, null);
                compilerState.CompileToAction = true;

                // extractor has signature like Func<ClauseEvaluationContext, T>
                var extractor = clause == null
                                    ? QueryParser.CompileFieldValueExtractorClause(compilerState, field, containerDescriptor, cacheInfo, MakeNullableType(field.DbType))
                                    : QueryParser.CompileClause(compilerState, clause, containerDescriptor, cacheInfo, MakeNullableType(field.DbType));
                // get the value into local variable, to prevent multiple invokations when row writer checks for null
                var extractedValue = Expression.Variable(extractor.Type);

                // now take the extractor and create another method, that will take the value and then put it into the changebuffer's data
                var changeBufferData = Expression.Field(Expression.Field(compilerState.Context, "ChangeBuffer"), "Data");
                var blockBody = Expression.Block(
                    new[] {extractedValue},
                    Expression.Assign(extractedValue, extractor),
                    DriverRowData.CreateWriteAccessor(extractedValue, changeBufferData, field.DbType, ordinal));

                updates.Add(
                    new ParsedRequest.FieldAssignment
                    {
                        Field = field,
                        CompiledExpression = (Action<ClauseEvaluationContext>)QueryParser.CompileClause(blockBody, compilerState)
                    });
            }
        }