/// <summary>
        /// Writes a profiling dataset to the database.
        /// </summary>
        public void WriteDataSet(IProfilingDataSet dataSet)
        {
            if (dataSet == null)
            {
                throw new ArgumentNullException("dataSet");
            }

            using (SQLiteTransaction transaction = this.connection.BeginTransaction()) {
                SQLiteCommand cmd = this.connection.CreateCommand();

                if (dataSetCount == -1)
                {
                    dataSetCount = 0;
                }

                cmd.Parameters.Add(new SQLiteParameter("id", dataSetCount));
                cmd.Parameters.Add(new SQLiteParameter("cpuuage", dataSet.CpuUsage.ToString(CultureInfo.InvariantCulture)));
                cmd.Parameters.Add(new SQLiteParameter("isfirst", dataSet.IsFirst));
                cmd.Parameters.Add(new SQLiteParameter("rootid", functionInfoCount));

                cmd.CommandText = "INSERT INTO DataSets(id, cpuusage, isfirst, rootid)" +
                                  "VALUES(?,?,?,?);";

                using (SQLiteCommand loopCommand = this.connection.CreateCommand()) {
                    CallTreeNode node = dataSet.RootNode;

                    loopCommand.CommandText = "INSERT INTO FunctionData(datasetid, id, endid, parentid, nameid, timespent, isactiveatstart, callcount)" +
                                              "VALUES(?,?,?,?,?,?,?,?);";

                    FunctionDataParams dataParams = new FunctionDataParams();
                    loopCommand.Parameters.Add(dataParams.dataSetId       = new SQLiteParameter());
                    loopCommand.Parameters.Add(dataParams.functionInfoId  = new SQLiteParameter());
                    loopCommand.Parameters.Add(dataParams.endId           = new SQLiteParameter());
                    loopCommand.Parameters.Add(dataParams.parentId        = new SQLiteParameter());
                    loopCommand.Parameters.Add(dataParams.nameId          = new SQLiteParameter());
                    loopCommand.Parameters.Add(dataParams.cpuCyclesSpent  = new SQLiteParameter());
                    loopCommand.Parameters.Add(dataParams.isActiveAtStart = new SQLiteParameter());
                    loopCommand.Parameters.Add(dataParams.callCount       = new SQLiteParameter());

                    bool addedData = true;

                    if (profileUnitTests)
                    {
                        addedData = FindUnitTestsAndInsert(loopCommand, node, dataSet, dataParams);
                    }
                    else
                    {
                        InsertTree(loopCommand, node, -1, dataSet, dataParams);
                    }

                    if (addedData)
                    {
                        cmd.ExecuteNonQuery();
                        dataSetCount++;
                    }
                }

                transaction.Commit();
            }
        }
        void InsertTree(SQLiteCommand cmd, CallTreeNode node, int parentId, IProfilingDataSet dataSet, FunctionDataParams dataParams)
        {
            int thisID = functionInfoCount++;

            foreach (CallTreeNode child in node.Children)
            {
                InsertTree(cmd, child, thisID, dataSet, dataParams);
            }

            // we sometimes saw invalid data with the 0x0080000000000000L bit set
            if (node.CpuCyclesSpent > 0x0007ffffffffffffL || node.CpuCyclesSpent < 0)
            {
                throw new InvalidOperationException("Too large CpuCyclesSpent - there's something wrong in the data");
            }

            dataParams.callCount.Value       = node.RawCallCount;
            dataParams.isActiveAtStart.Value = node.IsActiveAtStart;
            dataParams.cpuCyclesSpent.Value  = node.CpuCyclesSpent;
            dataParams.dataSetId.Value       = dataSetCount;
            dataParams.functionInfoId.Value  = thisID;
            dataParams.nameId.Value          = node.NameMapping.Id;
            dataParams.parentId.Value        = parentId;
            dataParams.endId.Value           = functionInfoCount - 1;

            cmd.ExecuteNonQuery();
        }
        bool FindUnitTestsAndInsert(SQLiteCommand cmd, CallTreeNode node, IProfilingDataSet dataSet, FunctionDataParams dataParams)
        {
            List <CallTreeNode> list = new List <CallTreeNode>();

            FindUnitTests(node, list);

            if (list.Count > 0)
            {
                InsertTree(cmd, new UnitTestRootCallTreeNode(list), -1, dataSet, dataParams);
                return(true);
            }

            return(false);
        }
		/// <summary>
		/// Writes a profiling dataset to the database.
		/// </summary>
		public void WriteDataSet(IProfilingDataSet dataSet)
		{
			if (dataSet == null)
				throw new ArgumentNullException("dataSet");
			
			using (SQLiteTransaction transaction = this.connection.BeginTransaction()) {
				SQLiteCommand cmd = this.connection.CreateCommand();
				
				if (dataSetCount == -1)
					dataSetCount = 0;
				
				cmd.Parameters.Add(new SQLiteParameter("id", dataSetCount));
				cmd.Parameters.Add(new SQLiteParameter("cpuuage", dataSet.CpuUsage.ToString(CultureInfo.InvariantCulture)));
				cmd.Parameters.Add(new SQLiteParameter("isfirst", dataSet.IsFirst));
				cmd.Parameters.Add(new SQLiteParameter("rootid", functionInfoCount));
				
				cmd.CommandText = "INSERT INTO DataSets(id, cpuusage, isfirst, rootid)" +
					"VALUES(?,?,?,?);";
				
				using (SQLiteCommand loopCommand = this.connection.CreateCommand()) {
					CallTreeNode node = dataSet.RootNode;
					
					loopCommand.CommandText = "INSERT INTO FunctionData(datasetid, id, endid, parentid, nameid, timespent, isactiveatstart, callcount)" +
						"VALUES(?,?,?,?,?,?,?,?);";
					
					FunctionDataParams dataParams = new FunctionDataParams();
					loopCommand.Parameters.Add(dataParams.dataSetId = new SQLiteParameter());
					loopCommand.Parameters.Add(dataParams.functionInfoId = new SQLiteParameter());
					loopCommand.Parameters.Add(dataParams.endId = new SQLiteParameter());
					loopCommand.Parameters.Add(dataParams.parentId = new SQLiteParameter());
					loopCommand.Parameters.Add(dataParams.nameId = new SQLiteParameter());
					loopCommand.Parameters.Add(dataParams.cpuCyclesSpent = new SQLiteParameter());
					loopCommand.Parameters.Add(dataParams.isActiveAtStart = new SQLiteParameter());
					loopCommand.Parameters.Add(dataParams.callCount = new SQLiteParameter());
					
					bool addedData = true;
					
					if (profileUnitTests)
						addedData = FindUnitTestsAndInsert(loopCommand, node, dataSet, dataParams);
					else
						InsertTree(loopCommand, node, -1, dataSet, dataParams);
					
					if (addedData) {
						cmd.ExecuteNonQuery();
						dataSetCount++;
					}
				}
				
				transaction.Commit();
			}
		}
		void InsertTree(SQLiteCommand cmd, CallTreeNode node, int parentId, IProfilingDataSet dataSet, FunctionDataParams dataParams)
		{
			int thisID = functionInfoCount++;
			
			foreach (CallTreeNode child in node.Children) {
				InsertTree(cmd, child, thisID, dataSet, dataParams);
			}
			
			// we sometimes saw invalid data with the 0x0080000000000000L bit set
			if (node.CpuCyclesSpent > 0x0007ffffffffffffL || node.CpuCyclesSpent < 0) {
				throw new InvalidOperationException("Too large CpuCyclesSpent - there's something wrong in the data");
			}
			
			dataParams.callCount.Value = node.RawCallCount;
			dataParams.isActiveAtStart.Value = node.IsActiveAtStart;
			dataParams.cpuCyclesSpent.Value = node.CpuCyclesSpent;
			dataParams.dataSetId.Value = dataSetCount;
			dataParams.functionInfoId.Value = thisID;
			dataParams.nameId.Value = node.NameMapping.Id;
			dataParams.parentId.Value = parentId;
			dataParams.endId.Value = functionInfoCount - 1;
			
			cmd.ExecuteNonQuery();
		}
		bool FindUnitTestsAndInsert(SQLiteCommand cmd, CallTreeNode node, IProfilingDataSet dataSet, FunctionDataParams dataParams)
		{
			List<CallTreeNode> list = new List<CallTreeNode>();

			FindUnitTests(node, list);
			
			if (list.Count > 0) {
				InsertTree(cmd, new UnitTestRootCallTreeNode(list), -1, dataSet, dataParams);
				return true;
			}
			
			return false;
		}