/// <summary>
		/// Removes a Message.
		/// </summary>
		/// <param name="transaction">A database transaction.</param>
		/// <param name="page">The Page.</param>
		/// <param name="id">The ID of the Message to remove.</param>
		/// <param name="removeReplies">A value specifying whether or not to remove the replies.</param>
		/// <returns>True if the Message is removed successfully.</returns>
		private bool RemoveMessage(DbTransaction transaction, PageInfo page, int id, bool removeReplies) {
			string nspace, name;
			NameTools.ExpandFullName(page.FullName, out nspace, out name);
			if( string.IsNullOrEmpty(nspace)||string.IsNullOrEmpty(nspace.Trim())) nspace = " ";

			Message[] messages = GetMessages(transaction, page);
			if(messages == null) return false;
			Message message = FindMessage(messages, id);
			if(message == null) return false;
			Message parent = FindAnchestor(messages, id);
			int parentId = parent != null ? parent.ID : -1;

			UnindexMessage(page, message.ID, message.Subject, message.DateTime, message.Body, transaction);

			if(removeReplies) {
				// Recursively remove all replies BEFORE removing parent (depth-first)
				foreach(Message reply in message.Replies) {
					if(!RemoveMessage(transaction, page, reply.ID, true)) return false;
				}
			}

			// Remove this message
			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("Message");
			query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
			query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");
			query = queryBuilder.AndWhere(query, "Id", WhereOperator.Equals, "Id");

			List<Parameter> parameters = new List<Parameter>(3);
			parameters.Add(new Parameter(ParameterType.String, "Page", name));
			parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));
			parameters.Add(new Parameter(ParameterType.Int16, "Id", (short)id));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			if(!removeReplies && rows == 1) {
				// Update replies' parent id

				query = queryBuilder.Update("Message", new string[] { "Parent" }, new string[] { "NewParent" });
				query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
				query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");
				query = queryBuilder.AndWhere(query, "Parent", WhereOperator.Equals, "OldParent");

				parameters = new List<Parameter>(4);
				if(parentId != -1) parameters.Add(new Parameter(ParameterType.Int16, "NewParent", parentId));
				else parameters.Add(new Parameter(ParameterType.Int16, "NewParent", DBNull.Value));
				parameters.Add(new Parameter(ParameterType.String, "Page", name));
				parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));
				parameters.Add(new Parameter(ParameterType.Int16, "OldParent", (short)id));

				command = builder.GetCommand(transaction, query, parameters);

				rows = ExecuteNonQuery(command, false);
			}

			return rows > 0;
		}
		/// <summary>
		/// Removes the user group membership for a user.
		/// </summary>
		/// <param name="transaction">A database transaction.</param>
		/// <param name="username">The username.</param>
		private void RemoveUserGroupMembership(DbTransaction transaction, string username) {
			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("UserGroupMembership");
			query = queryBuilder.Where(query, "User", WhereOperator.Equals, "Username");

			List<Parameter> parameters = new List<Parameter>(1);
			parameters.Add(new Parameter(ParameterType.String, "Username", username));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			ExecuteNonQuery(command, false);
		}
		/// <summary>
		/// Removes all messages for a page and stores the new messages.
		/// </summary>
		/// <param name="page">The page.</param>
		/// <param name="messages">The new messages to store.</param>
		/// <returns><c>true</c> if the messages are stored, <c>false</c> otherwise.</returns>
		/// <exception cref="ArgumentNullException">If <paramref name="page"/> or <paramref name="messages"/> are <c>null</c>.</exception>
		public bool BulkStoreMessages(PageInfo page, Message[] messages) {
			if(page == null) throw new ArgumentNullException("page");
			if(messages == null) throw new ArgumentNullException("messages");

			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			if(GetPage(transaction, page.FullName) == null) {
				RollbackTransaction(transaction);
				return false;
			}

			foreach(Message msg in GetMessages(transaction, page)) {
				UnindexMessageTree(page, msg, transaction);
			}

			string nspace, name;
			NameTools.ExpandFullName(page.FullName, out nspace, out name);
			if( string.IsNullOrEmpty(nspace)||string.IsNullOrEmpty(nspace.Trim())) nspace = " ";

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("Message");
			query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
			query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Page", name));
			parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			ExecuteNonQuery(command, false);

			List<Message> allMessages;
			List<int> parents;

			UnTreeMessages(messages, out allMessages, out parents, -1);

			string finalQuery = "";
			int count = 1;
			string countString;
			parameters = new List<Parameter>(MaxStatementsInBatch * 8);

			int rowsDone = 0;

			for(int i = 0; i < allMessages.Count; i++) {
				// Execute the batch in smaller chunks

				Message msg = allMessages[i];
				int parent = parents[i];

				countString = count.ToString();

				query = queryBuilder.InsertInto("Message", new string[] { "Page", "Namespace", "Id", "Parent", "Username", "Subject", "DateTime", "Body" },
					new string[] { "Page" + countString, "Namespace" + countString, "Id" + countString, "Parent" + countString, "Username" + countString, "Subject" + countString, "DateTime" + countString, "Body" + countString });

				parameters.Add(new Parameter(ParameterType.String, "Page" + countString, name));
				parameters.Add(new Parameter(ParameterType.String, "Namespace" + countString, nspace));
				parameters.Add(new Parameter(ParameterType.Int16, "Id" + countString, (short)msg.ID));
				if(parent != -1) parameters.Add(new Parameter(ParameterType.Int16, "Parent" + countString, parent));
				else parameters.Add(new Parameter(ParameterType.Int16, "Parent" + countString, DBNull.Value));
				parameters.Add(new Parameter(ParameterType.String, "Username" + countString, msg.Username));
				parameters.Add(new Parameter(ParameterType.String, "Subject" + countString, msg.Subject));
				parameters.Add(new Parameter(ParameterType.DateTime, "DateTime" + countString, msg.DateTime));
				parameters.Add(new Parameter(ParameterType.String, "Body" + countString, msg.Body));

				finalQuery = queryBuilder.AppendForBatch(finalQuery, query);

				count++;

				if(count == MaxStatementsInBatch) {
                    finalQuery = queryBuilder.BuildBatchCommand(finalQuery);
					command = builder.GetCommand(transaction, finalQuery, parameters);

                    rowsDone += ExecuteNonQuery(command, false, true, count); 

					finalQuery = "";
					count = 1;
					parameters.Clear();
				}
			}

			if(finalQuery.Length > 0) {
				command = builder.GetCommand(transaction, finalQuery, parameters);

                rowsDone += ExecuteNonQuery(command, false, true, count); 
			}

			if(rowsDone == allMessages.Count) {
				foreach(Message msg in messages) {
					IndexMessageTree(page, msg, transaction);
				}
				CommitTransaction(transaction);
				return true;
			}
			else {
				RollbackTransaction(transaction);
				return false;
			}
		}
		/// <summary>
		/// Deletes some ACL entries.
		/// </summary>
		/// <param name="entries">The entries to delete.</param>
		/// <returns><c>true</c> if one or more entries were deleted, <c>false</c> otherwise.</returns>
		private bool DeleteEntries(AclEntry[] entries) {
			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			foreach(AclEntry entry in entries) {
				string query = queryBuilder.DeleteFrom("AclEntry");
				query = queryBuilder.Where(query, "Resource", WhereOperator.Equals, "Resource");
				query = queryBuilder.AndWhere(query, "Action", WhereOperator.Equals, "Action");
				query = queryBuilder.AndWhere(query, "Subject", WhereOperator.Equals, "Subject");

				List<Parameter> parameters = new List<Parameter>(3);
				parameters.Add(new Parameter(ParameterType.String, "Resource", entry.Resource));
				parameters.Add(new Parameter(ParameterType.String, "Action", entry.Action));
				parameters.Add(new Parameter(ParameterType.String, "Subject", entry.Subject));

				DbCommand command = builder.GetCommand(transaction, query, parameters);

				if(ExecuteNonQuery(command, false) <= 0) {
					RollbackTransaction(transaction);
					return false;
				}
			}

			CommitTransaction(transaction);

			return true;
		}
		/// <summary>
		/// Cuts the recent changes if necessary.
		/// </summary>
		private void CutRecentChangesIfNecessary() {
			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.SelectCountFrom("RecentChange");

			DbCommand command = builder.GetCommand(transaction, query, new List<Parameter>());

			int rows = ExecuteScalar<int>(command, -1, false);

			int maxChanges = int.Parse(host.GetSettingValue(SettingName.MaxRecentChanges));

			if(rows > maxChanges) {
				// Remove 10% of old changes to avoid 1-by-1 deletion every time a change is made
				int entriesToDelete = maxChanges / 10;
				if(entriesToDelete > rows) entriesToDelete = rows;
				//entriesToDelete += entriesToDelete / 10;

				// This code is not optimized, but it surely works in most DBMS
				query = queryBuilder.SelectFrom("RecentChange", new string[] { "Id" });
				query = queryBuilder.OrderBy(query, new string[] { "Id" }, new Ordering[] { Ordering.Asc });

				command = builder.GetCommand(transaction, query, new List<Parameter>());

				DbDataReader reader = ExecuteReader(command);

				List<int> ids = new List<int>(entriesToDelete);

				if(reader != null) {
					while(reader.Read() && ids.Count < entriesToDelete) {
						ids.Add((int)Convert.ChangeType(reader["Id"],typeof(int)));
					}

					CloseReader(reader);
				}

				if(ids.Count > 0) {
					// Given that the IDs to delete can be many, the query is split in many chunks, each one deleting 50 items
					// This works-around the problem of too many parameters in a RPC call of Oracle
					// See also CutLog

					for(int chunk = 0; chunk <= ids.Count / MaxParametersInQuery; chunk++) {
						query = queryBuilder.DeleteFrom("RecentChange");
						List<string> parms = new List<string>(MaxParametersInQuery);
						List<Parameter> parameters = new List<Parameter>(MaxParametersInQuery);

						for(int i = chunk * MaxParametersInQuery; i < Math.Min(ids.Count, (chunk + 1) * MaxParametersInQuery); i++) {
							parms.Add("P" + i.ToString());
							parameters.Add(new Parameter(ParameterType.Int32, parms[parms.Count - 1], ids[i]));
						}

						query = queryBuilder.WhereIn(query, "Id", parms.ToArray());

						command = builder.GetCommand(transaction, query, parameters);

						if(ExecuteNonQuery(command, false) < 0) {
							RollbackTransaction(transaction);
							return;
						}
					}
				}
			}

			CommitTransaction(transaction);
		}
		/// <summary>
		/// Clears the index.
		/// </summary>
		/// <param name="state">A state object passed from the index.</param>
		private void ClearIndex(object state) {
			// state can be null, depending on when the method is called

			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("IndexWordMapping");
			query = queryBuilder.AppendForBatch(query, queryBuilder.DeleteFrom("IndexWord"));
			query = queryBuilder.AppendForBatch(query, queryBuilder.DeleteFrom("IndexDocument"));
            query= queryBuilder.BuildBatchCommand(query);

            DbCommand command = null;
            if (state == null) command = builder.GetCommand(connString, query, new List<Parameter>());
            else command = builder.GetCommand((DbTransaction)state, query, new List<Parameter>());

			ExecuteNonQuery(command, state == null);
		}
		/// <summary>
		/// Stores the outgoing links of a page, overwriting existing data.
		/// </summary>
		/// <param name="page">The full name of the page.</param>
		/// <param name="outgoingLinks">The full names of the pages that <b>page</b> links to.</param>
		/// <returns><c>true</c> if the outgoing links are stored, <c>false</c> otherwise.</returns>
		/// <exception cref="ArgumentNullException">If <b>page</b> or <b>outgoingLinks</b> are <c>null</c>.</exception>
		/// <exception cref="ArgumentException">If <b>page</b> or <b>outgoingLinks</b> are empty.</exception>
		public bool StoreOutgoingLinks(string page, string[] outgoingLinks) {
			if(page == null) throw new ArgumentNullException("page");
			if(page.Length == 0) throw new ArgumentException("Page cannot be empty", "page");
			if(outgoingLinks == null) throw new ArgumentNullException("outgoingLinks");

			foreach(string link in outgoingLinks) {
				if(link == null) throw new ArgumentNullException("outgoingLinks");
				if(link.Length == 0) throw new ArgumentException("Link cannot be empty", "outgoingLinks");
			}

			// 1. Delete old values, if any
			// 2. Store new values

			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("OutgoingLink");
			query = queryBuilder.Where(query, "Source", WhereOperator.Equals, "Source");

			List<Parameter> parameters = new List<Parameter>(1);
			parameters.Add(new Parameter(ParameterType.String, "Source", page));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			if(ExecuteNonQuery(command, false) < 0) {
				RollbackTransaction(transaction);
				return false;
			}

			foreach(string link in outgoingLinks) {
				query = queryBuilder.InsertInto("OutgoingLink", new string[] { "Source", "Destination" }, new string[] { "Source", "Destination" });

				parameters = new List<Parameter>(2);
				parameters.Add(new Parameter(ParameterType.String, "Source", page));
				parameters.Add(new Parameter(ParameterType.String, "Destination", link));

				command = builder.GetCommand(transaction, query, parameters);

				int rows = ExecuteNonQuery(command, false);

				if(rows != 1) {
					RollbackTransaction(transaction);
					return false;
				}
			}

			CommitTransaction(transaction);
			return true;
		}
		/// <summary>
		/// Removes a Category.
		/// </summary>
		/// <param name="transaction">A database transaction.</param>
		/// <param name="category">The Category to remove.</param>
		/// <returns>True if the Category has been removed successfully.</returns>
		private bool RemoveCategory(DbTransaction transaction, CategoryInfo category) {
			string nspace = null;
			string name = null;
			NameTools.ExpandFullName(category.FullName, out nspace, out name);
			if( string.IsNullOrEmpty(nspace)||string.IsNullOrEmpty(nspace.Trim())) nspace = " ";

			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("Category");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
			query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Name", name));
			parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			return rows > 0;
		}
		/// <summary>
		/// Merges two Categories.
		/// </summary>
		/// <param name="source">The source Category.</param>
		/// <param name="destination">The destination Category.</param>
		/// <returns>The correct <see cref="T:CategoryInfo" /> object.</returns>
		/// <remarks>The destination Category remains, while the source Category is deleted, and all its Pages re-bound 
		/// in the destination Category.</remarks>
		/// <exception cref="ArgumentNullException">If <paramref name="source"/> or <paramref name="destination"/> are <c>null</c>.</exception>
		public CategoryInfo MergeCategories(CategoryInfo source, CategoryInfo destination) {
			if(source == null) throw new ArgumentNullException("source");
			if(destination == null) throw new ArgumentNullException("destination");

			// 1. Check for same namespace
			// 2. Load all pages in source
			// 3. Load all pages in destination
			// 4. Merge lists in memory
			// 5. Delete all destination bindings
			// 6. Delete source cat
			// 7. Insert new bindings stored in memory

			string sourceNs = NameTools.GetNamespace(source.FullName);
			string destinationNs = NameTools.GetNamespace(destination.FullName);
			
			// If one is null and the other not null, fail
			if(sourceNs == null && destinationNs != null || sourceNs != null && destinationNs == null) return null;
			else {
				// Both non-null or both null
				if(sourceNs != null) {
					// Both non-null, check names
					NamespaceInfo tempSource = new NamespaceInfo(sourceNs, this, null);
					NamespaceInfo tempDest = new NamespaceInfo(destinationNs, this, null);
					// Different names, fail
					if(new NamespaceComparer().Compare(tempSource, tempDest) != 0) return null;
				}
				// else both null, OK
			}

			string nspace = sourceNs != null ? sourceNs : " ";

			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			CategoryInfo actualSource = GetCategory(transaction, source.FullName);
			CategoryInfo actualDestination = GetCategory(transaction, destination.FullName);

			if(actualSource == null) {
				RollbackTransaction(transaction);
				return null;
			}
			if(actualDestination == null) {
				RollbackTransaction(transaction);
				return null;
			}

			string destinationName = NameTools.GetLocalName(actualDestination.FullName);

			string[] mergedPages = MergeArrays(actualSource.Pages, actualDestination.Pages);

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("CategoryBinding");
			query = queryBuilder.Where(query, "Namespace", WhereOperator.Equals, "Namespace");
			query = queryBuilder.AndWhere(query, "Category", WhereOperator.Equals, "Category");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));
			parameters.Add(new Parameter(ParameterType.String, "Category", destinationName));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			if(rows == -1) {
				RollbackTransaction(transaction);
				return null;
			}

			if(!RemoveCategory(transaction, source)) {
				RollbackTransaction(transaction);
				return null;
			}

			string finalQuery = "";
			parameters = new List<Parameter>(MaxStatementsInBatch * 3);
			rows = 0;
			int count = 1;
			string countString;

			foreach(string page in mergedPages) {
				// This batch is executed in small chunks (MaxStatementsInBatch) to avoid exceeding DB's max batch length/size

				countString = count.ToString();

				query = queryBuilder.InsertInto("CategoryBinding", new string[] { "Namespace", "Category", "Page" },
					new string[] { "Namespace" + countString, "Category" + countString, "Page" + countString });
				finalQuery = queryBuilder.AppendForBatch(finalQuery, query);

				parameters.Add(new Parameter(ParameterType.String, "Namespace" + countString, nspace));
				parameters.Add(new Parameter(ParameterType.String, "Category" + countString, destinationName));
				parameters.Add(new Parameter(ParameterType.String, "Page" + countString, NameTools.GetLocalName(page)));

				count++;

				if(count == MaxStatementsInBatch) {
					// Batch is complete -> execute
                    finalQuery = queryBuilder.BuildBatchCommand(finalQuery);
					command = builder.GetCommand(transaction, finalQuery, parameters);
                    rows += ExecuteNonQuery(command, false, true, count);

					count = 1;
					finalQuery = "";
					parameters.Clear();
				}
			}

			if(finalQuery.Length > 0) {
				// Execute remaining queries, if any
				command = builder.GetCommand(transaction, finalQuery, parameters);
                rows += ExecuteNonQuery(command, false, true, count);
			}

			if(rows == mergedPages.Length) {
				CommitTransaction(transaction);
				CategoryInfo result = new CategoryInfo(actualDestination.FullName, this);
				result.Pages = mergedPages;
				return result;
			}
			else {
				RollbackTransaction(transaction);
				return null;
			}
		}
		/// <summary>
		/// Deletes a directory and all its contents.
		/// </summary>
		/// <param name="transaction">The current transaction to use.</param>
		/// <param name="fullPath">The full path of the directory.</param>
		/// <returns><c>true</c> if the directory is deleted, <c>false</c> otherwise.</returns>
		private bool DeleteDirectory(DbTransaction transaction, string fullPath) {
			string[] dirs = ListDirectories(transaction, fullPath);
			foreach(string dir in dirs) {
				if(!DeleteDirectory(transaction, dir)) {
					return false;
				}
			}

			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("Directory");
			query = queryBuilder.Where(query, "FullPath", WhereOperator.Equals, "FullPath");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "FullPath", fullPath));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			return rows > 0;
		}
		/// <summary>
		/// Removes a namespace.
		/// </summary>
		/// <param name="nspace">The namespace to remove.</param>
		/// <returns><c>true</c> if the namespace is removed, <c>false</c> otherwise.</returns>
		/// <exception cref="ArgumentNullException">If <paramref name="nspace"/> is <c>null</c>.</exception>
		public bool RemoveNamespace(NamespaceInfo nspace) {
			if( nspace==null) throw new ArgumentNullException("nspace");

			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			foreach(PageInfo page in GetPages(transaction, nspace)) {
				PageContent content = GetContent(transaction, page, CurrentRevision);
				UnindexPage(content, transaction);
				foreach(Message msg in GetMessages(transaction, page)) {
					UnindexMessageTree(page, msg, transaction);
				}
			}

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("Namespace");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");

			List<Parameter> parameters = new List<Parameter>(1);
			parameters.Add(new Parameter(ParameterType.String, "Name", nspace.Name));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);
			if(rows > 0) CommitTransaction(transaction);
			else RollbackTransaction(transaction);

			return rows > 0;
		}
		/// <summary>
		/// Deletes a File.
		/// </summary>
		/// <param name="fullName">The full name of the File.</param>
		/// <returns><c>true</c> if the File is deleted, <c>false</c> otherwise.</returns>
		/// <exception cref="ArgumentNullException">If <paramref name="fullName"/> is <c>null</c>.</exception>
		/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty or it does not exist.</exception>
		public bool DeleteFile(string fullName) {
			if(fullName == null) throw new ArgumentNullException("fullName");
			if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");

			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			if(!FileExists(transaction, fullName)) {
				RollbackTransaction(transaction);
				throw new ArgumentException("File does not exist", "fullName");
			}

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string directory, filename;
			SplitFileFullName(fullName, out directory, out filename);

			string query = queryBuilder.DeleteFrom("File");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
			query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "Directory");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Name", filename));
			parameters.Add(new Parameter(ParameterType.String, "Directory", directory));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);
			if(rows == 1) CommitTransaction(transaction);
			else RollbackTransaction(transaction);

			return rows == 1;
		}
		/// <summary>
		/// Deletes a Page Attachment.
		/// </summary>
		/// <param name="pageInfo">The Page Info that owns the Attachment.</param>
		/// <param name="name">The name of the Attachment, for example "myfile.jpg".</param>
		/// <returns><c>true</c> if the Attachment is deleted, <c>false</c> otherwise.</returns>
		/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/> or <paramref name="name"/> are <c>null</c>.</exception>
		/// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if the page or attachment do not exist.</exception>
		public bool DeletePageAttachment(PageInfo pageInfo, string name) {
			if(pageInfo == null) throw new ArgumentNullException("pageInfo");
			if(name == null) throw new ArgumentNullException("name");
			if(name.Length == 0) throw new ArgumentException("Name cannot be empty");

			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			if(!AttachmentExists(transaction, pageInfo, name)) {
				RollbackTransaction(transaction);
				throw new ArgumentException("Attachment does not exist", "name");
			}

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("Attachment");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
			query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Name", name));
			parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);
			if(rows == 1) CommitTransaction(transaction);
			else RollbackTransaction(transaction);

			return rows == 1;
		}
		/// <summary>
		/// Removes a user data element.
		/// </summary>
		/// <param name="transaction">A database transaction.</param>
		/// <param name="username">The username.</param>
		/// <param name="key">The key.</param>
		/// <returns><c>true</c> if the data element is removed, <c>false</c> otherwise.</returns>
		private bool RemoveUserData(DbTransaction transaction, string username, string key) {
			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("UserData");
			query = queryBuilder.Where(query, "User", WhereOperator.Equals, "Username");
			query = queryBuilder.AndWhere(query, "Key", WhereOperator.Equals, "Key");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Username", username));
			parameters.Add(new Parameter(ParameterType.String, "Key", key));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			return rows != -1; // Success also if no elements are removed
		}
		/// <summary>
		/// Removes a Navigation Path.
		/// </summary>
		/// <param name="connection">A database connection.</param>
		/// <param name="path">The navigation path to remove.</param>
		/// <returns><c>true</c> if the path is removed, <c>false</c> otherwise.</returns>
		private bool RemoveNavigationPath(DbConnection connection, NavigationPath path) {
			string nspace, name;
			NameTools.ExpandFullName(path.FullName, out nspace, out name);
			if( string.IsNullOrEmpty(nspace)||string.IsNullOrEmpty(nspace.Trim())) nspace = " ";

			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("NavigationPath");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
			query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Name", name));
			parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));

			DbCommand command = builder.GetCommand(connection, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			return rows > 0;
		}
		/// <summary>
		/// Deletes a revision of a page content.
		/// </summary>
		/// <param name="connection">A database connection.</param>
		/// <param name="page">The page.</param>
		/// <param name="revision">The revision.</param>
		/// <returns><c>true</c> if the content ir deleted, <c>false</c> otherwise.</returns>
		private bool DeleteContent(DbConnection connection, PageInfo page, int revision) {
			string name, nspace;
			NameTools.ExpandFullName(page.FullName, out nspace, out name);
			if( string.IsNullOrEmpty(nspace)||string.IsNullOrEmpty(nspace.Trim())) nspace = " ";

			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("PageContent");
			query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
			query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");
			query = queryBuilder.AndWhere(query, "Revision", WhereOperator.Equals, "Revision");

			List<Parameter> parameters = new List<Parameter>(3);
			parameters.Add(new Parameter(ParameterType.String, "Page", name));
			parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));
			parameters.Add(new Parameter(ParameterType.String, "Revision", revision));

			DbCommand command = builder.GetCommand(connection, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			return rows > 0;
		}
		/// <summary>
		/// Removes a content template.
		/// </summary>
		/// <param name="transaction">A database transaction.</param>
		/// <param name="name">The name of the template to remove.</param>
		/// <returns><c>true</c> if the template is removed, <c>false</c> otherwise.</returns>
		private bool RemoveContentTemplate(DbTransaction transaction, string name) {
			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("ContentTemplate");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");

			List<Parameter> parameters = new List<Parameter>(1);
			parameters.Add(new Parameter(ParameterType.String, "Name", name));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			return rows == 1;
		}
		/// <summary>
		/// Deletes all data associated to a document.
		/// </summary>
		/// <param name="document">The document.</param>
		/// <param name="state">A state object passed from the index (can be <c>null</c> or a <see cref="T:DbTransaction" />).</param>
		private void DeleteDataForDocument(IDocument document, object state) {
			// 1. Delete all data related to a document
			// 2. Delete all words that have no more mappings

			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("IndexDocument");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "DocName");
			List<Parameter> parameters = new List<Parameter>(1);
			parameters.Add(new Parameter(ParameterType.String, "DocName", document.Name));

			string subQuery = queryBuilder.SelectFrom("IndexWordMapping", new string[] { "Word" });
			subQuery = queryBuilder.GroupBy(subQuery, new string[] { "Word" });
			string query2 = queryBuilder.DeleteFrom("IndexWord");
			query2 = queryBuilder.WhereNotInSubquery(query2, "IndexWord", "Id", subQuery);

			query = queryBuilder.AppendForBatch(query, query2);
            query = queryBuilder.BuildBatchCommand(query);
			DbCommand command = null;
			if(state != null) command = builder.GetCommand((DbTransaction)state, query, parameters);
			else command = builder.GetCommand(connString, query, parameters);

			// Close only if state is null
			ExecuteNonQuery(command, state == null);
		}
		/// <summary>
		/// Stores the value of a Setting.
		/// </summary>
		/// <param name="name">The name of the Setting.</param>
		/// <param name="value">The value of the Setting. Value cannot contain CR and LF characters, which will be removed.</param>
		/// <returns>True if the Setting is stored, false otherwise.</returns>
		/// <remarks>This method stores the Value immediately.</remarks>
		public bool SetSetting(string name, string value) {
			if(name == null) throw new ArgumentNullException("name");
			if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");

			// 1. Delete old value, if any
			// 2. Store new value

            // Nulls and empty strings are converted to " "
			if(string.IsNullOrEmpty(value)) value = " ";

			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("Setting");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");

			List<Parameter> parameters = new List<Parameter>(1);
			parameters.Add(new Parameter(ParameterType.String, "Name", name));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			if(rows == -1) {
				RollbackTransaction(transaction);
				return false; // Deletion command failed (0-1 are OK)
			}

			query = queryBuilder.InsertInto("Setting",
				new string[] { "Name", "Value" }, new string[] { "Name", "Value" });
			parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Name", name));
			parameters.Add(new Parameter(ParameterType.String, "Value", value));

			command = builder.GetCommand(transaction, query, parameters);

			rows = ExecuteNonQuery(command, false);

			if(rows == 1) CommitTransaction(transaction);
			else RollbackTransaction(transaction);

			return rows == 1;
		}
		/// <summary>
		/// Deletes the Backups of a Page, up to a specified revision.
		/// </summary>
		/// <param name="page">The Page to delete the backups of.</param>
		/// <param name="revision">The newest revision to delete (newer revision are kept) o -1 to delete all the Backups.</param>
		/// <returns><c>true</c> if the deletion succeeded, <c>false</c> otherwise.</returns>
		/// <exception cref="ArgumentNullException">If <paramref name="page"/> is <c>null</c>.</exception>
		/// <exception cref="ArgumentOutOfRangeException">If <paramref name="revision"/> is less than -1.</exception>
		public bool DeleteBackups(PageInfo page, int revision) {
			if(page == null) throw new ArgumentNullException("page");
			if(revision < -1) throw new ArgumentOutOfRangeException("revision", "Invalid Revision");

			// 1. Retrieve target content (revision-1 = first kept revision)
			// 2. Replace the current content (delete, store)
			// 3. Delete all older revisions up to the specified on (included) "N-m...N"
			// 4. Re-number remaining revisions starting from FirstRevision (zero) to revision-1 (don't re-number revs -1, -100)

			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			if(GetPage(transaction, page.FullName) == null) {
				RollbackTransaction(transaction);
				return false;
			}

			int[] baks = GetBackups(transaction, page);
			if(baks.Length > 0 && revision > baks[baks.Length - 1]) {
				RollbackTransaction(transaction);
				return true;
			}

			string nspace, name;
			NameTools.ExpandFullName(page.FullName, out nspace, out name);
			if( string.IsNullOrEmpty(nspace)||string.IsNullOrEmpty(nspace.Trim())) nspace = " ";

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("PageContent");
			query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
			query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");
			if(revision != -1) query = queryBuilder.AndWhere(query, "Revision", WhereOperator.LessThanOrEqualTo, "Revision");
			query = queryBuilder.AndWhere(query, "Revision", WhereOperator.GreaterThanOrEqualTo, "FirstRevision");

			List<Parameter> parameters = new List<Parameter>(4);
			parameters.Add(new Parameter(ParameterType.String, "Page", name));
			parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));
			if(revision != -1) parameters.Add(new Parameter(ParameterType.Int16, "Revision", revision));
			parameters.Add(new Parameter(ParameterType.Int16, "FirstRevision", FirstRevision));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			if(rows == -1) {
				RollbackTransaction(transaction);
				return false;
			}

			if(revision != -1) {
				int revisionDelta = revision + 1;

				query = queryBuilder.UpdateIncrement("PageContent", "Revision", -revisionDelta);
				query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
				query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");
				query = queryBuilder.AndWhere(query, "Revision", WhereOperator.GreaterThanOrEqualTo, "FirstRevision");

				parameters = new List<Parameter>(3);
				parameters.Add(new Parameter(ParameterType.String, "Page", name));
				parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));
				parameters.Add(new Parameter(ParameterType.Int16, "FirstRevision", FirstRevision));

				command = builder.GetCommand(transaction, query, parameters);

				rows = ExecuteNonQuery(command, false);

				if(rows > 0) CommitTransaction(transaction);
				else RollbackTransaction(transaction);

				return rows >= 0;
			}
			else {
				CommitTransaction(transaction);
				return true;
			}
		}
		/// <summary>
		/// Deletes the outgoing links of a page and all the target links that include the page.
		/// </summary>
		/// <param name="page">The full name of the page.</param>
		/// <returns><c>true</c> if the links are deleted, <c>false</c> otherwise.</returns>
		/// <exception cref="ArgumentNullException">If <b>page</b> is <c>null</c>.</exception>
		/// <exception cref="ArgumentException">If <b>page</b> is empty.</exception>
		public bool DeleteOutgoingLinks(string page) {
			if(page == null) throw new ArgumentNullException("page");
			if(page.Length == 0) throw new ArgumentException("Page cannot be empty", "page");

			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("OutgoingLink");
			query = queryBuilder.Where(query, "Source", WhereOperator.Equals, "Source");
			query = queryBuilder.OrWhere(query, "Destination", WhereOperator.Equals, "Destination");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Source", page));
			parameters.Add(new Parameter(ParameterType.String, "Destination", page));

			DbCommand command = builder.GetCommand(connString, query, parameters);

			int rows = ExecuteNonQuery(command);

			return rows > 0;
		}
		/// <summary>
		/// Removes a Page.
		/// </summary>
		/// <param name="page">The Page to remove.</param>
		/// <returns>True if the Page is removed successfully.</returns>
		/// <exception cref="ArgumentNullException">If <paramref name="page"/> is <c>null</c>.</exception>
		public bool RemovePage(PageInfo page) {
			if(page == null) throw new ArgumentNullException("page");

			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			if(IsDefaultPage(transaction, page)) {
				RollbackTransaction(transaction);
				return false;
			}

			PageContent currentContent = GetContent(transaction, page, CurrentRevision);
			if(currentContent != null) {
				UnindexPage(currentContent, transaction);
				foreach(Message msg in GetMessages(transaction, page)) {
					UnindexMessageTree(page, msg, transaction);
				}
			}

			RebindPage(transaction, page, new string[0]);

			string nspace, name;
			NameTools.ExpandFullName(page.FullName, out nspace, out name);
			if( string.IsNullOrEmpty(nspace)||string.IsNullOrEmpty(nspace.Trim())) nspace = " ";

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("Page");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
			query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Name", name));
			parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			if(rows > 0) CommitTransaction(transaction);
			else RollbackTransaction(transaction);

			return rows > 0;
		}
		/// <summary>
		/// Sets a meta-data items' content.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <param name="tag">The tag that specifies the context (usually the namespace).</param>
		/// <param name="content">The content.</param>
		/// <returns><c>true</c> if the content is set, <c>false</c> otherwise.</returns>
		public bool SetMetaDataItem(MetaDataItem item, string tag, string content) {
			if(string.IsNullOrEmpty(tag)) tag = " ";
			if(string.IsNullOrEmpty(content)) content = " ";

			// 1. Delete old value, if any
			// 2. Store new value

			ICommandBuilder builder = GetCommandBuilder();
			DbConnection connection = builder.GetConnection(connString);
			DbTransaction transaction = BeginTransaction(connection);

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("MetaDataItem");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
			query = queryBuilder.AndWhere(query, "Tag", WhereOperator.Equals, "Tag");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Name", item.ToString()));
			parameters.Add(new Parameter(ParameterType.String, "Tag", tag));

			DbCommand command = builder.GetCommand(transaction, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			if(rows == -1) {
				RollbackTransaction(transaction);
				return false;
			}

			query = queryBuilder.InsertInto("MetaDataItem", new string[] { "Name", "Tag", "Data" }, new string[] { "Name", "Tag", "Content" });

			parameters = new List<Parameter>(3);
			parameters.Add(new Parameter(ParameterType.String, "Name", item.ToString()));
			parameters.Add(new Parameter(ParameterType.String, "Tag", tag));
			parameters.Add(new Parameter(ParameterType.String, "Content", content));

			command = builder.GetCommand(transaction, query, parameters);

			rows = ExecuteNonQuery(command, false);

			if(rows == 1) CommitTransaction(transaction);
			else RollbackTransaction(transaction);

			return rows == 1;
		}
		/// <summary>
		/// Binds a Page with one or more Categories.
		/// </summary>
		/// <param name="connection">A database connection.</param>
		/// <param name="page">The Page to bind.</param>
		/// <param name="categories">The Categories to bind the Page with.</param>
		/// <returns>True if the binding succeeded.</returns>
		/// <remarks>After a successful operation, the Page is bound with all and only the categories passed as argument.</remarks>
		private bool RebindPage(DbConnection connection, PageInfo page, string[] categories) {
			string nspace, name;
			NameTools.ExpandFullName(page.FullName, out nspace, out name);
			if( string.IsNullOrEmpty(nspace)||string.IsNullOrEmpty(nspace.Trim())) nspace = " ";

			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("CategoryBinding");
			query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
			query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");

			List<Parameter> parameters = new List<Parameter>(2);
			parameters.Add(new Parameter(ParameterType.String, "Page", name));
			parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));

			DbCommand command = builder.GetCommand(connection, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			if(rows < 0) return false;

			if(categories.Length > 0) {
				string finalQuery = "";
				parameters = new List<Parameter>(categories.Length * 3);
				int count = 0;
				string countString;

				foreach(string cat in categories) {
					countString = count.ToString();

					query = queryBuilder.InsertInto("CategoryBinding", new string[] { "Namespace", "Category", "Page" },
						new string[] { "Namespace" + countString, "Category" + countString, "Page" + countString });
					finalQuery = queryBuilder.AppendForBatch(finalQuery, query);

					parameters.Add(new Parameter(ParameterType.String, "Namespace" + countString, nspace));
					parameters.Add(new Parameter(ParameterType.String, "Category" + countString, NameTools.GetLocalName(cat)));
					parameters.Add(new Parameter(ParameterType.String, "Page" + countString, name));

					count++;
				}
                finalQuery = queryBuilder.BuildBatchCommand(finalQuery);
				command = builder.GetCommand(connection, finalQuery, parameters);

                rows = ExecuteNonQuery(command, false, true, count);

				return rows == categories.Length;
			}
			else return true;
		}
		/// <summary>
		/// Removes a plugin's assembly.
		/// </summary>
		/// <param name="connection">A database connection.</param>
		/// <param name="filename">The file name of the assembly to remove, such as "Assembly.dll".</param>
		/// <returns><c>true</c> if the assembly is removed, <c>false</c> otherwise.</returns>
		private bool DeletePluginAssembly(DbConnection connection, string filename) {
			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("PluginAssembly");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");

			List<Parameter> parameters = new List<Parameter>(1);
			parameters.Add(new Parameter(ParameterType.String, "Name", filename));

			DbCommand command = builder.GetCommand(connection, query, parameters);

			int rows = ExecuteNonQuery(command, false);

			return rows == 1;
		}
		/// <summary>
		/// Removes a user group.
		/// </summary>
		/// <param name="group">The group to remove.</param>
		/// <returns><c>true</c> if the group is removed, <c>false</c> otherwise.</returns>
		/// <exception cref="ArgumentNullException">If <b>group</b> is <c>null</c>.</exception>
		public bool RemoveUserGroup(UserGroup group) {
			if(group == null) throw new ArgumentNullException("group");

			ICommandBuilder builder = GetCommandBuilder();
			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.DeleteFrom("UserGroup");
			query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");

			List<Parameter> parameters = new List<Parameter>(1);
			parameters.Add(new Parameter(ParameterType.String, "Name", group.Name));

			DbCommand command = builder.GetCommand(connString, query, parameters);

			int rows = ExecuteNonQuery(command);

			return rows == 1;
		}