System.Data.SQLite
Check-in [12c508890c]
Not logged in

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

Overview
Comment:Add experimental support for interfacing with the authorizer callback mechanism in the SQLite core library.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 12c508890cd3486713263e359466636d3b5db892
User & Date: mistachkin 2013-09-18 02:10:34
Context
2013-09-18
02:31
Simplify and cleanup authorizer tests. Update version history docs. check-in: 441ccabcc6 user: mistachkin tags: trunk
02:10
Add experimental support for interfacing with the authorizer callback mechanism in the SQLite core library. check-in: 12c508890c user: mistachkin tags: trunk
2013-09-17
08:00
Add some simple tests of the new properties. check-in: 06c39db73a user: mistachkin tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to Setup/verify.lst.

403
404
405
406
407
408
409

410
411
412
413
414
415
416
  testlinq/Properties/
  testlinq/Properties/AssemblyInfo.cs
  testlinq/testlinq.2008.csproj
  testlinq/testlinq.2010.csproj
  testlinq/testlinq.2012.csproj
  Tests/
  Tests/all.eagle

  Tests/backup.eagle
  Tests/basic.eagle
  Tests/common.eagle
  Tests/empty.eagle
  Tests/installer.eagle
  Tests/Installer_Test_Vs2005.log
  Tests/Installer_Test_Vs2008.log







>







403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
  testlinq/Properties/
  testlinq/Properties/AssemblyInfo.cs
  testlinq/testlinq.2008.csproj
  testlinq/testlinq.2010.csproj
  testlinq/testlinq.2012.csproj
  Tests/
  Tests/all.eagle
  Tests/authorizer.eagle
  Tests/backup.eagle
  Tests/basic.eagle
  Tests/common.eagle
  Tests/empty.eagle
  Tests/installer.eagle
  Tests/Installer_Test_Vs2005.log
  Tests/Installer_Test_Vs2008.log

Changes to System.Data.SQLite/SQLite3.cs.

2041
2042
2043
2044
2045
2046
2047





2048
2049
2050
2051
2052
2053
2054

    internal override void ChangePassword(byte[] newPasswordBytes)
    {
      SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_rekey(_sql, newPasswordBytes, (newPasswordBytes == null) ? 0 : newPasswordBytes.Length);
      if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
    }
#endif






    internal override void SetUpdateHook(SQLiteUpdateCallback func)
    {
      UnsafeNativeMethods.sqlite3_update_hook(_sql, func, IntPtr.Zero);
    }

    internal override void SetCommitHook(SQLiteCommitCallback func)







>
>
>
>
>







2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059

    internal override void ChangePassword(byte[] newPasswordBytes)
    {
      SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_rekey(_sql, newPasswordBytes, (newPasswordBytes == null) ? 0 : newPasswordBytes.Length);
      if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
    }
#endif

    internal override void SetAuthorizerHook(SQLiteAuthorizerCallback func)
    {
      UnsafeNativeMethods.sqlite3_set_authorizer(_sql, func, IntPtr.Zero);
    }

    internal override void SetUpdateHook(SQLiteUpdateCallback func)
    {
      UnsafeNativeMethods.sqlite3_update_hook(_sql, func, IntPtr.Zero);
    }

    internal override void SetCommitHook(SQLiteCommitCallback func)

Changes to System.Data.SQLite/SQLiteBase.cs.

359
360
361
362
363
364
365

366
367
368
369
370
371
372
    internal abstract void LogMessage(SQLiteErrorCode iErrCode, string zMessage);

#if INTEROP_CODEC
    internal abstract void SetPassword(byte[] passwordBytes);
    internal abstract void ChangePassword(byte[] newPasswordBytes);
#endif


    internal abstract void SetUpdateHook(SQLiteUpdateCallback func);
    internal abstract void SetCommitHook(SQLiteCommitCallback func);
    internal abstract void SetTraceCallback(SQLiteTraceCallback func);
    internal abstract void SetRollbackHook(SQLiteRollbackCallback func);
    internal abstract SQLiteErrorCode SetLogCallback(SQLiteLogCallback func);

    /// <summary>







>







359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
    internal abstract void LogMessage(SQLiteErrorCode iErrCode, string zMessage);

#if INTEROP_CODEC
    internal abstract void SetPassword(byte[] passwordBytes);
    internal abstract void ChangePassword(byte[] newPasswordBytes);
#endif

    internal abstract void SetAuthorizerHook(SQLiteAuthorizerCallback func);
    internal abstract void SetUpdateHook(SQLiteUpdateCallback func);
    internal abstract void SetCommitHook(SQLiteCommitCallback func);
    internal abstract void SetTraceCallback(SQLiteTraceCallback func);
    internal abstract void SetRollbackHook(SQLiteRollbackCallback func);
    internal abstract SQLiteErrorCode SetLogCallback(SQLiteLogCallback func);

    /// <summary>

Changes to System.Data.SQLite/SQLiteConnection.cs.

474
475
476
477
478
479
480

481
482
483
484
485

486
487
488
489
490
491
492
....
2220
2221
2222
2223
2224
2225
2226



2227
2228
2229
2230
2231
2232
2233
....
3933
3934
3935
3936
3937
3938
3939


































3940
3941
3942
3943
3944
3945
3946
....
3963
3964
3965
3966
3967
3968
3969



















3970
3971
3972
3973
3974
3975
3976
....
4104
4105
4106
4107
4108
4109
4110












4111
4112
4113
4114
4115
4116
4117
....
4122
4123
4124
4125
4126
4127
4128









4129
4130
4131
4132
4133
4134
4135
....
4190
4191
4192
4193
4194
4195
4196





















































































































4197
4198
4199
4200
4201
4202
4203
    /// </summary>
    private bool _parseViaFramework;

    internal bool _binaryGuid;

    internal long _version;


    private event SQLiteUpdateEventHandler _updateHandler;
    private event SQLiteCommitHandler _commitHandler;
    private event SQLiteTraceEventHandler _traceHandler;
    private event EventHandler _rollbackHandler;


    private SQLiteUpdateCallback _updateCallback;
    private SQLiteCommitCallback _commitCallback;
    private SQLiteTraceCallback _traceCallback;
    private SQLiteRollbackCallback _rollbackCallback;
    #endregion

    ///////////////////////////////////////////////////////////////////////////////////////////////
................................................................................
                  {
                      cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA foreign_keys={0}", boolValue ? "ON" : "OFF");
                      cmd.ExecuteNonQuery();
                  }
              }
          }




          if (_commitHandler != null)
            _sql.SetCommitHook(_commitCallback);

          if (_updateHandler != null)
            _sql.SetUpdateHook(_updateCallback);

          if (_rollbackHandler != null)
................................................................................
      }

      tbl.EndLoadData();
      tbl.AcceptChanges();

      return tbl;
    }



































    /// <summary>
    /// This event is raised whenever SQLite makes an update/delete/insert into the database on
    /// this connection.  It only applies to the given connection.
    /// </summary>
    public event SQLiteUpdateEventHandler Update
    {
................................................................................
        if (_updateHandler == null)
        {
          if (_sql != null) _sql.SetUpdateHook(null);
          _updateCallback = null;
        }
      }
    }




















    private void UpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid)
    {
      _updateHandler(this, new UpdateEventArgs(
        SQLiteBase.UTF8ToString(database, -1),
        SQLiteBase.UTF8ToString(table, -1),
        (UpdateEventType)type,
................................................................................
    /// Use the default operating system's file flushing, SQLite does not explicitly flush the file buffers after writing
    /// </summary>
    Off = 2,
  }

#if !PLATFORM_COMPACTFRAMEWORK
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]












#endif
  internal delegate void SQLiteUpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid);

#if !PLATFORM_COMPACTFRAMEWORK
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
  internal delegate int SQLiteCommitCallback(IntPtr puser);
................................................................................
  internal delegate void SQLiteTraceCallback(IntPtr puser, IntPtr statement);

#if !PLATFORM_COMPACTFRAMEWORK
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
  internal delegate void SQLiteRollbackCallback(IntPtr puser);










  /// <summary>
  /// Raised when a transaction is about to be committed.  To roll back a transaction, set the
  /// rollbackTrans boolean value to true.
  /// </summary>
  /// <param name="sender">The connection committing the transaction</param>
  /// <param name="e">Event arguments on the transaction</param>
  public delegate void SQLiteCommitHandler(object sender, CommitEventArgs e);
................................................................................
    string destinationName,
    int pages,
    int remainingPages,
    int totalPages,
    bool retry
  );
  #endregion






















































































































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

  /// <summary>
  /// Whenever an update event is triggered on a connection, this enum will indicate
  /// exactly what type of operation is being performed.
  /// </summary>







>





>







 







>
>
>







 







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







 







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







 







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







 







>
>
>
>
>
>
>
>
>







 







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







474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
....
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
....
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
....
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
....
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
....
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
....
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
    /// </summary>
    private bool _parseViaFramework;

    internal bool _binaryGuid;

    internal long _version;

    private event SQLiteAuthorizerEventHandler _authorizerHandler;
    private event SQLiteUpdateEventHandler _updateHandler;
    private event SQLiteCommitHandler _commitHandler;
    private event SQLiteTraceEventHandler _traceHandler;
    private event EventHandler _rollbackHandler;

    private SQLiteAuthorizerCallback _authorizerCallback;
    private SQLiteUpdateCallback _updateCallback;
    private SQLiteCommitCallback _commitCallback;
    private SQLiteTraceCallback _traceCallback;
    private SQLiteRollbackCallback _rollbackCallback;
    #endregion

    ///////////////////////////////////////////////////////////////////////////////////////////////
................................................................................
                  {
                      cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA foreign_keys={0}", boolValue ? "ON" : "OFF");
                      cmd.ExecuteNonQuery();
                  }
              }
          }

          if (_authorizerHandler != null)
              _sql.SetAuthorizerHook(_authorizerCallback);

          if (_commitHandler != null)
            _sql.SetCommitHook(_commitCallback);

          if (_updateHandler != null)
            _sql.SetUpdateHook(_updateCallback);

          if (_rollbackHandler != null)
................................................................................
      }

      tbl.EndLoadData();
      tbl.AcceptChanges();

      return tbl;
    }

    /// <summary>
    /// This event is raised whenever SQLite encounters an action covered by the
    /// authorizer during query preparation.  Changing the value of the
    /// <see cref="AuthorizerEventArgs.ReturnCode" /> property will determine if
    /// the specific action will be allowed, ignored, or denied.  For the entire
    /// duration of the event, the associated connection and statement objects
    /// must not be modified, either directly or indirectly, by the called code.
    /// </summary>
    public event SQLiteAuthorizerEventHandler Authorize
    {
        add
        {
            CheckDisposed();

            if (_authorizerHandler == null)
            {
                _authorizerCallback = new SQLiteAuthorizerCallback(AuthorizerCallback);
                if (_sql != null) _sql.SetAuthorizerHook(_authorizerCallback);
            }
            _authorizerHandler += value;
        }
        remove
        {
            CheckDisposed();

            _authorizerHandler -= value;
            if (_authorizerHandler == null)
            {
                if (_sql != null) _sql.SetAuthorizerHook(null);
                _authorizerCallback = null;
            }
        }
    }

    /// <summary>
    /// This event is raised whenever SQLite makes an update/delete/insert into the database on
    /// this connection.  It only applies to the given connection.
    /// </summary>
    public event SQLiteUpdateEventHandler Update
    {
................................................................................
        if (_updateHandler == null)
        {
          if (_sql != null) _sql.SetUpdateHook(null);
          _updateCallback = null;
        }
      }
    }

    private SQLiteAuthorizerReturnCode AuthorizerCallback(
        IntPtr pUserData,
        SQLiteAuthorizerActionCode actionCode,
        IntPtr pArgument1,
        IntPtr pArgument2,
        IntPtr pDatabase,
        IntPtr pAuthContext)
    {
        AuthorizerEventArgs eventArgs = new AuthorizerEventArgs(pUserData, actionCode,
            SQLiteBase.UTF8ToString(pArgument1, -1), SQLiteBase.UTF8ToString(pArgument2, -1),
            SQLiteBase.UTF8ToString(pDatabase, -1), SQLiteBase.UTF8ToString(pAuthContext, -1),
            SQLiteAuthorizerReturnCode.Ok);

        if (_authorizerHandler != null)
            _authorizerHandler(this, eventArgs);

        return eventArgs.ReturnCode;
    }

    private void UpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid)
    {
      _updateHandler(this, new UpdateEventArgs(
        SQLiteBase.UTF8ToString(database, -1),
        SQLiteBase.UTF8ToString(table, -1),
        (UpdateEventType)type,
................................................................................
    /// Use the default operating system's file flushing, SQLite does not explicitly flush the file buffers after writing
    /// </summary>
    Off = 2,
  }

#if !PLATFORM_COMPACTFRAMEWORK
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
  internal delegate SQLiteAuthorizerReturnCode SQLiteAuthorizerCallback(
    IntPtr pUserData,
    SQLiteAuthorizerActionCode actionCode,
    IntPtr pArgument1,
    IntPtr pArgument2,
    IntPtr pDatabase,
    IntPtr pAuthContext
    );

#if !PLATFORM_COMPACTFRAMEWORK
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
  internal delegate void SQLiteUpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid);

#if !PLATFORM_COMPACTFRAMEWORK
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
  internal delegate int SQLiteCommitCallback(IntPtr puser);
................................................................................
  internal delegate void SQLiteTraceCallback(IntPtr puser, IntPtr statement);

#if !PLATFORM_COMPACTFRAMEWORK
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
  internal delegate void SQLiteRollbackCallback(IntPtr puser);

  /// <summary>
  /// Raised when authorization is required to perform an action contained
  /// within a SQL query.
  /// </summary>
  /// <param name="sender">The connection performing the action.</param>
  /// <param name="e">A <see cref="AuthorizerEventArgs" /> that contains the
  /// event data.</param>
  public delegate void SQLiteAuthorizerEventHandler(object sender, AuthorizerEventArgs e);

  /// <summary>
  /// Raised when a transaction is about to be committed.  To roll back a transaction, set the
  /// rollbackTrans boolean value to true.
  /// </summary>
  /// <param name="sender">The connection committing the transaction</param>
  /// <param name="e">Event arguments on the transaction</param>
  public delegate void SQLiteCommitHandler(object sender, CommitEventArgs e);
................................................................................
    string destinationName,
    int pages,
    int remainingPages,
    int totalPages,
    bool retry
  );
  #endregion

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

  /// <summary>
  /// The data associated with a call into the authorizer.
  /// </summary>
  public class AuthorizerEventArgs : EventArgs
  {
      /// <summary>
      /// The user-defined native data associated with this event.  Currently,
      /// this will always contain the value of <see cref="IntPtr.Zero" />.
      /// </summary>
      public readonly IntPtr UserData;

      /// <summary>
      /// The action code responsible for the current call into the authorizer.
      /// </summary>
      public readonly SQLiteAuthorizerActionCode ActionCode;

      /// <summary>
      /// The first string argument for the current call into the authorizer.
      /// The exact value will vary based on the action code, see the
      /// <see cref="SQLiteAuthorizerActionCode" /> enumeration for possible
      /// values.
      /// </summary>
      public readonly string Argument1;

      /// <summary>
      /// The second string argument for the current call into the authorizer.
      /// The exact value will vary based on the action code, see the
      /// <see cref="SQLiteAuthorizerActionCode" /> enumeration for possible
      /// values.
      /// </summary>
      public readonly string Argument2;

      /// <summary>
      /// The database name for the current call into the authorizer, if
      /// applicable.
      /// </summary>
      public readonly string Database;

      /// <summary>
      /// The name of the inner-most trigger or view that is responsible for
      /// the access attempt or a null value if this access attempt is directly
      /// from top-level SQL code.
      /// </summary>
      public readonly string Context;

      /// <summary>
      /// The return code for the current call into the authorizer.
      /// </summary>
      public SQLiteAuthorizerReturnCode ReturnCode;

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

      /// <summary>
      /// Constructs an instance of this class with default property values.
      /// </summary>
      private AuthorizerEventArgs()
      {
          this.UserData = IntPtr.Zero;
          this.ActionCode = SQLiteAuthorizerActionCode.None;
          this.Argument1 = null;
          this.Argument2 = null;
          this.Database = null;
          this.Context = null;
          this.ReturnCode = SQLiteAuthorizerReturnCode.Ok;
      }

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

      /// <summary>
      /// Constructs an instance of this class with specific property values.
      /// </summary>
      /// <param name="pUserData">
      /// The user-defined native data associated with this event.
      /// </param>
      /// <param name="actionCode">
      /// The authorizer action code.
      /// </param>
      /// <param name="argument1">
      /// The first authorizer argument.
      /// </param>
      /// <param name="argument2">
      /// The second authorizer argument.
      /// </param>
      /// <param name="database">
      /// The database name, if applicable.
      /// </param>
      /// <param name="context">
      /// The name of the inner-most trigger or view that is responsible for
      /// the access attempt or a null value if this access attempt is directly
      /// from top-level SQL code.
      /// </param>
      /// <param name="returnCode">
      /// The authorizer return code.
      /// </param>
      internal AuthorizerEventArgs(
          IntPtr pUserData,
          SQLiteAuthorizerActionCode actionCode,
          string argument1,
          string argument2,
          string database,
          string context,
          SQLiteAuthorizerReturnCode returnCode
          )
          : this()
      {
          this.UserData = pUserData;
          this.ActionCode = actionCode;
          this.Argument1 = argument1;
          this.Argument2 = argument2;
          this.Database = database;
          this.Context = context;
          this.ReturnCode = returnCode;
      }
  }

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

  /// <summary>
  /// Whenever an update event is triggered on a connection, this enum will indicate
  /// exactly what type of operation is being performed.
  /// </summary>

Changes to System.Data.SQLite/SQLiteConvert.cs.

1498
1499
1500
1501
1502
1503
1504













































































































































































































































1505
1506
1507
1508
1509
1510
1511

      /// <summary>
      /// Use the default command execution type.  Using this value is the same
      /// as using the <see cref="SQLiteExecuteType.NonQuery" /> value.
      /// </summary>
      Default = NonQuery /* TODO: Good default? */
  }














































































































































































































































  /// <summary>
  /// Class used internally to determine the datatype of a column in a resultset
  /// </summary>
  internal sealed class SQLiteType
  {
    /// <summary>







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







1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
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
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748

      /// <summary>
      /// Use the default command execution type.  Using this value is the same
      /// as using the <see cref="SQLiteExecuteType.NonQuery" /> value.
      /// </summary>
      Default = NonQuery /* TODO: Good default? */
  }

  /// <summary>
  /// The action code responsible for the current call into the authorizer.
  /// </summary>
  public enum SQLiteAuthorizerActionCode
  {
      /// <summary>
      /// No action is being performed.  This value should not be used from
      /// external code.
      /// </summary>
      None = -1,

      /// <summary>
      /// No longer used.
      /// </summary>
      Copy = 0,

      /// <summary>
      /// An index will be created.  The action-specific arguments are the
      /// index name and the table name.
      /// 
      /// </summary>
      CreateIndex = 1,

      /// <summary>
      /// A table will be created.  The action-specific arguments are the
      /// table name and a null value.
      /// </summary>
      CreateTable = 2,

      /// <summary>
      /// A temporary index will be created.  The action-specific arguments
      /// are the index name and the table name.
      /// </summary>
      CreateTempIndex = 3,

      /// <summary>
      /// A temporary table will be created.  The action-specific arguments
      /// are the table name and a null value.
      /// </summary>
      CreateTempTable = 4,

      /// <summary>
      /// A temporary trigger will be created.  The action-specific arguments
      /// are the trigger name and the table name.
      /// </summary>
      CreateTempTrigger = 5,

      /// <summary>
      /// A temporary view will be created.  The action-specific arguments are
      /// the view name and a null value.
      /// </summary>
      CreateTempView = 6,

      /// <summary>
      /// A trigger will be created.  The action-specific arguments are the
      /// trigger name and the table name.
      /// </summary>
      CreateTrigger = 7,

      /// <summary>
      /// A view will be created.  The action-specific arguments are the view
      /// name and a null value.
      /// </summary>
      CreateView = 8,

      /// <summary>
      /// A DELETE statement will be executed.  The action-specific arguments
      /// are the table name and a null value.
      /// </summary>
      Delete = 9,

      /// <summary>
      /// An index will be dropped.  The action-specific arguments are the
      /// index name and the table name.
      /// </summary>
      DropIndex = 10,

      /// <summary>
      /// A table will be dropped.  The action-specific arguments are the tables
      /// name and a null value.
      /// </summary>
      DropTable = 11,

      /// <summary>
      /// A temporary index will be dropped.  The action-specific arguments are
      /// the index name and the table name.
      /// </summary>
      DropTempIndex = 12,

      /// <summary>
      /// A temporary table will be dropped.  The action-specific arguments are
      /// the table name and a null value.
      /// </summary>
      DropTempTable = 13,

      /// <summary>
      /// A temporary trigger will be dropped.  The action-specific arguments
      /// are the trigger name and the table name.
      /// </summary>
      DropTempTrigger = 14,

      /// <summary>
      /// A temporary view will be dropped.  The action-specific arguments are
      /// the view name and a null value.
      /// </summary>
      DropTempView = 15,

      /// <summary>
      /// A trigger will be dropped.  The action-specific arguments are the
      /// trigger name and the table name.
      /// </summary>
      DropTrigger = 16,

      /// <summary>
      /// A view will be dropped.  The action-specific arguments are the view
      /// name and a null value.
      /// </summary>
      DropView = 17,

      /// <summary>
      /// An INSERT statement will be executed.  The action-specific arguments
      /// are the table name and a null value.
      /// </summary>
      Insert = 18,

      /// <summary>
      /// A PRAGMA statement will be executed.  The action-specific arguments
      /// are the name of the PRAGMA and the new value or a null value.
      /// </summary>
      Pragma = 19,

      /// <summary>
      /// A table column will be read.  The action-specific arguments are the
      /// table name and the column name.
      /// </summary>
      Read = 20,

      /// <summary>
      /// A SELECT statement will be executed.  The action-specific arguments
      /// are both null values.
      /// </summary>
      Select = 21,

      /// <summary>
      /// A transaction will be started, committed, or rolled back.  The
      /// action-specific arguments are the name of the operation (BEGIN,
      /// COMMIT, or ROLLBACK) and a null value.
      /// </summary>
      Transaction = 22,

      /// <summary>
      /// An UPDATE statement will be executed.  The action-specific arguments
      /// are the table name and the column name.
      /// </summary>
      Update = 23,

      /// <summary>
      /// A database will be attached to the connection.  The action-specific
      /// arguments are the database file name and a null value.
      /// </summary>
      Attach = 24,

      /// <summary>
      /// A database will be detached from the connection.  The action-specific
      /// arguments are the database name and a null value.
      /// </summary>
      Detach = 25,

      /// <summary>
      /// The schema of a table will be altered.  The action-specific arguments
      /// are the database name and the table name.
      /// </summary>
      AlterTable = 26,

      /// <summary>
      /// An index will be deleted and then recreated.  The action-specific
      /// arguments are the index name and a null value.
      /// </summary>
      Reindex = 27,

      /// <summary>
      /// A table will be analyzed to gathers statistics about it.  The
      /// action-specific arguments are the table name and a null value.
      /// </summary>
      Analyze = 28,

      /// <summary>
      /// A virtual table will be created.  The action-specific arguments are
      /// the table name and the module name.
      /// </summary>
      CreateVtable = 29,

      /// <summary>
      /// A virtual table will be dropped.  The action-specific arguments are
      /// the table name and the module name.
      /// </summary>
      DropVtable = 30,

      /// <summary>
      /// A SQL function will be called.  The action-specific arguments are a
      /// null value and the function name.
      /// </summary>
      Function = 31,

      /// <summary>
      /// A savepoint will be created, released, or rolled back.  The
      /// action-specific arguments are the name of the operation (BEGIN,
      /// RELEASE, or ROLLBACK) and the savepoint name.
      /// </summary>
      Savepoint = 32
  }

  /// <summary>
  /// The return code for the current call into the authorizer.
  /// </summary>
  public enum SQLiteAuthorizerReturnCode
  {
      /// <summary>
      /// The action will be allowed.
      /// </summary>
      Ok = 0,

      /// <summary>
      /// The overall action will be disallowed and an error message will be
      /// returned from the query preparation method.
      /// </summary>
      Deny = 1,

      /// <summary>
      /// The specific action will be disallowed; however, the overall action
      /// will continue.  The exact effects of this return code vary depending
      /// on the specific action, please refer to the SQLite core library
      /// documentation for futher details.
      /// </summary>
      Ignore = 2
  }

  /// <summary>
  /// Class used internally to determine the datatype of a column in a resultset
  /// </summary>
  internal sealed class SQLiteType
  {
    /// <summary>

Changes to System.Data.SQLite/UnsafeNativeMethods.cs.

1465
1466
1467
1468
1469
1470
1471







1472
1473
1474
1475
1476
1477
1478
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern SQLiteErrorCode sqlite3_rekey(IntPtr db, byte[] key, int keylen);
#endif








#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern IntPtr sqlite3_update_hook(IntPtr db, SQLiteUpdateCallback func, IntPtr pvUser);








>
>
>
>
>
>
>







1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern SQLiteErrorCode sqlite3_rekey(IntPtr db, byte[] key, int keylen);
#endif

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern IntPtr sqlite3_set_authorizer(IntPtr db, SQLiteAuthorizerCallback func, IntPtr pvUser);

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern IntPtr sqlite3_update_hook(IntPtr db, SQLiteUpdateCallback func, IntPtr pvUser);

Added Tests/authorizer.eagle.

































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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
155
156
157
158
159
160
###############################################################################
#
# authorizer.eagle --
#
# Written by Joe Mistachkin.
# Released to the public domain, use at your own risk!
#
###############################################################################

package require Eagle
package require Eagle.Library
package require Eagle.Test

runTestPrologue

###############################################################################

package require System.Data.SQLite.Test
runSQLiteTestPrologue

###############################################################################

runTest {test authorizer-1.1 {SQLiteConnection Authorize event} -setup {
  proc onAuthorize { sender e } {
    #
    # NOTE: Filter out the "noise" by allowing all standard
    #       events on the "sqlite_*" tables.
    #
    set noiseActionCodes [list \
        CreateTable CreateIndex Read Insert Update Delete]

    if {[$e ActionCode] in $noiseActionCodes && \
        [string match "sqlite_*" [$e Argument1]]} then {
      return
    }

    lappend ::data [list \
        [$e UserData] [$e ActionCode] [$e Argument1] \
        [$e Argument2] [$e Database] [$e Context]]

    if {[$e ActionCode] eq "CreateTable" && \
        [$e Argument1] eq "tDeny"} then {
      $e ReturnCode Deny
    }
  }

  setupDb [set fileName authorizer-1.1.db]
} -body {
  set connection [getDbConnection]

  set callback onAuthorize
  object invoke $connection add_Authorize $callback

  set results [list]

  set sql [list \
    CreateTable       {CREATE TABLE t1(x);} \
    CreateIndex       {CREATE INDEX i1 ON t1(x);} \
    CreateTrigger     {CREATE TRIGGER tr1 BEFORE INSERT ON t1
                       BEGIN
                         SELECT RAISE(IGNORE);
                       END;} \
    CreateView        {CREATE VIEW v1 AS SELECT * FROM t1;} \
    CreateTempTable   {CREATE TEMPORARY TABLE t2(x);} \
    CreateTempIndex   {CREATE INDEX i2 ON t2(x);} \
    CreateTempTrigger {CREATE TEMPORARY TRIGGER tr2 BEFORE INSERT ON t2
                       BEGIN
                         SELECT RAISE(IGNORE);
                       END;} \
    CreateTempView    {CREATE TEMPORARY VIEW v2 AS SELECT * FROM t2;} \
    Pragma            {PRAGMA journal_mode=WAL;} \
    Function          {SELECT julianday('now');} \
    Read              {SELECT x FROM t1;} \
    Select            {SELECT * FROM t1;} \
    Insert            {INSERT INTO t1(x) VALUES(1);} \
    Update            {UPDATE t1 SET x = x - 1;} \
    Delete            {DELETE FROM t1;} \
    AlterTable        {ALTER TABLE t1 ADD COLUMN y;} \
    Reindex           {REINDEX t1;} \
    Analyze           {ANALYZE t1;} \
    DropTempView      {DROP VIEW v2;} \
    DropTempTrigger   {DROP TRIGGER tr2;} \
    DropTempIndex     {DROP INDEX i2;} \
    DropTempTable     {DROP TABLE t2;} \
    DropView          {DROP VIEW v1;} \
    DropTrigger       {DROP TRIGGER tr1;} \
    DropIndex         {DROP INDEX i1;} \
    DropTable         {DROP TABLE t1;} \
    Transaction       {BEGIN; SELECT 0; COMMIT;} \
    Savepoint         {SAVEPOINT s1; RELEASE SAVEPOINT s1;} \
    Attach            {ATTACH DATABASE ':memory:' AS d1;} \
    Detach            {DETACH DATABASE d1;} \
    CreateVtable      {CREATE VIRTUAL TABLE t3 USING fts4(x TEXT);} \
    DropVtable        {DROP TABLE t3;} \
    CreateTable       {CREATE TABLE tDeny(x);}]

  foreach {name value} $sql {
    set data [list]; set code [catch {sql execute $db $value} result]
    set result [lindex [split [string map [list \r\n \n] $result] \n] 0]
    lappend results [list $name $data $code $result]
  }
  lappend results [isTableInDb tDeny]

  set results
} -cleanup {
  catch {object invoke $connection remove_Authorize $callback}
  catch {object removecallback $callback}

  cleanupDb $fileName

  freeDbConnection

  unset -nocomplain data result code value name sql results callback \
      connection db fileName

  rename onAuthorize ""
} -constraints \
{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \
{{CreateTable {{0 CreateTable t1 {} main {}}} 0 0} {CreateIndex {{0 CreateIndex\
i1 t1 main {}} {0 Reindex i1 {} main {}}} 0 0} {CreateTrigger {{0 CreateTrigger\
tr1 t1 main {}}} 0 0} {CreateView {{0 CreateView v1 {} main {}}} 0 0}\
{CreateTempTable {{0 CreateTempTable t2 {} temp {}}} 0 0} {CreateTempIndex {{0\
CreateTempIndex i2 t2 temp {}} {0 Reindex i2 {} temp {}}} 0 0}\
{CreateTempTrigger {{0 CreateTempTrigger tr2 t2 temp {}}} 0 0} {CreateTempView\
{{0 CreateTempView v2 {} temp {}}} 0 0} {Pragma {{0 Pragma journal_mode WAL {}\
{}}} 0 0} {Function {{0 Select {} {} {} {}} {0 Function {} julianday {} {}}} 0\
0} {Read {{0 Select {} {} {} {}} {0 Read t1 x main {}}} 0 0} {Select {{0 Select\
{} {} {} {}} {0 Read t1 x main {}}} 0 0} {Insert {{0 Insert t1 {} main {}} {0\
Select {} {} {} tr1}} 0 0} {Update {{0 Read t1 x main {}} {0 Update t1 x main\
{}}} 0 0} {Delete {{0 Delete t1 {} main {}}} 0 0} {AlterTable {{0 AlterTable\
main t1 {} {}} {0 Function {} substr {} {}} {0 Function {} substr {} {}}} 0 0}\
{Reindex {{0 Reindex i1 {} main {}}} 0 0} {Analyze {{0 Analyze t1 {} main {}}\
{0 Select {} {} {} {}} {0 Select {} {} {} {}} {0 Function {} count {} {}} {0\
Select {} {} {} {}}} 0 0} {DropTempView {{0 DropTempView v2 {} temp {}} {0\
Delete v2 {} temp {}}} 0 0} {DropTempTrigger {{0 DropTempTrigger tr2 t2 temp\
{}}} 0 0} {DropTempIndex {{0 DropTempIndex i2 t2 temp {}}} 0 0} {DropTempTable\
{{0 DropTempTable t2 {} temp {}} {0 Delete t2 {} temp {}}} 0 0} {DropView {{0\
DropView v1 {} main {}} {0 Delete v1 {} main {}}} 0 0} {DropTrigger {{0\
DropTrigger tr1 t1 main {}}} 0 0} {DropIndex {{0 DropIndex i1 t1 main {}}} 0 0}\
{DropTable {{0 DropTable t1 {} main {}} {0 Delete t1 {} main {}}} 0 0}\
{Transaction {{0 Transaction BEGIN {} {} {}} {0 Select {} {} {} {}} {0\
Transaction COMMIT {} {} {}}} 0 0} {Savepoint {{0 Savepoint BEGIN s1 {} {}} {0\
Savepoint RELEASE s1 {} {}}} 0 0} {Attach {{0 Attach :memory: {} {} {}}} 0 0}\
{Detach {{0 Detach d1 {} {} {}}} 0 0} {CreateVtable {{0 CreateVtable t3 fts4\
main {}} {0 CreateTable t3_content {} main {}} {0 CreateTable t3_segments {}\
main {}} {0 CreateTable t3_segdir {} main {}} {0 CreateTable t3_docsize {} main\
{}} {0 CreateTable t3_stat {} main {}} {0 Pragma page_size {} main {}}} 0 0}\
{DropVtable {{0 DropVtable t3 fts4 main {}} {0 Delete t3 {} main {}} {0\
DropTable t3_content {} main {}} {0 Delete t3_content {} main {}} {0 DropTable\
t3_segments {} main {}} {0 Delete t3_segments {} main {}} {0 DropTable\
t3_segdir {} main {}} {0 Delete t3_segdir {} main {}} {0 DropTable t3_docsize\
{} main {}} {0 Delete t3_docsize {} main {}} {0 DropTable t3_stat {} main {}}\
{0 Delete t3_stat {} main {}}} 0 0} {CreateTable {{0 CreateTable tDeny {} main\
{}}} 1 {System.Data.SQLite.SQLiteException (0x80004005): authorization denied}}\
False}}

###############################################################################

runSQLiteTestEpilogue
runTestEpilogue

Changes to Tests/common.eagle.

995
996
997
998
999
1000
1001










1002
1003
1004
1005
1006
1007
1008
      # NOTE: Execute the SQL query against the sqlite_master table to check if
      #       the named table is present and return non-zero if it is.
      #
      return [expr {[sql execute -execute scalar $db \
          "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = ?;" \
          [list param1 String $name]] > 0}]
    }










 
    proc executeSql { sql {execute none} {fileName ""} } {
      if {[string length $fileName] == 0} then {set fileName :memory:}
      setupDb $fileName "" "" "" "" "" false false false false memDb

      try {
        return [sql execute -execute $execute $memDb $sql]







>
>
>
>
>
>
>
>
>
>







995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
      # NOTE: Execute the SQL query against the sqlite_master table to check if
      #       the named table is present and return non-zero if it is.
      #
      return [expr {[sql execute -execute scalar $db \
          "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = ?;" \
          [list param1 String $name]] > 0}]
    }
 
    proc trimSql { sql } {
      set result [string map [list \r\n " " \r " " \n " "] $sql]

      while {[string first "  " $result] != -1} {
        set result [string map [list "  " " "] $result]
      }

      return $result
    }
 
    proc executeSql { sql {execute none} {fileName ""} } {
      if {[string length $fileName] == 0} then {set fileName :memory:}
      setupDb $fileName "" "" "" "" "" false false false false memDb

      try {
        return [sql execute -execute $execute $memDb $sql]