Commit 7053c56f authored by unknown's avatar unknown

Rewrite of the schema-change handling; the "see if it errors, then recalc" is,...

Rewrite of the schema-change handling; the "see if it errors, then recalc" is, it turns out, stupid.
parent b8dedd0e
...@@ -118,13 +118,40 @@ private Link(TKey key, TValue value, Link<TKey, TValue> tail) ...@@ -118,13 +118,40 @@ private Link(TKey key, TValue value, Link<TKey, TValue> tail)
} }
class CacheInfo class CacheInfo
{ {
public Func<IDataReader, object> Deserializer { get; set; } public DeserializerState Deserializer { get; set; }
public Func<IDataReader, object>[] OtherDeserializers { get; set; } public Func<IDataReader, object>[] OtherDeserializers { get; set; }
public Action<IDbCommand, object> ParamReader { get; set; } public Action<IDbCommand, object> ParamReader { get; set; }
private int hitCount; private int hitCount;
public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); } public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); }
public void RecordHit() { Interlocked.Increment(ref hitCount); } public void RecordHit() { Interlocked.Increment(ref hitCount); }
} }
static int GetColumnHash(IDataReader reader)
{
unchecked
{
int colCount = reader.FieldCount, hash = colCount;
for (int i = 0; i < colCount; i++)
{
object tmp = reader.GetName(i);
hash = (hash * 31) + (tmp == null ? 0 : tmp.GetHashCode());
reader.GetFieldType(i);
hash = (hash * 31) + (tmp == null ? 0 : tmp.GetHashCode());
}
return hash;
}
}
struct DeserializerState
{
public readonly int Hash;
public readonly Func<IDataReader, object> Func;
public DeserializerState(int hash, Func<IDataReader, object> func)
{
Hash = hash;
Func = func;
}
}
/// <summary> /// <summary>
/// Called if the query cache is purged via PurgeQueryCache /// Called if the query cache is purged via PurgeQueryCache
...@@ -580,34 +607,19 @@ private static IEnumerable<T> QueryInternal<T>(this IDbConnection cnn, string sq ...@@ -580,34 +607,19 @@ private static IEnumerable<T> QueryInternal<T>(this IDbConnection cnn, string sq
{ {
using (var reader = cmd.ExecuteReader()) using (var reader = cmd.ExecuteReader())
{ {
Func<Func<IDataReader, object>> cacheDeserializer = () => var tuple = info.Deserializer;
int hash = GetColumnHash(reader);
if (tuple.Func == null || tuple.Hash != hash)
{ {
info.Deserializer = GetDeserializer(typeof(T), reader, 0, -1, false); tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(typeof(T), reader, 0, -1, false));
SetQueryCache(identity, info); SetQueryCache(identity, info);
return info.Deserializer;
};
if (info.Deserializer == null)
{
cacheDeserializer();
} }
var deserializer = info.Deserializer; var func = tuple.Func;
while (reader.Read()) while (reader.Read())
{ {
object next; yield return (T)func(reader);
try
{
next = deserializer(reader);
}
catch (DataException)
{
// give it another shot, in case the underlying schema changed
deserializer = cacheDeserializer();
next = deserializer(reader);
}
yield return (T)next;
} }
} }
...@@ -747,40 +759,25 @@ class DontMap { } ...@@ -747,40 +759,25 @@ class DontMap { }
ownedReader = ownedCommand.ExecuteReader(); ownedReader = ownedCommand.ExecuteReader();
reader = ownedReader; reader = ownedReader;
} }
Func<IDataReader, object> deserializer = null; DeserializerState deserializer = default(DeserializerState);
Func<IDataReader, object>[] otherDeserializers = null; Func<IDataReader, object>[] otherDeserializers = null;
Action cacheDeserializers = () => int hash = GetColumnHash(reader);
{ if ((deserializer = cinfo.Deserializer).Func == null || (otherDeserializers = cinfo.OtherDeserializers) == null || hash != deserializer.Hash)
var deserializers = GenerateDeserializers(new Type[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth)}, splitOn, reader); {
deserializer = cinfo.Deserializer = deserializers[0]; var deserializers = GenerateDeserializers(new Type[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth) }, splitOn, reader);
deserializer = cinfo.Deserializer = new DeserializerState(hash, deserializers[0]);
otherDeserializers = cinfo.OtherDeserializers = deserializers.Skip(1).ToArray(); otherDeserializers = cinfo.OtherDeserializers = deserializers.Skip(1).ToArray();
SetQueryCache(identity, cinfo); SetQueryCache(identity, cinfo);
};
if ((deserializer = cinfo.Deserializer) == null || (otherDeserializers = cinfo.OtherDeserializers) == null)
{
cacheDeserializers();
} }
Func<IDataReader, TReturn> mapIt = GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(deserializer, otherDeserializers, map); Func<IDataReader, TReturn> mapIt = GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(deserializer.Func, otherDeserializers, map);
if (mapIt != null) if (mapIt != null)
{ {
while (reader.Read()) while (reader.Read())
{ {
TReturn next; yield return mapIt(reader);
try
{
next = mapIt(reader);
}
catch (DataException)
{
cacheDeserializers();
mapIt = GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(deserializer, otherDeserializers, map);
next = mapIt(reader);
}
yield return next;
} }
} }
} }
...@@ -1773,19 +1770,14 @@ public IEnumerable<T> Read<T>() ...@@ -1773,19 +1770,14 @@ public IEnumerable<T> Read<T>()
CacheInfo cache = GetCacheInfo(typedIdentity); CacheInfo cache = GetCacheInfo(typedIdentity);
var deserializer = cache.Deserializer; var deserializer = cache.Deserializer;
Func<Func<IDataReader, object>> deserializerGenerator = () => int hash = GetColumnHash(reader);
if (deserializer.Func == null || deserializer.Hash != hash)
{ {
deserializer = GetDeserializer(typeof(T), reader, 0, -1, false); deserializer = new DeserializerState(hash, GetDeserializer(typeof(T), reader, 0, -1, false));
cache.Deserializer = deserializer; cache.Deserializer = deserializer;
return deserializer;
};
if (deserializer == null)
{
deserializer = deserializerGenerator();
} }
consumed = true; consumed = true;
return ReadDeferred<T>(gridIndex, deserializer, typedIdentity, deserializerGenerator); return ReadDeferred<T>(gridIndex, deserializer.Func, typedIdentity);
} }
private IEnumerable<TReturn> MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(object func, string splitOn) private IEnumerable<TReturn> MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(object func, string splitOn)
...@@ -1888,23 +1880,13 @@ public IEnumerable<T> Read<T>() ...@@ -1888,23 +1880,13 @@ public IEnumerable<T> Read<T>()
} }
#endif #endif
private IEnumerable<T> ReadDeferred<T>(int index, Func<IDataReader, object> deserializer, Identity typedIdentity, Func<Func<IDataReader, object>> deserializerGenerator) private IEnumerable<T> ReadDeferred<T>(int index, Func<IDataReader, object> deserializer, Identity typedIdentity)
{ {
try try
{ {
while (index == gridIndex && reader.Read()) while (index == gridIndex && reader.Read())
{ {
object next; yield return (T)deserializer(reader);
try
{
next = deserializer(reader);
}
catch (DataException)
{
deserializer = deserializerGenerator();
next = deserializer(reader);
}
yield return (T)next;
} }
} }
finally // finally so that First etc progresses things even when multiple rows finally // finally so that First etc progresses things even when multiple rows
......
...@@ -1517,6 +1517,47 @@ public void TestCommandWithInheritedTransaction() ...@@ -1517,6 +1517,47 @@ public void TestCommandWithInheritedTransaction()
} }
} }
public void TestReaderWhenResultsChange()
{
try
{
connection.Execute("create table #ResultsChange (X int);create table #ResultsChange2 (Y int);insert #ResultsChange (X) values(1);insert #ResultsChange2 (Y) values(1);");
var obj1 = connection.Query<ResultsChangeType>("select * from #ResultsChange").Single();
obj1.X.IsEqualTo(1);
obj1.Y.IsEqualTo(0);
obj1.Z.IsEqualTo(0);
var obj2 = connection.Query<ResultsChangeType>("select * from #ResultsChange rc inner join #ResultsChange2 rc2 on rc2.Y=rc.X").Single();
obj2.X.IsEqualTo(1);
obj2.Y.IsEqualTo(1);
obj2.Z.IsEqualTo(0);
connection.Execute("alter table #ResultsChange add Z int null");
connection.Execute("update #ResultsChange set Z = 2");
var obj3 = connection.Query<ResultsChangeType>("select * from #ResultsChange").Single();
obj3.X.IsEqualTo(1);
obj3.Y.IsEqualTo(0);
obj3.Z.IsEqualTo(2);
var obj4 = connection.Query<ResultsChangeType>("select * from #ResultsChange rc inner join #ResultsChange2 rc2 on rc2.Y=rc.X").Single();
obj4.X.IsEqualTo(1);
obj4.Y.IsEqualTo(1);
obj4.Z.IsEqualTo(2);
} finally
{
connection.Execute("drop table #ResultsChange;drop table #ResultsChange2;");
}
}
class ResultsChangeType
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}
class TransactedConnection : IDbConnection class TransactedConnection : IDbConnection
{ {
IDbConnection _conn; IDbConnection _conn;
......
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