System.Data.SQLite
Check-in [aafb14f219]
Not logged in

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

Overview
Comment:Object lifetime fixes for stream adapters and their managed delegates.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: aafb14f219e1c971ef864a8a8ebba286fb8ac632
User & Date: mistachkin 2017-10-13 17:47:50
Context
2017-10-13
18:26
Add tests for patch sets. check-in: 4490b24bbb user: mistachkin tags: sessions
17:47
Object lifetime fixes for stream adapters and their managed delegates. check-in: aafb14f219 user: mistachkin tags: sessions
17:46
Corrections to tests. Closed-Leaf check-in: acbeea6018 user: mistachkin tags: sessionStreamManager
2017-10-12
20:04
Add stream tests for the CombineWith and Apply methods. check-in: cce9d5de83 user: mistachkin tags: sessions
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

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

    11     11   #if DEBUG
    12     12   using System.Diagnostics;
    13     13   #endif
    14     14   
    15     15   using System.IO;
    16     16   using System.Globalization;
    17     17   using System.Runtime.InteropServices;
    18         -using System.Threading;
    19     18   
    20     19   namespace System.Data.SQLite
    21     20   {
    22     21       #region Session Extension Enumerations
    23     22       public enum SQLiteChangeSetConflictType
    24     23       {
    25     24           Data = 1,
................................................................................
   706    705   
   707    706       ///////////////////////////////////////////////////////////////////////////
   708    707   
   709    708       #region SQLiteStreamChangeSetIterator Class
   710    709       internal sealed class SQLiteStreamChangeSetIterator :
   711    710           SQLiteChangeSetIterator
   712    711       {
          712  +        #region Private Data
          713  +        private SQLiteStreamAdapter streamAdapter;
          714  +        #endregion
          715  +
          716  +        ///////////////////////////////////////////////////////////////////////
          717  +
   713    718           #region Private Constructors
   714    719           private SQLiteStreamChangeSetIterator(
          720  +            SQLiteStreamAdapter streamAdapter,
   715    721               IntPtr iterator,
   716    722               bool ownHandle
   717    723               )
   718    724               : base(iterator, ownHandle)
   719    725           {
   720         -            // do nothing.
          726  +            this.streamAdapter = streamAdapter;
   721    727           }
   722    728           #endregion
   723    729   
   724    730           ///////////////////////////////////////////////////////////////////////
   725    731   
   726    732           #region Static "Factory" Methods
   727    733           public static SQLiteStreamChangeSetIterator Create(
................................................................................
   728    734               Stream stream,
   729    735               SQLiteConnectionFlags flags
   730    736               )
   731    737           {
   732    738               if (stream == null)
   733    739                   throw new ArgumentNullException("stream");
   734    740   
          741  +            SQLiteStreamAdapter streamAdapter = null;
   735    742               SQLiteStreamChangeSetIterator result = null;
   736    743               IntPtr iterator = IntPtr.Zero;
   737    744   
   738    745               try
   739    746               {
          747  +                streamAdapter = new SQLiteStreamAdapter(stream, flags);
          748  +
   740    749                   SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_start_strm(
   741         -                    ref iterator, new SQLiteStreamAdapter(stream, flags).xInput,
   742         -                    IntPtr.Zero);
          750  +                    ref iterator, streamAdapter.GetInputDelegate(), IntPtr.Zero);
   743    751   
   744    752                   if (rc != SQLiteErrorCode.Ok)
   745         -                    throw new SQLiteException(rc, "sqlite3changeset_start_strm");
          753  +                {
          754  +                    throw new SQLiteException(
          755  +                        rc, "sqlite3changeset_start_strm");
          756  +                }
   746    757   
   747         -                result = new SQLiteStreamChangeSetIterator(iterator, true);
          758  +                result = new SQLiteStreamChangeSetIterator(
          759  +                    streamAdapter, iterator, true);
   748    760               }
   749    761               finally
   750    762               {
   751    763                   if (result == null)
   752    764                   {
   753    765                       if (iterator != IntPtr.Zero)
   754    766                       {
   755    767                           UnsafeNativeMethods.sqlite3changeset_finalize(
   756    768                               iterator);
   757    769   
   758    770                           iterator = IntPtr.Zero;
   759    771                       }
          772  +
          773  +                    if (streamAdapter != null)
          774  +                    {
          775  +                        streamAdapter.Dispose();
          776  +                        streamAdapter = null;
          777  +                    }
   760    778                   }
   761    779               }
   762    780   
   763    781               return result;
   764    782           }
   765    783           #endregion
   766    784   
................................................................................
   817    835   
   818    836       #region SQLiteStreamAdapter Class
   819    837       internal sealed class SQLiteStreamAdapter : IDisposable
   820    838       {
   821    839           #region Private Data
   822    840           private Stream stream; /* EXEMPT: NOT OWNED */
   823    841           private SQLiteConnectionFlags flags;
          842  +
          843  +        ///////////////////////////////////////////////////////////////////////
          844  +
          845  +        private UnsafeNativeMethods.xSessionInput xInput;
          846  +        private UnsafeNativeMethods.xSessionOutput xOutput;
   824    847           #endregion
   825    848   
   826    849           ///////////////////////////////////////////////////////////////////////
   827    850   
   828    851           #region Public Constructors
   829    852           public SQLiteStreamAdapter(
   830    853               Stream stream,
................................................................................
   843    866           {
   844    867               return flags;
   845    868           }
   846    869           #endregion
   847    870   
   848    871           ///////////////////////////////////////////////////////////////////////
   849    872   
          873  +        #region Public Methods
          874  +        public UnsafeNativeMethods.xSessionInput GetInputDelegate()
          875  +        {
          876  +            CheckDisposed();
          877  +
          878  +            if (xInput == null)
          879  +                xInput = new UnsafeNativeMethods.xSessionInput(Input);
          880  +
          881  +            return xInput;
          882  +        }
          883  +
          884  +        ///////////////////////////////////////////////////////////////////////
          885  +
          886  +        public UnsafeNativeMethods.xSessionOutput GetOutputDelegate()
          887  +        {
          888  +            CheckDisposed();
          889  +
          890  +            if (xOutput == null)
          891  +                xOutput = new UnsafeNativeMethods.xSessionOutput(Output);
          892  +
          893  +            return xOutput;
          894  +        }
          895  +        #endregion
          896  +
          897  +        ///////////////////////////////////////////////////////////////////////
          898  +
   850    899           #region Native Callback Methods
   851         -        internal SQLiteErrorCode xInput(
          900  +        private SQLiteErrorCode Input(
   852    901               IntPtr context,
   853    902               IntPtr pData,
   854    903               ref int nData
   855    904               )
   856    905           {
   857    906               try
   858    907               {
   859         -                Stream localStream = Interlocked.CompareExchange(
   860         -                    ref stream, null, null);
          908  +                Stream localStream = stream;
   861    909   
   862    910                   if (localStream == null)
   863    911                       return SQLiteErrorCode.Misuse;
   864    912   
   865    913                   if (nData > 0)
   866    914                   {
   867    915                       byte[] bytes = new byte[nData];
          916  +                    int nRead = localStream.Read(bytes, 0, nData);
   868    917   
   869         -                    nData = localStream.Read(bytes, 0, nData);
          918  +                    if ((nRead > 0) && (pData != IntPtr.Zero))
          919  +                        Marshal.Copy(bytes, 0, pData, nRead);
   870    920   
   871         -                    if (nData > 0)
   872         -                        Marshal.Copy(bytes, 0, pData, nData);
          921  +                    nData = nRead;
   873    922                   }
   874    923   
   875    924                   return SQLiteErrorCode.Ok;
   876    925               }
   877    926               catch (Exception e)
   878    927               {
   879    928                   try
................................................................................
   881    930                       if (HelperMethods.LogCallbackExceptions(GetFlags()))
   882    931                       {
   883    932                           SQLiteLog.LogMessage(
   884    933                               SQLiteBase.COR_E_EXCEPTION,
   885    934                               HelperMethods.StringFormat(
   886    935                               CultureInfo.CurrentCulture,
   887    936                               UnsafeNativeMethods.ExceptionMessageFormat,
   888         -                            "xInput", e)); /* throw */
          937  +                            "xSessionInput", e)); /* throw */
   889    938                       }
   890    939                   }
   891    940                   catch
   892    941                   {
   893    942                       // do nothing.
   894    943                   }
   895    944               }
   896    945   
   897    946               return SQLiteErrorCode.IoErr_Read;
   898    947           }
   899    948   
   900    949           ///////////////////////////////////////////////////////////////////////
   901    950   
   902         -        internal SQLiteErrorCode xOutput(
          951  +        private SQLiteErrorCode Output(
   903    952               IntPtr context,
   904    953               IntPtr pData,
   905    954               int nData
   906    955               )
   907    956           {
   908    957               try
   909    958               {
   910         -                Stream localStream = Interlocked.CompareExchange(
   911         -                    ref stream, null, null);
          959  +                Stream localStream = stream;
   912    960   
   913    961                   if (localStream == null)
   914    962                       return SQLiteErrorCode.Misuse;
   915    963   
   916    964                   if (nData > 0)
   917    965                   {
   918    966                       byte[] bytes = new byte[nData];
   919    967   
   920         -                    Marshal.Copy(pData, bytes, 0, nData);
          968  +                    if (pData != IntPtr.Zero)
          969  +                        Marshal.Copy(pData, bytes, 0, nData);
          970  +
   921    971                       localStream.Write(bytes, 0, nData);
   922    972                   }
   923    973   
   924    974                   localStream.Flush();
   925    975   
   926    976                   return SQLiteErrorCode.Ok;
   927    977               }
................................................................................
   932    982                       if (HelperMethods.LogCallbackExceptions(GetFlags()))
   933    983                       {
   934    984                           SQLiteLog.LogMessage(
   935    985                               SQLiteBase.COR_E_EXCEPTION,
   936    986                               HelperMethods.StringFormat(
   937    987                               CultureInfo.CurrentCulture,
   938    988                               UnsafeNativeMethods.ExceptionMessageFormat,
   939         -                            "xOutput", e)); /* throw */
          989  +                            "xSessionOutput", e)); /* throw */
   940    990                       }
   941    991                   }
   942    992                   catch
   943    993                   {
   944    994                       // do nothing.
   945    995                   }
   946    996               }
................................................................................
   976   1026   
   977   1027           ///////////////////////////////////////////////////////////////////////
   978   1028   
   979   1029           private /* protected virtual */ void Dispose(bool disposing)
   980   1030           {
   981   1031               try
   982   1032               {
   983         -                //if (!disposed)
   984         -                //{
   985         -                //    if (disposing)
   986         -                //    {
   987         -                //        ////////////////////////////////////
   988         -                //        // dispose managed resources here...
   989         -                //        ////////////////////////////////////
   990         -                //    }
         1033  +                if (!disposed)
         1034  +                {
         1035  +                    if (disposing)
         1036  +                    {
         1037  +                        ////////////////////////////////////
         1038  +                        // dispose managed resources here...
         1039  +                        ////////////////////////////////////
   991   1040   
   992         -                //    //////////////////////////////////////
   993         -                //    // release unmanaged resources here...
   994         -                //    //////////////////////////////////////
   995         -                //}
         1041  +                        if (xInput != null)
         1042  +                            xInput = null;
         1043  +
         1044  +                        if (xOutput != null)
         1045  +                            xOutput = null;
         1046  +
         1047  +                        if (stream != null)
         1048  +                            stream = null; /* NOT OWNED */
         1049  +                    }
         1050  +
         1051  +                    //////////////////////////////////////
         1052  +                    // release unmanaged resources here...
         1053  +                    //////////////////////////////////////
         1054  +                }
   996   1055               }
   997   1056               finally
   998   1057               {
   999   1058                   //
  1000   1059                   // NOTE: Everything should be fully disposed at this point.
  1001   1060                   //
  1002   1061                   disposed = true;
................................................................................
  1013   1072           }
  1014   1073           #endregion
  1015   1074       }
  1016   1075       #endregion
  1017   1076   
  1018   1077       ///////////////////////////////////////////////////////////////////////////
  1019   1078   
         1079  +    #region SQLiteSessionStreamManager Class
         1080  +    internal sealed class SQLiteSessionStreamManager : IDisposable
         1081  +    {
         1082  +        #region Private Data
         1083  +        private Dictionary<Stream, SQLiteStreamAdapter> streamAdapters;
         1084  +        private SQLiteConnectionFlags flags;
         1085  +        #endregion
         1086  +
         1087  +        ///////////////////////////////////////////////////////////////////////
         1088  +
         1089  +        #region Public Constructors
         1090  +        public SQLiteSessionStreamManager(
         1091  +            SQLiteConnectionFlags flags
         1092  +            )
         1093  +        {
         1094  +            this.flags = flags;
         1095  +
         1096  +            InitializeStreamAdapters();
         1097  +        }
         1098  +        #endregion
         1099  +
         1100  +        ///////////////////////////////////////////////////////////////////////
         1101  +
         1102  +        #region Private Methods
         1103  +        private void InitializeStreamAdapters()
         1104  +        {
         1105  +            if (streamAdapters != null)
         1106  +                return;
         1107  +
         1108  +            streamAdapters = new Dictionary<Stream, SQLiteStreamAdapter>();
         1109  +        }
         1110  +
         1111  +        ///////////////////////////////////////////////////////////////////////
         1112  +
         1113  +        private void DisposeStreamAdapters()
         1114  +        {
         1115  +            if (streamAdapters == null)
         1116  +                return;
         1117  +
         1118  +            foreach (KeyValuePair<Stream, SQLiteStreamAdapter> pair
         1119  +                    in streamAdapters)
         1120  +            {
         1121  +                SQLiteStreamAdapter streamAdapter = pair.Value;
         1122  +
         1123  +                if (streamAdapter == null)
         1124  +                    continue;
         1125  +
         1126  +                streamAdapter.Dispose();
         1127  +            }
         1128  +
         1129  +            streamAdapters.Clear();
         1130  +            streamAdapters = null;
         1131  +        }
         1132  +        #endregion
         1133  +
         1134  +        ///////////////////////////////////////////////////////////////////////
         1135  +
         1136  +        #region Public Methods
         1137  +        public SQLiteStreamAdapter GetAdapter(
         1138  +            Stream stream
         1139  +            )
         1140  +        {
         1141  +            CheckDisposed();
         1142  +
         1143  +            if (stream == null)
         1144  +                return null;
         1145  +
         1146  +            SQLiteStreamAdapter streamAdapter;
         1147  +
         1148  +            if (streamAdapters.TryGetValue(stream, out streamAdapter))
         1149  +                return streamAdapter;
         1150  +
         1151  +            streamAdapter = new SQLiteStreamAdapter(stream, flags);
         1152  +            streamAdapters.Add(stream, streamAdapter);
         1153  +
         1154  +            return streamAdapter;
         1155  +        }
         1156  +        #endregion
         1157  +
         1158  +        ///////////////////////////////////////////////////////////////////////
         1159  +
         1160  +        #region IDisposable Members
         1161  +        public void Dispose()
         1162  +        {
         1163  +            Dispose(true);
         1164  +            GC.SuppressFinalize(this);
         1165  +        }
         1166  +        #endregion
         1167  +
         1168  +        ///////////////////////////////////////////////////////////////////////
         1169  +
         1170  +        #region IDisposable "Pattern" Members
         1171  +        private bool disposed;
         1172  +        private void CheckDisposed() /* throw */
         1173  +        {
         1174  +#if THROW_ON_DISPOSED
         1175  +            if (disposed)
         1176  +            {
         1177  +                throw new ObjectDisposedException(
         1178  +                    typeof(SQLiteSessionStreamManager).Name);
         1179  +            }
         1180  +#endif
         1181  +        }
         1182  +
         1183  +        ///////////////////////////////////////////////////////////////////////
         1184  +
         1185  +        private /* protected virtual */ void Dispose(bool disposing)
         1186  +        {
         1187  +            try
         1188  +            {
         1189  +                if (!disposed)
         1190  +                {
         1191  +                    if (disposing)
         1192  +                    {
         1193  +                        ////////////////////////////////////
         1194  +                        // dispose managed resources here...
         1195  +                        ////////////////////////////////////
         1196  +
         1197  +                        DisposeStreamAdapters();
         1198  +                    }
         1199  +
         1200  +                    //////////////////////////////////////
         1201  +                    // release unmanaged resources here...
         1202  +                    //////////////////////////////////////
         1203  +                }
         1204  +            }
         1205  +            finally
         1206  +            {
         1207  +                //
         1208  +                // NOTE: Everything should be fully disposed at this point.
         1209  +                //
         1210  +                disposed = true;
         1211  +            }
         1212  +        }
         1213  +        #endregion
         1214  +
         1215  +        ///////////////////////////////////////////////////////////////////////
         1216  +
         1217  +        #region Destructor
         1218  +        ~SQLiteSessionStreamManager()
         1219  +        {
         1220  +            Dispose(false);
         1221  +        }
         1222  +        #endregion
         1223  +    }
         1224  +    #endregion
         1225  +
         1226  +    ///////////////////////////////////////////////////////////////////////////
         1227  +
  1020   1228       #region SQLiteChangeGroup Class
  1021   1229       internal sealed class SQLiteChangeGroup : ISQLiteChangeGroup
  1022   1230       {
  1023   1231           #region Private Data
         1232  +        private SQLiteSessionStreamManager streamManager;
  1024   1233           private SQLiteConnectionFlags flags;
  1025   1234   
  1026   1235           ///////////////////////////////////////////////////////////////////////
  1027   1236   
  1028   1237           private IntPtr changeGroup;
  1029   1238           #endregion
  1030   1239   
................................................................................
  1033   1242           #region Public Constructors
  1034   1243           public SQLiteChangeGroup(
  1035   1244               SQLiteConnectionFlags flags
  1036   1245               )
  1037   1246           {
  1038   1247               this.flags = flags;
  1039   1248   
  1040         -            Initialize();
         1249  +            InitializeHandle();
  1041   1250           }
  1042   1251           #endregion
  1043   1252   
  1044   1253           ///////////////////////////////////////////////////////////////////////
  1045   1254   
  1046   1255           #region Private Methods
  1047   1256           private void CheckHandle()
................................................................................
  1048   1257           {
  1049   1258               if (changeGroup == IntPtr.Zero)
  1050   1259                   throw new InvalidOperationException("change group not open");
  1051   1260           }
  1052   1261   
  1053   1262           ///////////////////////////////////////////////////////////////////////
  1054   1263   
  1055         -        private void Initialize()
         1264  +        private void InitializeHandle()
  1056   1265           {
  1057   1266               if (changeGroup != IntPtr.Zero)
  1058   1267                   return;
  1059   1268   
  1060   1269               SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changegroup_new(
  1061   1270                   ref changeGroup);
  1062   1271   
  1063   1272               if (rc != SQLiteErrorCode.Ok)
  1064   1273                   throw new SQLiteException(rc, "sqlite3changegroup_new");
  1065   1274           }
         1275  +
         1276  +        ///////////////////////////////////////////////////////////////////////
         1277  +
         1278  +        private void InitializeStreamManager()
         1279  +        {
         1280  +            if (streamManager != null)
         1281  +                return;
         1282  +
         1283  +            streamManager = new SQLiteSessionStreamManager(flags);
         1284  +        }
         1285  +
         1286  +        ///////////////////////////////////////////////////////////////////////
         1287  +
         1288  +        private SQLiteStreamAdapter GetStreamAdapter(
         1289  +            Stream stream
         1290  +            )
         1291  +        {
         1292  +            InitializeStreamManager();
         1293  +
         1294  +            return streamManager.GetAdapter(stream);
         1295  +        }
  1066   1296           #endregion
  1067   1297   
  1068   1298           ///////////////////////////////////////////////////////////////////////
  1069   1299   
  1070   1300           #region ISQLiteChangeGroup Members
  1071   1301           public void AddChangeSet(
  1072   1302               byte[] rawData
................................................................................
  1108   1338               )
  1109   1339           {
  1110   1340               CheckDisposed();
  1111   1341               CheckHandle();
  1112   1342   
  1113   1343               if (stream == null)
  1114   1344                   throw new ArgumentNullException("stream");
         1345  +
         1346  +            SQLiteStreamAdapter streamAdapter = GetStreamAdapter(stream);
         1347  +
         1348  +            if (streamAdapter == null)
         1349  +            {
         1350  +                throw new SQLiteException(
         1351  +                    "could not get or create adapter for input stream");
         1352  +            }
  1115   1353   
  1116   1354               SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changegroup_add_strm(
  1117         -                changeGroup, new SQLiteStreamAdapter(stream, flags).xInput,
  1118         -                IntPtr.Zero);
         1355  +                changeGroup, streamAdapter.GetInputDelegate(), IntPtr.Zero);
  1119   1356   
  1120   1357               if (rc != SQLiteErrorCode.Ok)
  1121   1358                   throw new SQLiteException(rc, "sqlite3changegroup_add_strm");
  1122   1359           }
  1123   1360   
  1124   1361           ///////////////////////////////////////////////////////////////////////
  1125   1362   
................................................................................
  1163   1400               )
  1164   1401           {
  1165   1402               CheckDisposed();
  1166   1403               CheckHandle();
  1167   1404   
  1168   1405               if (stream == null)
  1169   1406                   throw new ArgumentNullException("stream");
         1407  +
         1408  +            SQLiteStreamAdapter streamAdapter = GetStreamAdapter(stream);
         1409  +
         1410  +            if (streamAdapter == null)
         1411  +            {
         1412  +                throw new SQLiteException(
         1413  +                    "could not get or create adapter for output stream");
         1414  +            }
  1170   1415   
  1171   1416               SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changegroup_output_strm(
  1172         -                changeGroup, new SQLiteStreamAdapter(stream, flags).xOutput,
  1173         -                IntPtr.Zero);
         1417  +                changeGroup, streamAdapter.GetOutputDelegate(), IntPtr.Zero);
  1174   1418   
  1175   1419               if (rc != SQLiteErrorCode.Ok)
  1176   1420                   throw new SQLiteException(rc, "sqlite3changegroup_output_strm");
  1177   1421           }
  1178   1422           #endregion
  1179   1423   
  1180   1424           ///////////////////////////////////////////////////////////////////////
................................................................................
  1212   1456                   {
  1213   1457                       if (disposing)
  1214   1458                       {
  1215   1459                           ////////////////////////////////////
  1216   1460                           // dispose managed resources here...
  1217   1461                           ////////////////////////////////////
  1218   1462   
  1219         -                        if (changeGroup != IntPtr.Zero)
         1463  +                        if (streamManager != null)
  1220   1464                           {
  1221         -                            UnsafeNativeMethods.sqlite3changegroup_delete(
  1222         -                                changeGroup);
  1223         -
  1224         -                            changeGroup = IntPtr.Zero;
         1465  +                            streamManager.Dispose();
         1466  +                            streamManager = null;
  1225   1467                           }
  1226   1468                       }
  1227   1469   
  1228   1470                       //////////////////////////////////////
  1229   1471                       // release unmanaged resources here...
  1230   1472                       //////////////////////////////////////
         1473  +
         1474  +                    if (changeGroup != IntPtr.Zero)
         1475  +                    {
         1476  +                        UnsafeNativeMethods.sqlite3changegroup_delete(
         1477  +                            changeGroup);
         1478  +
         1479  +                        changeGroup = IntPtr.Zero;
         1480  +                    }
  1231   1481                   }
  1232   1482               }
  1233   1483               finally
  1234   1484               {
  1235   1485                   //
  1236   1486                   // NOTE: Everything should be fully disposed at this point.
  1237   1487                   //
................................................................................
  1253   1503   
  1254   1504       ///////////////////////////////////////////////////////////////////////////
  1255   1505   
  1256   1506       #region SQLiteSession Class
  1257   1507       internal sealed class SQLiteSession : SQLiteConnectionLock, ISQLiteSession
  1258   1508       {
  1259   1509           #region Private Data
         1510  +        private SQLiteSessionStreamManager streamManager;
  1260   1511           private string databaseName;
  1261   1512   
  1262   1513           ///////////////////////////////////////////////////////////////////////
  1263   1514   
  1264   1515           private IntPtr session;
  1265   1516   
  1266   1517           ///////////////////////////////////////////////////////////////////////
  1267   1518   
         1519  +        private UnsafeNativeMethods.xSessionFilter xFilter;
  1268   1520           private SessionTableFilterCallback tableFilterCallback;
  1269   1521           private object tableFilterClientData;
  1270   1522           #endregion
  1271   1523   
  1272   1524           ///////////////////////////////////////////////////////////////////////
  1273   1525   
  1274   1526           #region Public Constructors
................................................................................
  1277   1529               SQLiteConnectionFlags flags,
  1278   1530               string databaseName
  1279   1531               )
  1280   1532               : base(handle, flags)
  1281   1533           {
  1282   1534               this.databaseName = databaseName;
  1283   1535   
  1284         -            Initialize();
         1536  +            InitializeHandle();
  1285   1537           }
  1286   1538           #endregion
  1287   1539   
  1288   1540           ///////////////////////////////////////////////////////////////////////
  1289   1541   
  1290   1542           #region Private Methods
  1291   1543           private void CheckHandle()
................................................................................
  1292   1544           {
  1293   1545               if (session == IntPtr.Zero)
  1294   1546                   throw new InvalidOperationException("session is not open");
  1295   1547           }
  1296   1548   
  1297   1549           ///////////////////////////////////////////////////////////////////////
  1298   1550   
  1299         -        private void Initialize()
         1551  +        private void InitializeHandle()
  1300   1552           {
  1301   1553               if (session != IntPtr.Zero)
  1302   1554                   return;
  1303   1555   
  1304   1556               SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_create(
  1305   1557                   GetIntPtr(), SQLiteString.GetUtf8BytesFromString(databaseName),
  1306   1558                   ref session);
................................................................................
  1312   1564           ///////////////////////////////////////////////////////////////////////
  1313   1565   
  1314   1566           private UnsafeNativeMethods.xSessionFilter ApplyTableFilter(
  1315   1567               SessionTableFilterCallback callback, /* in: NULL OK */
  1316   1568               object clientData                    /* in: NULL OK */
  1317   1569               )
  1318   1570           {
  1319         -            this.tableFilterCallback = callback;
  1320         -            this.tableFilterClientData = clientData;
         1571  +            tableFilterCallback = callback;
         1572  +            tableFilterClientData = clientData;
  1321   1573   
  1322         -            return (callback != null) ?
  1323         -                xFilter : (UnsafeNativeMethods.xSessionFilter)null;
         1574  +            if (callback == null)
         1575  +            {
         1576  +                if (xFilter != null)
         1577  +                    xFilter = null;
         1578  +
         1579  +                return null;
         1580  +            }
         1581  +
         1582  +            if (xFilter == null)
         1583  +                xFilter = new UnsafeNativeMethods.xSessionFilter(Filter);
         1584  +
         1585  +            return xFilter;
  1324   1586           }
         1587  +
         1588  +        ///////////////////////////////////////////////////////////////////////
         1589  +
         1590  +        private void InitializeStreamManager()
         1591  +        {
         1592  +            if (streamManager != null)
         1593  +                return;
         1594  +
         1595  +            streamManager = new SQLiteSessionStreamManager(GetFlags());
         1596  +        }
         1597  +
         1598  +        ///////////////////////////////////////////////////////////////////////
         1599  +
         1600  +        private SQLiteStreamAdapter GetStreamAdapter(
         1601  +            Stream stream
         1602  +            )
         1603  +        {
         1604  +            InitializeStreamManager();
         1605  +
         1606  +            return streamManager.GetAdapter(stream);
         1607  +        }
         1608  +        #endregion
  1325   1609   
  1326   1610           ///////////////////////////////////////////////////////////////////////
  1327   1611   
  1328   1612           #region Native Callback Methods
  1329         -        private int xFilter(
         1613  +        private int Filter(
  1330   1614               IntPtr context, /* NOT USED */
  1331   1615               IntPtr pTblName
  1332   1616               )
  1333   1617           {
  1334   1618               try
  1335   1619               {
  1336   1620                   return tableFilterCallback(tableFilterClientData,
................................................................................
  1343   1627                       if (HelperMethods.LogCallbackExceptions(GetFlags()))
  1344   1628                       {
  1345   1629                           SQLiteLog.LogMessage( /* throw */
  1346   1630                               SQLiteBase.COR_E_EXCEPTION,
  1347   1631                               HelperMethods.StringFormat(
  1348   1632                               CultureInfo.CurrentCulture,
  1349   1633                               UnsafeNativeMethods.ExceptionMessageFormat,
  1350         -                            "xFilter", e));
         1634  +                            "xSessionFilter", e));
  1351   1635                       }
  1352   1636                   }
  1353   1637                   catch
  1354   1638                   {
  1355   1639                       // do nothing.
  1356   1640                   }
  1357   1641               }
  1358   1642   
  1359   1643               return 0;
  1360   1644           }
  1361         -        #endregion
  1362   1645           #endregion
  1363   1646   
  1364   1647           ///////////////////////////////////////////////////////////////////////
  1365   1648   
  1366   1649           #region ISQLiteSession Members
  1367   1650           public bool IsEnabled()
  1368   1651           {
................................................................................
  1503   1786           {
  1504   1787               CheckDisposed();
  1505   1788               CheckHandle();
  1506   1789   
  1507   1790               if (stream == null)
  1508   1791                   throw new ArgumentNullException("stream");
  1509   1792   
  1510         -            SQLiteConnectionFlags flags = GetFlags();
         1793  +            SQLiteStreamAdapter streamAdapter = GetStreamAdapter(stream);
         1794  +
         1795  +            if (streamAdapter == null)
         1796  +            {
         1797  +                throw new SQLiteException(
         1798  +                    "could not get or create adapter for output stream");
         1799  +            }
  1511   1800   
  1512   1801               SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_changeset_strm(
  1513         -                session, new SQLiteStreamAdapter(stream, flags).xOutput,
  1514         -                IntPtr.Zero);
         1802  +                session, streamAdapter.GetOutputDelegate(), IntPtr.Zero);
  1515   1803   
  1516   1804               if (rc != SQLiteErrorCode.Ok)
  1517   1805                   throw new SQLiteException(rc, "sqlite3session_changeset_strm");
  1518   1806           }
  1519   1807   
  1520   1808           ///////////////////////////////////////////////////////////////////////
  1521   1809   
................................................................................
  1558   1846           {
  1559   1847               CheckDisposed();
  1560   1848               CheckHandle();
  1561   1849   
  1562   1850               if (stream == null)
  1563   1851                   throw new ArgumentNullException("stream");
  1564   1852   
  1565         -            SQLiteConnectionFlags flags = GetFlags();
         1853  +            SQLiteStreamAdapter streamAdapter = GetStreamAdapter(stream);
         1854  +
         1855  +            if (streamAdapter == null)
         1856  +            {
         1857  +                throw new SQLiteException(
         1858  +                    "could not get or create adapter for output stream");
         1859  +            }
  1566   1860   
  1567   1861               SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_patchset_strm(
  1568         -                session, new SQLiteStreamAdapter(stream, flags).xOutput,
  1569         -                IntPtr.Zero);
         1862  +                session, streamAdapter.GetOutputDelegate(), IntPtr.Zero);
  1570   1863   
  1571   1864               if (rc != SQLiteErrorCode.Ok)
  1572   1865                   throw new SQLiteException(rc, "sqlite3session_patchset_strm");
  1573   1866           }
  1574   1867   
  1575   1868           ///////////////////////////////////////////////////////////////////////
  1576   1869   
................................................................................
  1643   1936   
  1644   1937           protected override void Dispose(bool disposing)
  1645   1938           {
  1646   1939               try
  1647   1940               {
  1648   1941                   if (!disposed)
  1649   1942                   {
  1650         -                    //if (disposing)
  1651         -                    //{
  1652         -                    //    ////////////////////////////////////
  1653         -                    //    // dispose managed resources here...
  1654         -                    //    ////////////////////////////////////
  1655         -                    //}
         1943  +                    if (disposing)
         1944  +                    {
         1945  +                        ////////////////////////////////////
         1946  +                        // dispose managed resources here...
         1947  +                        ////////////////////////////////////
         1948  +
         1949  +                        if (xFilter != null)
         1950  +                            xFilter = null;
         1951  +
         1952  +                        if (streamManager != null)
         1953  +                        {
         1954  +                            streamManager.Dispose();
         1955  +                            streamManager = null;
         1956  +                        }
         1957  +                    }
  1656   1958   
  1657   1959                       //////////////////////////////////////
  1658   1960                       // release unmanaged resources here...
  1659   1961                       //////////////////////////////////////
  1660   1962   
  1661   1963                       if (session != IntPtr.Zero)
  1662   1964                       {
................................................................................
  1739   2041                           if (HelperMethods.LogCallbackExceptions(GetFlags()))
  1740   2042                           {
  1741   2043                               SQLiteLog.LogMessage( /* throw */
  1742   2044                                   SQLiteBase.COR_E_EXCEPTION,
  1743   2045                                   HelperMethods.StringFormat(
  1744   2046                                   CultureInfo.CurrentCulture,
  1745   2047                                   UnsafeNativeMethods.ExceptionMessageFormat,
  1746         -                                "xFilter", e));
         2048  +                                "xSessionFilter", e));
  1747   2049                           }
  1748   2050                       }
  1749   2051                       catch
  1750   2052                       {
  1751   2053                           // do nothing.
  1752   2054                       }
  1753   2055                   }
................................................................................
  1795   2097                           if (HelperMethods.LogCallbackExceptions(GetFlags()))
  1796   2098                           {
  1797   2099                               SQLiteLog.LogMessage( /* throw */
  1798   2100                                   SQLiteBase.COR_E_EXCEPTION,
  1799   2101                                   HelperMethods.StringFormat(
  1800   2102                                   CultureInfo.CurrentCulture,
  1801   2103                                   UnsafeNativeMethods.ExceptionMessageFormat,
  1802         -                                "xConflict", e));
         2104  +                                "xSessionConflict", e));
  1803   2105                           }
  1804   2106                       }
  1805   2107                       catch
  1806   2108                       {
  1807   2109                           // do nothing.
  1808   2110                       }
  1809   2111                   }
................................................................................
  2142   2444       ///////////////////////////////////////////////////////////////////////////
  2143   2445   
  2144   2446       #region SQLiteStreamChangeSet Class
  2145   2447       internal sealed class SQLiteStreamChangeSet :
  2146   2448           SQLiteChangeSetBase, ISQLiteChangeSet
  2147   2449       {
  2148   2450           #region Private Data
         2451  +        private SQLiteStreamAdapter inputStreamAdapter;
         2452  +        private SQLiteStreamAdapter outputStreamAdapter;
  2149   2453           private Stream inputStream;
  2150   2454           private Stream outputStream;
  2151   2455           #endregion
  2152   2456   
  2153   2457           ///////////////////////////////////////////////////////////////////////
  2154   2458   
  2155   2459           #region Private Constructors
................................................................................
  2172   2476           private void CheckInputStream()
  2173   2477           {
  2174   2478               if (inputStream == null)
  2175   2479               {
  2176   2480                   throw new InvalidOperationException(
  2177   2481                       "input stream unavailable");
  2178   2482               }
         2483  +
         2484  +            if (inputStreamAdapter == null)
         2485  +            {
         2486  +                inputStreamAdapter = new SQLiteStreamAdapter(
         2487  +                    inputStream, GetFlags());
         2488  +            }
  2179   2489           }
  2180   2490   
  2181   2491           ///////////////////////////////////////////////////////////////////////
  2182   2492   
  2183   2493           private void CheckOutputStream()
  2184   2494           {
  2185   2495               if (outputStream == null)
  2186   2496               {
  2187   2497                   throw new InvalidOperationException(
  2188   2498                       "output stream unavailable");
  2189   2499               }
         2500  +
         2501  +            if (outputStreamAdapter == null)
         2502  +            {
         2503  +                outputStreamAdapter = new SQLiteStreamAdapter(
         2504  +                    outputStream, GetFlags());
         2505  +            }
  2190   2506           }
  2191   2507           #endregion
  2192   2508   
  2193   2509           ///////////////////////////////////////////////////////////////////////
  2194   2510   
  2195   2511           #region ISQLiteChangeSet Members
  2196   2512           public ISQLiteChangeSet Invert()
  2197   2513           {
  2198   2514               CheckDisposed();
  2199   2515               CheckInputStream();
  2200   2516               CheckOutputStream();
  2201   2517   
  2202         -            SQLiteConnectionFlags flags = GetFlags();
  2203         -
  2204   2518               SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_invert_strm(
  2205         -                new SQLiteStreamAdapter(inputStream, flags).xInput, IntPtr.Zero,
  2206         -                new SQLiteStreamAdapter(outputStream, flags).xOutput, IntPtr.Zero);
         2519  +                inputStreamAdapter.GetInputDelegate(), IntPtr.Zero,
         2520  +                outputStreamAdapter.GetOutputDelegate(), IntPtr.Zero);
  2207   2521   
  2208   2522               if (rc != SQLiteErrorCode.Ok)
  2209   2523                   throw new SQLiteException(rc, "sqlite3changeset_invert_strm");
  2210   2524   
  2211   2525               return null;
  2212   2526           }
  2213   2527   
................................................................................
  2228   2542               {
  2229   2543                   throw new ArgumentException(
  2230   2544                       "not a stream based change set", "changeSet");
  2231   2545               }
  2232   2546   
  2233   2547               streamChangeSet.CheckInputStream();
  2234   2548   
  2235         -            SQLiteConnectionFlags otherFlags = streamChangeSet.GetFlags();
  2236         -            SQLiteConnectionFlags flags = GetFlags();
  2237         -
  2238   2549               SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_concat_strm(
  2239         -                new SQLiteStreamAdapter(inputStream, flags).xInput,
  2240         -                IntPtr.Zero, new SQLiteStreamAdapter(streamChangeSet.inputStream,
  2241         -                otherFlags).xInput, IntPtr.Zero, new SQLiteStreamAdapter(
  2242         -                outputStream, flags).xOutput, IntPtr.Zero);
         2550  +                inputStreamAdapter.GetInputDelegate(), IntPtr.Zero,
         2551  +                streamChangeSet.inputStreamAdapter.GetInputDelegate(),
         2552  +                IntPtr.Zero, outputStreamAdapter.GetOutputDelegate(),
         2553  +                IntPtr.Zero);
  2243   2554   
  2244   2555               if (rc != SQLiteErrorCode.Ok)
  2245   2556                   throw new SQLiteException(rc, "sqlite3changeset_concat_strm");
  2246   2557   
  2247   2558               return null;
  2248   2559           }
  2249   2560   
................................................................................
  2275   2586   
  2276   2587               UnsafeNativeMethods.xSessionFilter xFilter = GetDelegate(
  2277   2588                   tableFilterCallback, clientData);
  2278   2589   
  2279   2590               UnsafeNativeMethods.xSessionConflict xConflict = GetDelegate(
  2280   2591                   conflictCallback, clientData);
  2281   2592   
  2282         -            SQLiteConnectionFlags flags = GetFlags();
  2283         -
  2284   2593               SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_apply_strm(
  2285         -                GetIntPtr(), new SQLiteStreamAdapter(inputStream, flags).xInput,
  2286         -                IntPtr.Zero, xFilter, xConflict, IntPtr.Zero);
         2594  +                GetIntPtr(), inputStreamAdapter.GetInputDelegate(), IntPtr.Zero,
         2595  +                xFilter, xConflict, IntPtr.Zero);
  2287   2596   
  2288   2597               if (rc != SQLiteErrorCode.Ok)
  2289   2598                   throw new SQLiteException(rc, "sqlite3changeset_apply_strm");
  2290   2599           }
  2291   2600           #endregion
  2292   2601   
  2293   2602           ///////////////////////////////////////////////////////////////////////
  2294   2603   
  2295   2604           #region IEnumerable<ISQLiteChangeSetMetadataItem> Members
  2296   2605           public IEnumerator<ISQLiteChangeSetMetadataItem> GetEnumerator()
  2297   2606           {
  2298         -            SQLiteConnectionFlags flags = GetFlags();
  2299         -            return new SQLiteStreamChangeSetEnumerator(inputStream, flags);
         2607  +            return new SQLiteStreamChangeSetEnumerator(
         2608  +                inputStream, GetFlags());
  2300   2609           }
  2301   2610           #endregion
  2302   2611   
  2303   2612           ///////////////////////////////////////////////////////////////////////
  2304   2613   
  2305   2614           #region IEnumerable Members
  2306   2615           IEnumerator System.Collections.IEnumerable.GetEnumerator()

Changes to Tests/session.eagle.

    86     86       return [object invoke -flags +NonPublic \
    87     87           System.Data.SQLite.HelperMethods ToDisplayString $value]
    88     88     }
    89     89   }
    90     90   
    91     91   ###############################################################################
    92     92   
    93         -proc createMemoryChangeSetForSession { session } {
           93  +proc createMemoryChangeSetForConnection { connection rawData } {
           94  +  if {[isObjectHandle $connection] && $connection ne "null"} then {
           95  +    return [$connection -alias CreateChangeSet $rawData]
           96  +  }
           97  +
           98  +  return null
           99  +}
          100  +
          101  +###############################################################################
          102  +
          103  +proc createMemoryChangeSetForSession { session {patch false} } {
    94    104     if {[isObjectHandle $session] && $session ne "null"} then {
    95    105       set byteArray null
    96         -    $session -alias CreateChangeSet byteArray
          106  +
          107  +    if {$patch} then {
          108  +      $session -alias CreatePatchSet byteArray
          109  +    } else {
          110  +      $session -alias CreateChangeSet byteArray
          111  +    }
    97    112   
    98    113       set rawData [createByteArray [arrayToList byteArray]]
    99    114       object removeref $rawData
   100    115   
   101    116       return $rawData
   102    117     }
   103    118   
................................................................................
   150    165     }
   151    166   
   152    167     return false
   153    168   }
   154    169   
   155    170   ###############################################################################
   156    171   
   157         -proc writeStreamChangeSetForSession { session fileName } {
          172  +proc writeStreamChangeSetForSession { session fileName {patch false} } {
   158    173     if {[isObjectHandle $session] && $session ne "null"} then {
   159    174       set stream [object create -alias \
   160    175           System.IO.FileStream $fileName Create Write]
   161    176   
   162         -    $session -alias CreateChangeSet $stream
          177  +    if {$patch} then {
          178  +      $session -alias CreatePatchSet $stream
          179  +    } else {
          180  +      $session -alias CreateChangeSet $stream
          181  +    }
          182  +
   163    183       $stream Flush; $stream Close
   164    184   
   165    185       return true
   166    186     }
   167    187   
   168    188     return false
   169    189   }
................................................................................
   171    191   ###############################################################################
   172    192   
   173    193   proc changeSetFileToString { connection fileName includeValues } {
   174    194     set result [list]
   175    195   
   176    196     if {[isObjectHandle $connection] && $connection ne "null"} then {
   177    197       if {[openStreamChangeSetForConnection \
   178         -        $connection $fileName "" changeSet]} then {
   179         -      return [changeSetToString $changeSet(changeSet) $includeValues]
          198  +        $connection $fileName "" state]} then {
          199  +      return [changeSetToString $state(changeSet) $includeValues]
   180    200       }
   181    201     }
   182    202   
   183    203     return $result
   184    204   }
   185    205   
   186    206   ###############################################################################
................................................................................
   509    529     $session AttachTable null
   510    530   
   511    531     makeSomeChanges $db t1 [list insert update delete]
   512    532   
   513    533     set rawData [createMemoryChangeSetForSession $session]
   514    534     object removeref $rawData
   515    535   
   516         -  set changeSet(1) [$connection -alias CreateChangeSet $rawData]
          536  +  set changeSet(1) \
          537  +      [createMemoryChangeSetForConnection $connection $rawData]
          538  +
          539  +  object removeref $changeSet(1)
          540  +
   517    541     set changeSet(2) [$changeSet(1) -alias Invert]
   518    542   
   519    543     list [changeSetToString $changeSet(2) true]
   520    544   } -cleanup {
   521    545     cleanupSomeText
   522    546   
   523    547     unset -nocomplain changeSet rawData byteArray session
................................................................................
   642    666   
   643    667     makeSomeChanges $db t1 [list insert]
   644    668     lappend result IsEmpty [$session IsEmpty]
   645    669   
   646    670     set rawData [createMemoryChangeSetForSession $session]
   647    671     object removeref $rawData
   648    672   
   649         -  set changeSet(1) [$connection -alias CreateChangeSet $rawData]
          673  +  set changeSet(1) \
          674  +      [createMemoryChangeSetForConnection $connection $rawData]
          675  +
          676  +  object removeref $changeSet(1)
          677  +
   650    678     lappend result [changeSetToString $changeSet(1) false]
   651    679   } -cleanup {
   652    680     cleanupSomeText
   653    681   
   654    682     unset -nocomplain result changeSet rawData byteArray session
   655    683   
   656    684     freeDbConnection
................................................................................
   699    727   
   700    728     makeSomeChanges $db t1 [list insert]
   701    729     lappend result IsEmpty [$session IsEmpty]
   702    730   
   703    731     set rawData [createMemoryChangeSetForSession $session]
   704    732     object removeref $rawData
   705    733   
   706         -  set changeSet(1) [$connection -alias CreateChangeSet $rawData]
          734  +  set changeSet(1) \
          735  +      [createMemoryChangeSetForConnection $connection $rawData]
          736  +
          737  +  object removeref $changeSet(1)
          738  +
   707    739     lappend result [changeSetToString $changeSet(1) false]
   708    740   } -cleanup {
   709    741     cleanupSomeText
   710    742   
   711    743     unset -nocomplain result changeSet rawData byteArray session
   712    744   
   713    745     freeDbConnection
................................................................................
   817    849   
   818    850     makeSomeChanges $db t1 [list insert] 1
   819    851   
   820    852     set rawData(2) [captureChangeSetRawData $connection main null {
   821    853       makeSomeChanges $db t1 [list insert]
   822    854     }]; object removeref $rawData(2)
   823    855   
   824         -  set changeSet(1) [$connection -alias CreateChangeSet $rawData(1)]
   825         -  set changeSet(2) [$connection -alias CreateChangeSet $rawData(2)]
          856  +  set changeSet(1) \
          857  +      [createMemoryChangeSetForConnection $connection $rawData(1)]
          858  +
          859  +  object removeref $changeSet(1)
          860  +
          861  +  set changeSet(2) \
          862  +      [createMemoryChangeSetForConnection $connection $rawData(2)]
          863  +
          864  +  object removeref $changeSet(2)
          865  +
   826    866     set changeSet(3) [$changeSet(1) -alias CombineWith $changeSet(2)]
   827    867   
   828    868     $changeSet(3) -marshalflags +DynamicCallback \
   829    869         Apply conflictCallback null
   830    870   
   831    871     list [changeSetToString $changeSet(3) true] Data \
   832    872         [sql execute -execute reader -format list $db \
................................................................................
  1008   1048   rename changeSetToString ""
  1009   1049   rename metadataItemToString ""
  1010   1050   rename changeSetFileToString ""
  1011   1051   rename writeStreamChangeSetForSession ""
  1012   1052   rename openStreamChangeSetForConnection ""
  1013   1053   rename writeRawDataToFile ""
  1014   1054   rename createMemoryChangeSetForSession ""
         1055  +rename createMemoryChangeSetForConnection ""
  1015   1056   rename forDisplay ""
  1016   1057   rename cleanupSomeText ""
  1017   1058   rename getSomeText ""
  1018   1059   
  1019   1060   ###############################################################################
  1020   1061   
  1021   1062   runSQLiteTestEpilogue
  1022   1063   runTestEpilogue