System.Data.SQLite

Check-in [0e4ebe697a]
Login

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

Overview
Comment:Improve diagnostics for test data-1.1. Fix connection handle leak by removing superfluous _ownHandle fields and add properties instead.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | virtualTables
Files: files | file ages | folders
SHA1: 0e4ebe697a83904197f671e5b88cef1c1e3957cb
User & Date: mistachkin 2013-06-22 00:52:12.459
Context
2013-06-22
01:35
Make the error codes used with the SQLiteException class consistent. Throw an exception if the SQLite3.Open method is called when already open. check-in: 7aae5042f8 user: mistachkin tags: virtualTables
00:52
Improve diagnostics for test data-1.1. Fix connection handle leak by removing superfluous _ownHandle fields and add properties instead. check-in: 0e4ebe697a user: mistachkin tags: virtualTables
2013-06-21
23:44
In the SQLite3.Close method, be sure to null out of the _sql field when _ownHandle is false. Centralize the SQLite3 instance creation logic used by the SQLiteConnection class. Restrict the ConnectionStringBuilder test to run only when SQLite is in use. check-in: 67f779810e user: mistachkin tags: virtualTables
Changes
Unified Diff Ignore Whitespace Patch
Changes to System.Data.SQLite/SQLite3.cs.
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
    protected bool _usePool;
    protected int _poolVersion;

#if (NET_35 || NET_40 || NET_45) && !PLATFORM_COMPACTFRAMEWORK
    private bool _buildingSchema;
#endif

    /// <summary>
    /// This field will be non-zero if this instance owns the native connection
    /// handle and should dispose of it when it is no longer needed.
    /// </summary>
    protected bool _ownHandle;

    /// <summary>
    /// The user-defined functions registered on this connection
    /// </summary>
    protected SQLiteFunction[] _functionsArray;

    /// <summary>
    /// The modules created using this connection.







<
<
<
<
<
<







68
69
70
71
72
73
74






75
76
77
78
79
80
81
    protected bool _usePool;
    protected int _poolVersion;

#if (NET_35 || NET_40 || NET_45) && !PLATFORM_COMPACTFRAMEWORK
    private bool _buildingSchema;
#endif







    /// <summary>
    /// The user-defined functions registered on this connection
    /// </summary>
    protected SQLiteFunction[] _functionsArray;

    /// <summary>
    /// The modules created using this connection.
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
        string fileName,
        bool ownHandle
        )
      : base(fmt, kind, fmtString)
    {
        if (db != IntPtr.Zero)
        {
            _ownHandle = ownHandle;
            _sql = new SQLiteConnectionHandle(db, _ownHandle);
            _fileName = fileName;
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////

    #region IDisposable "Pattern" Members







<
|







92
93
94
95
96
97
98

99
100
101
102
103
104
105
106
        string fileName,
        bool ownHandle
        )
      : base(fmt, kind, fmtString)
    {
        if (db != IntPtr.Zero)
        {

            _sql = new SQLiteConnectionHandle(db, ownHandle);
            _fileName = fileName;
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////

    #region IDisposable "Pattern" Members
168
169
170
171
172
173
174


175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

    // It isn't necessary to cleanup any functions we've registered.  If the connection
    // goes to the pool and is resurrected later, re-registered functions will overwrite the
    // previous functions.  The SQLiteFunctionCookieHandle will take care of freeing unmanaged
    // resources belonging to the previously-registered functions.
    internal override void Close(bool canThrow)
    {


      if (!_ownHandle)
      {
        _sql = null;
        return;
      }

      if (_sql != null)
      {
          if (_usePool)
          {
              if (SQLiteBase.ResetConnection(_sql, _sql, canThrow))
              {
                  SQLiteConnectionPool.Add(_fileName, _sql, _poolVersion);

#if !NET_COMPACT_20 && TRACE_CONNECTION







>
>
|
|
|
|
|

<
<







161
162
163
164
165
166
167
168
169
170
171
172
173
174
175


176
177
178
179
180
181
182

    // It isn't necessary to cleanup any functions we've registered.  If the connection
    // goes to the pool and is resurrected later, re-registered functions will overwrite the
    // previous functions.  The SQLiteFunctionCookieHandle will take care of freeing unmanaged
    // resources belonging to the previously-registered functions.
    internal override void Close(bool canThrow)
    {
      if (_sql != null)
      {
          if (!_sql.OwnHandle)
          {
              _sql = null;
              return;
          }



          if (_usePool)
          {
              if (SQLiteBase.ResetConnection(_sql, _sql, canThrow))
              {
                  SQLiteConnectionPool.Add(_fileName, _sql, _poolVersion);

#if !NET_COMPACT_20 && TRACE_CONNECTION
340
341
342
343
344
345
346


















347
348
349
350
351
352
353
        UnsafeNativeMethods.sqlite3_memory_highwater_interop(0, ref bytes);
        return bytes;
#else
        throw new NotImplementedException();
#endif
      }
    }



















    internal override SQLiteErrorCode SetMemoryStatus(bool value)
    {
        return StaticSetMemoryStatus(value);
    }

    internal static SQLiteErrorCode StaticSetMemoryStatus(bool value)







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
358
359
360
361
362
363
364
        UnsafeNativeMethods.sqlite3_memory_highwater_interop(0, ref bytes);
        return bytes;
#else
        throw new NotImplementedException();
#endif
      }
    }

    /// <summary>
    /// Returns non-zero if the underlying native connection handle is owned
    /// by this instance.
    /// </summary>
    internal override bool OwnHandle
    {
        get
        {
            if (_sql == null)
            {
                throw new SQLiteException(SQLiteErrorCode.Error,
                    "no connection handle available");
            }

            return _sql.OwnHandle;
        }
    }

    internal override SQLiteErrorCode SetMemoryStatus(bool value)
    {
        return StaticSetMemoryStatus(value);
    }

    internal static SQLiteErrorCode StaticSetMemoryStatus(bool value)
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
          }

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

          if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);
          _ownHandle = true;
          _sql = new SQLiteConnectionHandle(db, _ownHandle);
        }
        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);







<
|







424
425
426
427
428
429
430

431
432
433
434
435
436
437
438
          }

#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, true);
        }
        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);
Changes to System.Data.SQLite/SQLite3_UTF16.cs.
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
          }

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

          if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);
          base._ownHandle = true;
          _sql = new SQLiteConnectionHandle(db, base._ownHandle);
        }
        lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ }
      }
      _functionsArray = SQLiteFunction.BindFunctions(this, connectionFlags);
      SetTimeout(0);
      GC.KeepAlive(_sql);
    }







<
|







146
147
148
149
150
151
152

153
154
155
156
157
158
159
160
          }

#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, true);
        }
        lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ }
      }
      _functionsArray = SQLiteFunction.BindFunctions(this, connectionFlags);
      SetTimeout(0);
      GC.KeepAlive(_sql);
    }
Changes to System.Data.SQLite/SQLiteBase.cs.
54
55
56
57
58
59
60




61
62
63
64
65
66
67
    /// </summary>
    internal abstract long MemoryUsed { get; }
    /// <summary>
    /// Returns the maximum amount of memory (in bytes) used by the SQLite core library since the high-water mark was last reset.
    /// This is not really a per-connection value, it is global to the process.
    /// </summary>
    internal abstract long MemoryHighwater { get; }




    /// <summary>
    /// Sets the status of the memory usage tracking subsystem in the SQLite core library.  By default, this is enabled.
    /// If this is disabled, memory usage tracking will not be performed.  This is not really a per-connection value, it is
    /// global to the process.
    /// </summary>
    /// <param name="value">Non-zero to enable memory usage tracking, zero otherwise.</param>
    /// <returns>A standard SQLite return code (i.e. zero for success and non-zero for failure).</returns>







>
>
>
>







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    /// </summary>
    internal abstract long MemoryUsed { get; }
    /// <summary>
    /// Returns the maximum amount of memory (in bytes) used by the SQLite core library since the high-water mark was last reset.
    /// This is not really a per-connection value, it is global to the process.
    /// </summary>
    internal abstract long MemoryHighwater { get; }
    /// <summary>
    /// Returns non-zero if the underlying native connection handle is owned by this instance.
    /// </summary>
    internal abstract bool OwnHandle { get; }
    /// <summary>
    /// Sets the status of the memory usage tracking subsystem in the SQLite core library.  By default, this is enabled.
    /// If this is disabled, memory usage tracking will not be performed.  This is not really a per-connection value, it is
    /// global to the process.
    /// </summary>
    /// <param name="value">Non-zero to enable memory usage tracking, zero otherwise.</param>
    /// <returns>A standard SQLite return code (i.e. zero for success and non-zero for failure).</returns>
Changes to System.Data.SQLite/SQLiteConnection.cs.
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// Whether or not the connection is enlisted in a distrubuted transaction
    /// </summary>
    internal SQLiteEnlistment _enlistment;
#endif

    /// <summary>
    /// This field will be non-zero if this instance owns the native connection
    /// handle and should dispose of it when it is no longer needed.
    /// </summary>
    private bool _ownHandle;

    /// <summary>
    /// The base SQLite object to interop with
    /// </summary>
    internal SQLiteBase _sql;
    /// <summary>
    /// The database filename minus path and extension
    /// </summary>







<
<
<
<
<
<







400
401
402
403
404
405
406






407
408
409
410
411
412
413
#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// Whether or not the connection is enlisted in a distrubuted transaction
    /// </summary>
    internal SQLiteEnlistment _enlistment;
#endif







    /// <summary>
    /// The base SQLite object to interop with
    /// </summary>
    internal SQLiteBase _sql;
    /// <summary>
    /// The database filename minus path and extension
    /// </summary>
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
    /// <param name="ownHandle">
    /// Non-zero if this instance owns the native connection handle and
    /// should dispose of it when it is no longer needed.
    /// </param>
    internal SQLiteConnection(IntPtr db, string fileName, bool ownHandle)
        : this()
    {
        _ownHandle = ownHandle;

        _sql = new SQLite3(
            SQLiteDateFormats.Default, DateTimeKind.Unspecified, null,
            db, fileName, _ownHandle);

        _flags = SQLiteConnectionFlags.None;

        _connectionState = (db != IntPtr.Zero) ?
            ConnectionState.Open : ConnectionState.Closed;

        _connectionString = null; /* unknown */







<
<


|







501
502
503
504
505
506
507


508
509
510
511
512
513
514
515
516
517
    /// <param name="ownHandle">
    /// Non-zero if this instance owns the native connection handle and
    /// should dispose of it when it is no longer needed.
    /// </param>
    internal SQLiteConnection(IntPtr db, string fileName, bool ownHandle)
        : this()
    {


        _sql = new SQLite3(
            SQLiteDateFormats.Default, DateTimeKind.Unspecified, null,
            db, fileName, ownHandle);

        _flags = SQLiteConnectionFlags.None;

        _connectionState = (db != IntPtr.Zero) ?
            ConnectionState.Open : ConnectionState.Closed;

        _connectionString = null; /* unknown */
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
        string dateTimeFormat = FindKey(opts, "DateTimeFormatString",
            DefaultDateTimeFormatString);

        //
        // NOTE: SQLite automatically sets the encoding of the database
        //       to UTF16 if called from sqlite3_open16().
        //
        _ownHandle = true;

        if (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding",
                  DefaultUseUTF16Encoding.ToString())))
        {
            _sql = new SQLite3_UTF16(
                dateFormat, kind, dateTimeFormat, IntPtr.Zero, null,
                _ownHandle);
        }
        else
        {
            _sql = new SQLite3(
                dateFormat, kind, dateTimeFormat, IntPtr.Zero, null,
                _ownHandle);
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////

    #region IDisposable "Pattern" Members
    private bool disposed;







<
<





|





|







897
898
899
900
901
902
903


904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
        string dateTimeFormat = FindKey(opts, "DateTimeFormatString",
            DefaultDateTimeFormatString);

        //
        // NOTE: SQLite automatically sets the encoding of the database
        //       to UTF16 if called from sqlite3_open16().
        //


        if (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding",
                  DefaultUseUTF16Encoding.ToString())))
        {
            _sql = new SQLite3_UTF16(
                dateFormat, kind, dateTimeFormat, IntPtr.Zero, null,
                false);
        }
        else
        {
            _sql = new SQLite3(
                dateFormat, kind, dateTimeFormat, IntPtr.Zero, null,
                false);
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////

    #region IDisposable "Pattern" Members
    private bool disposed;
2212
2213
2214
2215
2216
2217
2218

















2219
2220
2221
2222
2223
2224
2225
    /// possible values.
    /// </summary>
    public SQLiteConnectionFlags Flags
    {
      get { CheckDisposed(); return _flags; }
      set { CheckDisposed(); _flags = value; }
    }


















    /// <summary>
    /// Returns the version of the underlying SQLite database engine
    /// </summary>
#if !PLATFORM_COMPACTFRAMEWORK
    [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
#endif







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
    /// possible values.
    /// </summary>
    public SQLiteConnectionFlags Flags
    {
      get { CheckDisposed(); return _flags; }
      set { CheckDisposed(); _flags = value; }
    }

    /// <summary>
    /// Returns non-zero if the underlying native connection handle is
    /// owned by this instance.
    /// </summary>
    public bool OwnHandle
    {
        get
        {
            CheckDisposed();

            if (_sql == null)
                throw new InvalidOperationException("Database connection not valid for checking handle.");

            return _sql.OwnHandle;
        }
    }

    /// <summary>
    /// Returns the version of the underlying SQLite database engine
    /// </summary>
#if !PLATFORM_COMPACTFRAMEWORK
    [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
#endif
Changes to System.Data.SQLite/SQLiteLog.cs.
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
                //
                // NOTE: Create an instance of the SQLite wrapper class.
                //
                if (_sql == null)
                {
                    _sql = new SQLite3(
                        SQLiteDateFormats.Default, DateTimeKind.Unspecified,
                        null, IntPtr.Zero, null, true);
                }

                //
                // NOTE: Create a single "global" (i.e. per-process) callback
                //       to register with SQLite.  This callback will pass the
                //       event on to any registered handler.  We only want to
                //       do this once.







|







180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
                //
                // NOTE: Create an instance of the SQLite wrapper class.
                //
                if (_sql == null)
                {
                    _sql = new SQLite3(
                        SQLiteDateFormats.Default, DateTimeKind.Unspecified,
                        null, IntPtr.Zero, null, false);
                }

                //
                // NOTE: Create a single "global" (i.e. per-process) callback
                //       to register with SQLite.  This callback will pass the
                //       event on to any registered handler.  We only want to
                //       do this once.
Changes to System.Data.SQLite/UnsafeNativeMethods.cs.
2272
2273
2274
2275
2276
2277
2278















2279
2280
2281
2282
2283
2284
2285
#if COUNT_HANDLE
        public int WasReleasedOk()
        {
            return Interlocked.Decrement(
                ref UnsafeNativeMethods.connectionCount);
        }
#endif
















        ///////////////////////////////////////////////////////////////////////

        public override bool IsInvalid
        {
            get
            {







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
#if COUNT_HANDLE
        public int WasReleasedOk()
        {
            return Interlocked.Decrement(
                ref UnsafeNativeMethods.connectionCount);
        }
#endif

        ///////////////////////////////////////////////////////////////////////

        public bool OwnHandle
        {
            get
            {
#if PLATFORM_COMPACTFRAMEWORK
                lock (syncRoot)
#endif
                {
                    return ownHandle;
                }
            }
        }

        ///////////////////////////////////////////////////////////////////////

        public override bool IsInvalid
        {
            get
            {
Changes to Tests/basic.eagle.
74
75
76
77
78
79
80









81
82
83
84
85
86
87
    # NOTE: For the sake of backward compatibility, the "-autoRun" argument
    #       must be first.
    #
    testClrExec $testExeFile [list -eventflags Wait -directory \
        [file dirname $testExeFile] -stdout output -success 0] -autoRun \
        -fileName [appendArgs \" [file nativename $fileName] \"]
  } error]










  tlog "---- BEGIN STDOUT OUTPUT\n"
  tlog $output
  tlog "\n---- END STDOUT OUTPUT\n"

  list $code [expr {$code == 0 ? "" : $error}]
} -cleanup {







>
>
>
>
>
>
>
>
>







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
    # NOTE: For the sake of backward compatibility, the "-autoRun" argument
    #       must be first.
    #
    testClrExec $testExeFile [list -eventflags Wait -directory \
        [file dirname $testExeFile] -stdout output -success 0] -autoRun \
        -fileName [appendArgs \" [file nativename $fileName] \"]
  } error]

  set successCount [regexp -all -- {\tSucceeded\t} $output]
  set failureCount [regexp -all -- {\tFailed\t} $output]
  set totalCount [expr {$successCount + $failureCount}]

  tputs $test_channel [appendArgs \
      "---- found and executed " $totalCount " unit tests from \"" \
      $testExeFile "\", " $successCount " passed, " $failureCount \
      " failed\n"]

  tlog "---- BEGIN STDOUT OUTPUT\n"
  tlog $output
  tlog "\n---- END STDOUT OUTPUT\n"

  list $code [expr {$code == 0 ? "" : $error}]
} -cleanup {