/// <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>
		/// Retrieves a File.
		/// </summary>
		/// <param name="fullName">The full name of the File.</param>
		/// <param name="destinationStream">A Stream object used as <b>destination</b> of a byte stream,
		/// i.e. the method writes to the Stream the file content.</param>
		/// <param name="countHit">A value indicating whether or not to count this retrieval in the statistics.</param>
		/// <returns><c>true</c> if the file is retrieved, <c>false</c> otherwise.</returns>
		/// <exception cref="ArgumentNullException">If <paramref name="fullName"/> or <paramref name="destinationStream"/> are <c>null</c>.</exception>
		/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty or <paramref name="destinationStream"/> does not support writing.</exception>
		public bool RetrieveFile(string fullName, System.IO.Stream destinationStream, bool countHit) {
			if(fullName == null) throw new ArgumentNullException("fullName");
			if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");
			if(destinationStream == null) throw new ArgumentNullException("destinationStream");
			if(!destinationStream.CanWrite) throw new ArgumentException("Cannot write into Destination Stream", "destinationStream");

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

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

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

			QueryBuilder queryBuilder = new QueryBuilder(builder);

			string query = queryBuilder.SelectFrom("File", new string[] { "Size", "Data" });
			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);

			DbDataReader reader = ExecuteReader(command);

			if(reader != null) {
				bool done = false;

				if(reader.Read()) {
					int read = ReadBinaryColumn(reader, "Data", destinationStream);
					done = (long)read == (long)Convert.ChangeType(reader["Size"],typeof(long));
				}

				CloseReader(reader);

				if(!done) {
					RollbackTransaction(transaction);
					return false;
				}
			}
			else {
				RollbackTransaction(transaction);
				return false;
			}

			if(countHit) {
				// Update download count
				query = queryBuilder.UpdateIncrement("File", "Downloads", 1);
				query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
				query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "Directory");

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

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

				int rows = ExecuteNonQuery(command, false);
				if(rows != 1) {
					RollbackTransaction(transaction);
					return false;
				}
			}

			CommitTransaction(transaction);

			return true;
		}