Commit c002a00e authored by Marc Gravell's avatar Marc Gravell

migrate BookSleeve "Scripting" suite; fix "eval inside transaction" bug

parent 6c191d56
...@@ -188,11 +188,11 @@ where Attribute.IsDefined(method, typeof(TestAttribute)) ...@@ -188,11 +188,11 @@ where Attribute.IsDefined(method, typeof(TestAttribute))
} }
Console.WriteLine("Passed: {0}; Failed: {1}", pass, fail); Console.WriteLine("Passed: {0}; Failed: {1}", pass, fail);
foreach (var msg in epicFail) Console.WriteLine(msg); foreach (var msg in epicFail) Console.WriteLine(msg);
#if DEBUG //#if DEBUG
Console.WriteLine(); // Console.WriteLine();
Console.WriteLine("Callbacks: {0:###,###,##0} sync, {1:###,###,##0} async", // Console.WriteLine("Callbacks: {0:###,###,##0} sync, {1:###,###,##0} async",
BookSleeve.RedisConnectionBase.AllSyncCallbacks, BookSleeve.RedisConnectionBase.AllAsyncCallbacks); // BookSleeve.RedisConnectionBase.AllSyncCallbacks, BookSleeve.RedisConnectionBase.AllAsyncCallbacks);
#endif //#endif
} }
} }
......
This diff is collapsed.
...@@ -273,9 +273,18 @@ internal Message GetSelectDatabaseCommand(int targetDatabase, Message message) ...@@ -273,9 +273,18 @@ internal Message GetSelectDatabaseCommand(int targetDatabase, Message message)
} }
return null; return null;
} }
if(message.Command == RedisCommand.SELECT)
{
// this could come from an EVAL/EVALSHA inside a transaction, for example; we'll accept it
bridge.Trace("Switching database: " + targetDatabase);
currentDatabase = targetDatabase;
return null;
}
if (TransactionActive) if (TransactionActive)
{// should never see this, since the API doesn't allow it; thus not too worried about ExceptionFactory {// should never see this, since the API doesn't allow it; thus not too worried about ExceptionFactory
throw new RedisCommandException("Multiple databases inside a transaction are not currently supported" + targetDatabase); throw new RedisCommandException("Multiple databases inside a transaction are not currently supported: " + targetDatabase);
} }
if (available != 0 && targetDatabase >= available) // we positively know it is out of range if (available != 0 && targetDatabase >= available) // we positively know it is out of range
...@@ -284,12 +293,16 @@ internal Message GetSelectDatabaseCommand(int targetDatabase, Message message) ...@@ -284,12 +293,16 @@ internal Message GetSelectDatabaseCommand(int targetDatabase, Message message)
} }
bridge.Trace("Switching database: " + targetDatabase); bridge.Trace("Switching database: " + targetDatabase);
currentDatabase = targetDatabase; currentDatabase = targetDatabase;
return targetDatabase < DefaultRedisDatabaseCount return GetSelectDatabaseCommand(targetDatabase);
? ReusableChangeDatabaseCommands[targetDatabase] // 0-15 by default
: Message.Create(targetDatabase, CommandFlags.FireAndForget, RedisCommand.SELECT);
} }
return null; return null;
} }
internal static Message GetSelectDatabaseCommand(int targetDatabase)
{
return targetDatabase < DefaultRedisDatabaseCount
? ReusableChangeDatabaseCommands[targetDatabase] // 0-15 by default
: Message.Create(targetDatabase, CommandFlags.FireAndForget, RedisCommand.SELECT);
}
internal int GetSentAwaitingResponseCount() internal int GetSentAwaitingResponseCount()
{ {
......
...@@ -7,6 +7,7 @@ namespace StackExchange.Redis ...@@ -7,6 +7,7 @@ namespace StackExchange.Redis
/// </summary> /// </summary>
public abstract class RedisResult public abstract class RedisResult
{ {
// internally, this is very similar to RawResult, except it is designed to be usable // internally, this is very similar to RawResult, except it is designed to be usable
// outside of the IO-processing pipeline: the buffers are standalone, etc // outside of the IO-processing pipeline: the buffers are standalone, etc
...@@ -42,6 +43,11 @@ internal static RedisResult TryCreate(PhysicalConnection connection, RawResult r ...@@ -42,6 +43,11 @@ internal static RedisResult TryCreate(PhysicalConnection connection, RawResult r
} }
} }
/// <summary>
/// Indicates whether this result was a null result
/// </summary>
public abstract bool IsNull { get; }
/// <summary> /// <summary>
/// Interprets the result as a String /// Interprets the result as a String
/// </summary> /// </summary>
...@@ -168,6 +174,10 @@ internal static RedisResult TryCreate(PhysicalConnection connection, RawResult r ...@@ -168,6 +174,10 @@ internal static RedisResult TryCreate(PhysicalConnection connection, RawResult r
internal abstract string[] AsStringArray(); internal abstract string[] AsStringArray();
private sealed class ArrayRedisResult : RedisResult private sealed class ArrayRedisResult : RedisResult
{ {
public override bool IsNull
{
get { return value == null; }
}
private readonly RedisResult[] value; private readonly RedisResult[] value;
public ArrayRedisResult(RedisResult[] value) public ArrayRedisResult(RedisResult[] value)
{ {
...@@ -275,6 +285,10 @@ public ErrorRedisResult(string value) ...@@ -275,6 +285,10 @@ public ErrorRedisResult(string value)
if (value == null) throw new ArgumentNullException("value"); if (value == null) throw new ArgumentNullException("value");
this.value = value; this.value = value;
} }
public override bool IsNull
{
get { return value == null; }
}
public override string ToString() { return value; } public override string ToString() { return value; }
internal override bool AsBoolean() { throw new RedisServerException(value); } internal override bool AsBoolean() { throw new RedisServerException(value); }
...@@ -326,6 +340,11 @@ public SingleRedisResult(RedisValue value) ...@@ -326,6 +340,11 @@ public SingleRedisResult(RedisValue value)
this.value = value; this.value = value;
} }
public override bool IsNull
{
get { return value.IsNull; }
}
public override string ToString() { return value.ToString(); } public override string ToString() { return value.ToString(); }
internal override bool AsBoolean() { return (bool)value; } internal override bool AsBoolean() { return (bool)value; }
......
...@@ -86,6 +86,22 @@ internal override Task<T> ExecuteAsync<T>(Message message, ResultProcessor<T> pr ...@@ -86,6 +86,22 @@ internal override Task<T> ExecuteAsync<T>(Message message, ResultProcessor<T> pr
// store it, and return the task of the *outer* command // store it, and return the task of the *outer* command
// (there is no task for the inner command) // (there is no task for the inner command)
(pending ?? (pending = new List<QueuedMessage>())).Add(queued); (pending ?? (pending = new List<QueuedMessage>())).Add(queued);
switch(message.Command)
{
case RedisCommand.EVAL:
case RedisCommand.EVALSHA:
// people can do very naughty things in an EVAL
// including change the DB; change it back to what we
// think it should be!
var sel = PhysicalConnection.GetSelectDatabaseCommand(message.Db);
queued = new QueuedMessage(sel);
wasQueued = ResultBox<bool>.Get(null);
queued.SetSource(wasQueued, QueuedProcessor.Default);
pending.Add(queued);
break;
}
return task; return task;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment