Skip to content

Commit d2b144c

Browse files
committed
Move prepared inserts out of mapping
This was the wrong place for them because they are tied to connections and could accidentally get shared between connections. Also remove NO_CONCURRENT as it's dangerous
1 parent 5356727 commit d2b144c

File tree

3 files changed

+85
-112
lines changed

3 files changed

+85
-112
lines changed

nuget/SQLite-net/SQLite-net.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<DebugType>full</DebugType>
2424
<Optimize>false</Optimize>
2525
<OutputPath>bin\Debug\</OutputPath>
26-
<DefineConstants>TRACE;DEBUG;USE_SQLITEPCL_RAW;NO_CONCURRENT</DefineConstants>
26+
<DefineConstants>DEBUG;USE_SQLITEPCL_RAW</DefineConstants>
2727
<ErrorReport>prompt</ErrorReport>
2828
<WarningLevel>4</WarningLevel>
2929
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
@@ -34,7 +34,7 @@
3434
<DebugType>portable</DebugType>
3535
<Optimize>true</Optimize>
3636
<OutputPath>bin\Release\</OutputPath>
37-
<DefineConstants>TRACE;USE_SQLITEPCL_RAW;NO_CONCURRENT</DefineConstants>
37+
<DefineConstants>USE_SQLITEPCL_RAW</DefineConstants>
3838
<ErrorReport>prompt</ErrorReport>
3939
<WarningLevel>4</WarningLevel>
4040
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

src/SQLite.cs

Lines changed: 78 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,6 @@
3030
using System.Runtime.InteropServices;
3131
#endif
3232
using System.Collections.Generic;
33-
#if NO_CONCURRENT
34-
using ConcurrentStringDictionary = System.Collections.Generic.Dictionary<string, object>;
35-
using SQLite.Extensions;
36-
#else
37-
using ConcurrentStringDictionary = System.Collections.Concurrent.ConcurrentDictionary<string, object>;
38-
#endif
3933
using System.Reflection;
4034
using System.Linq;
4135
using System.Linq.Expressions;
@@ -1578,7 +1572,7 @@ public int Insert (object obj, string extra, Type objType)
15781572
vals[i] = cols[i].GetValue (obj);
15791573
}
15801574

1581-
var insertCmd = map.GetInsertCommand (this, extra);
1575+
var insertCmd = GetInsertCommand (map, extra);
15821576
int count;
15831577

15841578
lock (insertCmd) {
@@ -1605,6 +1599,61 @@ public int Insert (object obj, string extra, Type objType)
16051599
return count;
16061600
}
16071601

1602+
readonly Dictionary<Tuple<string, string>, PreparedSqlLiteInsertCommand> _insertCommandMap = new Dictionary<Tuple<string, string>, PreparedSqlLiteInsertCommand> ();
1603+
1604+
PreparedSqlLiteInsertCommand GetInsertCommand (TableMapping map, string extra)
1605+
{
1606+
PreparedSqlLiteInsertCommand prepCmd;
1607+
1608+
var key = Tuple.Create (map.MappedType.FullName, extra);
1609+
1610+
lock (_insertCommandMap) {
1611+
_insertCommandMap.TryGetValue (key, out prepCmd);
1612+
}
1613+
1614+
if (prepCmd == null) {
1615+
prepCmd = CreateInsertCommand (map, extra);
1616+
var added = false;
1617+
lock (_insertCommandMap) {
1618+
if (!_insertCommandMap.ContainsKey (key)) {
1619+
_insertCommandMap.Add (key, prepCmd);
1620+
added = true;
1621+
}
1622+
}
1623+
if (!added) {
1624+
prepCmd.Dispose ();
1625+
}
1626+
}
1627+
1628+
return prepCmd;
1629+
}
1630+
1631+
PreparedSqlLiteInsertCommand CreateInsertCommand (TableMapping map, string extra)
1632+
{
1633+
var cols = map.InsertColumns;
1634+
string insertSql;
1635+
if (cols.Length == 0 && map.Columns.Length == 1 && map.Columns[0].IsAutoInc) {
1636+
insertSql = string.Format ("insert {1} into \"{0}\" default values", map.TableName, extra);
1637+
}
1638+
else {
1639+
var replacing = string.Compare (extra, "OR REPLACE", StringComparison.OrdinalIgnoreCase) == 0;
1640+
1641+
if (replacing) {
1642+
cols = map.InsertOrReplaceColumns;
1643+
}
1644+
1645+
insertSql = string.Format ("insert {3} into \"{0}\"({1}) values ({2})", map.TableName,
1646+
string.Join (",", (from c in cols
1647+
select "\"" + c.Name + "\"").ToArray ()),
1648+
string.Join (",", (from c in cols
1649+
select "?").ToArray ()), extra);
1650+
1651+
}
1652+
1653+
var insertCommand = new PreparedSqlLiteInsertCommand (this, insertSql);
1654+
return insertCommand;
1655+
}
1656+
16081657
/// <summary>
16091658
/// Updates all of the columns of a table using the specified object
16101659
/// except for its primary key.
@@ -1844,10 +1893,11 @@ protected virtual void Dispose (bool disposing)
18441893
if (_open && Handle != NullHandle) {
18451894
try {
18461895
if (disposing) {
1847-
if (_mappings != null) {
1848-
foreach (var sqlInsertCommand in _mappings.Values) {
1896+
lock (_insertCommandMap) {
1897+
foreach (var sqlInsertCommand in _insertCommandMap.Values) {
18491898
sqlInsertCommand.Dispose ();
18501899
}
1900+
_insertCommandMap.Clear ();
18511901
}
18521902

18531903
var r = useClose2 ? SQLite3.Close2 (Handle) : SQLite3.Close (Handle);
@@ -2114,7 +2164,6 @@ from p in ti.DeclaredProperties
21142164
// People should not be calling Get/Find without a PK
21152165
GetByPrimaryKeySql = string.Format ("select * from \"{0}\" limit 1", TableName);
21162166
}
2117-
_insertCommandMap = new ConcurrentStringDictionary ();
21182167
}
21192168

21202169
public bool HasAutoIncPK { get; private set; }
@@ -2156,59 +2205,6 @@ public Column FindColumn (string columnName)
21562205
return exact;
21572206
}
21582207

2159-
ConcurrentStringDictionary _insertCommandMap;
2160-
2161-
public PreparedSqlLiteInsertCommand GetInsertCommand (SQLiteConnection conn, string extra)
2162-
{
2163-
object prepCmdO;
2164-
2165-
if (!_insertCommandMap.TryGetValue (extra, out prepCmdO)) {
2166-
var prepCmd = CreateInsertCommand (conn, extra);
2167-
prepCmdO = prepCmd;
2168-
if (!_insertCommandMap.TryAdd (extra, prepCmd)) {
2169-
// Concurrent add attempt beat us.
2170-
prepCmd.Dispose ();
2171-
_insertCommandMap.TryGetValue (extra, out prepCmdO);
2172-
}
2173-
}
2174-
return (PreparedSqlLiteInsertCommand)prepCmdO;
2175-
}
2176-
2177-
PreparedSqlLiteInsertCommand CreateInsertCommand (SQLiteConnection conn, string extra)
2178-
{
2179-
var cols = InsertColumns;
2180-
string insertSql;
2181-
if (!cols.Any () && Columns.Count () == 1 && Columns[0].IsAutoInc) {
2182-
insertSql = string.Format ("insert {1} into \"{0}\" default values", TableName, extra);
2183-
}
2184-
else {
2185-
var replacing = string.Compare (extra, "OR REPLACE", StringComparison.OrdinalIgnoreCase) == 0;
2186-
2187-
if (replacing) {
2188-
cols = InsertOrReplaceColumns;
2189-
}
2190-
2191-
insertSql = string.Format ("insert {3} into \"{0}\"({1}) values ({2})", TableName,
2192-
string.Join (",", (from c in cols
2193-
select "\"" + c.Name + "\"").ToArray ()),
2194-
string.Join (",", (from c in cols
2195-
select "?").ToArray ()), extra);
2196-
2197-
}
2198-
2199-
var insertCommand = new PreparedSqlLiteInsertCommand (conn);
2200-
insertCommand.CommandText = insertSql;
2201-
return insertCommand;
2202-
}
2203-
2204-
protected internal void Dispose ()
2205-
{
2206-
foreach (var pair in _insertCommandMap) {
2207-
((PreparedSqlLiteInsertCommand)pair.Value).Dispose ();
2208-
}
2209-
_insertCommandMap = null;
2210-
}
2211-
22122208
public class Column
22132209
{
22142210
PropertyInfo _prop;
@@ -2845,32 +2841,37 @@ object ReadCol (Sqlite3Statement stmt, int index, SQLite3.ColType type, Type clr
28452841
/// <summary>
28462842
/// Since the insert never changed, we only need to prepare once.
28472843
/// </summary>
2848-
public class PreparedSqlLiteInsertCommand : IDisposable
2844+
class PreparedSqlLiteInsertCommand : IDisposable
28492845
{
2850-
public bool Initialized { get; set; }
2846+
bool Initialized;
28512847

2852-
protected SQLiteConnection Connection { get; set; }
2848+
SQLiteConnection Connection;
28532849

2854-
public string CommandText { get; set; }
2850+
string CommandText;
28552851

2856-
protected Sqlite3Statement Statement { get; set; }
2857-
internal static readonly Sqlite3Statement NullStatement = default (Sqlite3Statement);
2852+
Sqlite3Statement Statement;
2853+
static readonly Sqlite3Statement NullStatement = default (Sqlite3Statement);
28582854

2859-
internal PreparedSqlLiteInsertCommand (SQLiteConnection conn)
2855+
public PreparedSqlLiteInsertCommand (SQLiteConnection conn, string commandText)
28602856
{
28612857
Connection = conn;
2858+
CommandText = commandText;
28622859
}
28632860

28642861
public int ExecuteNonQuery (object[] source)
28652862
{
2863+
if (Initialized && Statement == NullStatement) {
2864+
throw new ObjectDisposedException (nameof (PreparedSqlLiteInsertCommand));
2865+
}
2866+
28662867
if (Connection.Trace) {
28672868
Connection.Tracer?.Invoke ("Executing: " + CommandText);
28682869
}
28692870

28702871
var r = SQLite3.Result.OK;
28712872

28722873
if (!Initialized) {
2873-
Statement = Prepare ();
2874+
Statement = SQLite3.Prepare2 (Connection.Handle, CommandText);
28742875
Initialized = true;
28752876
}
28762877

@@ -2902,28 +2903,19 @@ public int ExecuteNonQuery (object[] source)
29022903
}
29032904
}
29042905

2905-
protected virtual Sqlite3Statement Prepare ()
2906-
{
2907-
var stmt = SQLite3.Prepare2 (Connection.Handle, CommandText);
2908-
return stmt;
2909-
}
2910-
29112906
public void Dispose ()
29122907
{
29132908
Dispose (true);
29142909
GC.SuppressFinalize (this);
29152910
}
29162911

2917-
private void Dispose (bool disposing)
2912+
void Dispose (bool disposing)
29182913
{
2919-
if (Statement != NullStatement) {
2920-
try {
2921-
SQLite3.Finalize (Statement);
2922-
}
2923-
finally {
2924-
Statement = NullStatement;
2925-
Connection = null;
2926-
}
2914+
var s = Statement;
2915+
Statement = NullStatement;
2916+
Connection = null;
2917+
if (s != NullStatement) {
2918+
SQLite3.Finalize (s);
29272919
}
29282920
}
29292921

@@ -4074,24 +4066,3 @@ public enum ColType : int
40744066
}
40754067
}
40764068
}
4077-
4078-
#if NO_CONCURRENT
4079-
namespace SQLite.Extensions
4080-
{
4081-
public static class ListEx
4082-
{
4083-
public static bool TryAdd<TKey, TValue> (this IDictionary<TKey, TValue> dict, TKey key, TValue value)
4084-
{
4085-
try {
4086-
dict.Add (key, value);
4087-
return true;
4088-
}
4089-
catch (ArgumentException) {
4090-
return false;
4091-
}
4092-
}
4093-
}
4094-
}
4095-
#endif
4096-
4097-

tests/NotNullAttributeTest.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,6 @@ public void UpdateQueryWithNullThrowsException ()
285285
public void ExecuteNonQueryWithNullThrowsException ()
286286
{
287287
using (TestDb db = new TestDb ()) {
288-
TableMapping map;
289288

290289
db.CreateTable<NotNullNoPK> ();
291290

@@ -297,8 +296,11 @@ public void ExecuteNonQueryWithNullThrowsException ()
297296
};
298297
db.Insert (obj);
299298

300-
map = db.GetMapping<NotNullNoPK> ();
301-
map.GetInsertCommand (db, "OR REPLACE").ExecuteNonQuery (new object[] { 1, null, 123, null, null, null });
299+
NotNullNoPK obj2 = new NotNullNoPK () {
300+
objectId = 1,
301+
OptionalIntProp = 123,
302+
};
303+
db.InsertOrReplace (obj2);
302304
}
303305
catch (NotNullConstraintViolationException) {
304306
return;

0 commit comments

Comments
 (0)