System.Data.SQLite

Check-in [e553cea4a0]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add experimental protection against leaking native resources when the Thread.Abort method is used. These changes may not be included in any official release.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | threadAbortProtect
Files: files | file ages | folders
SHA1: e553cea4a0e2fa27f562e8336558111507c04cf4
User & Date: mistachkin 2012-10-12 04:23:13.535
Context
2012-10-12
11:28
Enhance Thread.Abort protection and add a special test for it. Also, enhance the test suite infrastructure in support of this new test and fix comments in a related test file. check-in: 6f81afd900 user: mistachkin tags: threadAbortProtect
04:23
Add experimental protection against leaking native resources when the Thread.Abort method is used. These changes may not be included in any official release. check-in: e553cea4a0 user: mistachkin tags: threadAbortProtect
2012-10-11
06:58
Add sqlite3_backup_finish_interop function with optional diagnostics. Improvements to sqlite3_finalize_interop diagnostics. Make interop function names consistent. check-in: c826898164 user: mistachkin tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to System.Data.SQLite/SQLite3.cs.
317
318
319
320
321
322
323






324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344

345
346
347
348
349
350
351
#if !NET_COMPACT_20 && TRACE_CONNECTION
        Trace.WriteLine(String.Format("Open (Pool): {0}", (_sql != null) ? _sql.ToString() : "<null>"));
#endif
      }

      if (_sql == null)
      {






        IntPtr db;
        SQLiteErrorCode n;

#if !SQLITE_STANDARD
        if ((connectionFlags & SQLiteConnectionFlags.NoExtensionFunctions) != SQLiteConnectionFlags.NoExtensionFunctions)
        {
          n = UnsafeNativeMethods.sqlite3_open_interop(ToUTF8(strFilename), openFlags, out db);
        }
        else
#endif
        {
          n = UnsafeNativeMethods.sqlite3_open_v2(ToUTF8(strFilename), out db, openFlags, IntPtr.Zero);
        }

#if !NET_COMPACT_20 && TRACE_CONNECTION
        Trace.WriteLine(String.Format("Open: {0}", db));
#endif

        if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);

        _sql = new SQLiteConnectionHandle(db);

        lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ }
      }
      // Bind functions to this connection.  If any previous functions of the same name
      // were already bound, then the new bindings replace the old.
      _functionsArray = SQLiteFunction.BindFunctions(this, connectionFlags);
      SetTimeout(0);
      GC.KeepAlive(_sql);







>
>
>
>
>
>
|
|


|
|
|
|
|

|
|
|


|


|
<
|
>







317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348

349
350
351
352
353
354
355
356
357
#if !NET_COMPACT_20 && TRACE_CONNECTION
        Trace.WriteLine(String.Format("Open (Pool): {0}", (_sql != null) ? _sql.ToString() : "<null>"));
#endif
      }

      if (_sql == null)
      {
        try
        {
            // do nothing.
        }
        finally /* NOTE: Thread.Abort() protection. */
        {
          IntPtr db;
          SQLiteErrorCode n;

#if !SQLITE_STANDARD
          if ((connectionFlags & SQLiteConnectionFlags.NoExtensionFunctions) != SQLiteConnectionFlags.NoExtensionFunctions)
          {
            n = UnsafeNativeMethods.sqlite3_open_interop(ToUTF8(strFilename), openFlags, out db);
          }
          else
#endif
          {
            n = UnsafeNativeMethods.sqlite3_open_v2(ToUTF8(strFilename), out db, openFlags, IntPtr.Zero);
          }

#if !NET_COMPACT_20 && TRACE_CONNECTION
          Trace.WriteLine(String.Format("Open: {0}", db));
#endif

          if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);

          _sql = new SQLiteConnectionHandle(db);
        }
        lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ }
      }
      // Bind functions to this connection.  If any previous functions of the same name
      // were already bound, then the new bindings replace the old.
      _functionsArray = SQLiteFunction.BindFunctions(this, connectionFlags);
      SetTimeout(0);
      GC.KeepAlive(_sql);
512
513
514
515
516
517
518

519
520
521
522






523
524
525
526
527
528
529
530
531
532




533
534
535
536
537
538
539
      string typedefs = null;
      SQLiteStatement cmd = null;
      Random rnd = null;
      uint starttick = (uint)Environment.TickCount;

      GCHandle handle = GCHandle.Alloc(b, GCHandleType.Pinned);
      IntPtr psql = handle.AddrOfPinnedObject();

      try
      {
        while ((n == SQLiteErrorCode.Schema || n == SQLiteErrorCode.Locked || n == SQLiteErrorCode.Busy) && retries < 3)
        {






#if !SQLITE_STANDARD
          n = UnsafeNativeMethods.sqlite3_prepare_interop(_sql, psql, b.Length - 1, out stmt, out ptr, out len);
#else
          n = UnsafeNativeMethods.sqlite3_prepare(_sql, psql, b.Length - 1, out stmt, out ptr);
          len = -1;
#endif

#if !NET_COMPACT_20 && TRACE_STATEMENT
          Trace.WriteLine(String.Format("Prepare ({0}): {1}", n, stmt));
#endif





          if (n == SQLiteErrorCode.Schema)
            retries++;
          else if (n == SQLiteErrorCode.Error)
          {
            if (String.Compare(GetLastError(), "near \"TYPES\": syntax error", StringComparison.OrdinalIgnoreCase) == 0)
            {







>




>
>
>
>
>
>

|

|
|



|

>
>
>
>







518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
      string typedefs = null;
      SQLiteStatement cmd = null;
      Random rnd = null;
      uint starttick = (uint)Environment.TickCount;

      GCHandle handle = GCHandle.Alloc(b, GCHandleType.Pinned);
      IntPtr psql = handle.AddrOfPinnedObject();
      SQLiteStatementHandle statementHandle = null;
      try
      {
        while ((n == SQLiteErrorCode.Schema || n == SQLiteErrorCode.Locked || n == SQLiteErrorCode.Busy) && retries < 3)
        {
          try
          {
            // do nothing.
          }
          finally /* NOTE: Thread.Abort() protection. */
          {
#if !SQLITE_STANDARD
            n = UnsafeNativeMethods.sqlite3_prepare_interop(_sql, psql, b.Length - 1, out stmt, out ptr, out len);
#else
            n = UnsafeNativeMethods.sqlite3_prepare(_sql, psql, b.Length - 1, out stmt, out ptr);
            len = -1;
#endif

#if !NET_COMPACT_20 && TRACE_STATEMENT
            Trace.WriteLine(String.Format("Prepare ({0}): {1}", n, stmt));
#endif

            if ((n == SQLiteErrorCode.Ok) && (stmt != IntPtr.Zero))
              statementHandle = new SQLiteStatementHandle(_sql, stmt);
          }

          if (n == SQLiteErrorCode.Schema)
            retries++;
          else if (n == SQLiteErrorCode.Error)
          {
            if (String.Compare(GetLastError(), "near \"TYPES\": syntax error", StringComparison.OrdinalIgnoreCase) == 0)
            {
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
          }
        }

        if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());

        strRemain = UTF8ToString(ptr, len);

        if (stmt != IntPtr.Zero) cmd = new SQLiteStatement(this, flags, new SQLiteStatementHandle(_sql, stmt), strSql.Substring(0, strSql.Length - strRemain.Length), previous);

        return cmd;
      }
      finally
      {
        handle.Free();
      }







|







619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
          }
        }

        if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());

        strRemain = UTF8ToString(ptr, len);

        if (statementHandle != null) cmd = new SQLiteStatement(this, flags, statementHandle, strSql.Substring(0, strSql.Length - strRemain.Length), previous);

        return cmd;
      }
      finally
      {
        handle.Free();
      }
1595
1596
1597
1598
1599
1600
1601








1602
1603
1604
1605
1606
1607



1608
1609
1610

1611
1612
1613
1614
1615
1616
1617
        if (sourceHandle == null)
            throw new InvalidOperationException(
                "Source connection has an invalid handle.");

        byte[] zDestName = ToUTF8(destName);
        byte[] zSourceName = ToUTF8(sourceName);









        IntPtr backup = UnsafeNativeMethods.sqlite3_backup_init(
            destHandle, zDestName, sourceHandle, zSourceName);

        if (backup == IntPtr.Zero)
            throw new SQLiteException(ResultCode(), GetLastError());




        return new SQLiteBackup(
            this, new SQLiteBackupHandle(destHandle, backup),
            destHandle, zDestName, sourceHandle, zSourceName);

    }

    /// <summary>
    /// Copies up to N pages from the source database to the destination
    /// database associated with the specified backup object.
    /// </summary>
    /// <param name="backup">The backup object to use.</param>







>
>
>
>
>
>
>
>
|
|

|
|

>
>
>

<
|
>







1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636

1637
1638
1639
1640
1641
1642
1643
1644
1645
        if (sourceHandle == null)
            throw new InvalidOperationException(
                "Source connection has an invalid handle.");

        byte[] zDestName = ToUTF8(destName);
        byte[] zSourceName = ToUTF8(sourceName);

        SQLiteBackupHandle backupHandle = null;

        try
        {
            // do nothing.
        }
        finally /* NOTE: Thread.Abort() protection. */
        {
            IntPtr backup = UnsafeNativeMethods.sqlite3_backup_init(
                destHandle, zDestName, sourceHandle, zSourceName);

            if (backup == IntPtr.Zero)
                throw new SQLiteException(ResultCode(), GetLastError());

            backupHandle = new SQLiteBackupHandle(destHandle, backup);
        }

        return new SQLiteBackup(

            this, backupHandle, destHandle, zDestName, sourceHandle,
            zSourceName);
    }

    /// <summary>
    /// Copies up to N pages from the source database to the destination
    /// database associated with the specified backup object.
    /// </summary>
    /// <param name="backup">The backup object to use.</param>
Changes to System.Data.SQLite/SQLite3_UTF16.cs.
99
100
101
102
103
104
105
106
107
108
109
110
111






112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

142
143
144
145
146
147
148
      _fileName = strFilename;

      if (usePool)
      {
        _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion);

#if !NET_COMPACT_20 && TRACE_CONNECTION
        Trace.WriteLine(String.Format("Open (Pool): {0}", (_sql != null) ? _sql.ToString() : "<null>"));
#endif
      }

      if (_sql == null)
      {






        IntPtr db;
        SQLiteErrorCode n;

#if !SQLITE_STANDARD
        if ((connectionFlags & SQLiteConnectionFlags.NoExtensionFunctions) != SQLiteConnectionFlags.NoExtensionFunctions)
        {
          n = UnsafeNativeMethods.sqlite3_open16_interop(ToUTF8(strFilename), openFlags, out db);
        }
        else
#endif
        {
          //
          // NOTE: This flag check is designed to enforce the constraint that opening
          //       a database file that does not already exist requires specifying the
          //       "Create" flag, even when a native API is used that does not accept
          //       a flags parameter.
          //
          if (((openFlags & SQLiteOpenFlagsEnum.Create) != SQLiteOpenFlagsEnum.Create) && !File.Exists(strFilename))
            throw new SQLiteException(SQLiteErrorCode.CantOpen, strFilename);

          n = UnsafeNativeMethods.sqlite3_open16(strFilename, out db);
        }

#if !NET_COMPACT_20 && TRACE_CONNECTION
        Trace.WriteLine(String.Format("Open: {0}", db));
#endif

        if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);

        _sql = new SQLiteConnectionHandle(db);

        lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ }
      }
      _functionsArray = SQLiteFunction.BindFunctions(this, connectionFlags);
      SetTimeout(0);
      GC.KeepAlive(_sql);
    }








|





>
>
>
>
>
>
|
|


|
|
|
|
|

|
|
|
|
|
|
|
|
|

|
|


|


|
<
|
>







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
153
154
      _fileName = strFilename;

      if (usePool)
      {
        _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion);

#if !NET_COMPACT_20 && TRACE_CONNECTION
        Trace.WriteLine(String.Format("Open16 (Pool): {0}", (_sql != null) ? _sql.ToString() : "<null>"));
#endif
      }

      if (_sql == null)
      {
        try
        {
            // do nothing.
        }
        finally /* NOTE: Thread.Abort() protection. */
        {
          IntPtr db;
          SQLiteErrorCode n;

#if !SQLITE_STANDARD
          if ((connectionFlags & SQLiteConnectionFlags.NoExtensionFunctions) != SQLiteConnectionFlags.NoExtensionFunctions)
          {
            n = UnsafeNativeMethods.sqlite3_open16_interop(ToUTF8(strFilename), openFlags, out db);
          }
          else
#endif
          {
            //
            // NOTE: This flag check is designed to enforce the constraint that opening
            //       a database file that does not already exist requires specifying the
            //       "Create" flag, even when a native API is used that does not accept
            //       a flags parameter.
            //
            if (((openFlags & SQLiteOpenFlagsEnum.Create) != SQLiteOpenFlagsEnum.Create) && !File.Exists(strFilename))
              throw new SQLiteException(SQLiteErrorCode.CantOpen, strFilename);

            n = UnsafeNativeMethods.sqlite3_open16(strFilename, out db);
          }

#if !NET_COMPACT_20 && TRACE_CONNECTION
          Trace.WriteLine(String.Format("Open16: {0}", db));
#endif

          if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);

          _sql = new SQLiteConnectionHandle(db);
        }
        lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ }
      }
      _functionsArray = SQLiteFunction.BindFunctions(this, connectionFlags);
      SetTimeout(0);
      GC.KeepAlive(_sql);
    }