AndWhere() public méthode

Adds another WHERE clause to a query.
public AndWhere ( string query, string column, WhereOperator op, string parameter ) : string
query string The query.
column string The column subject of the WHERE clause.
op WhereOperator The operator.
parameter string The name of the parameter for the WHERE clause.
Résultat string
Exemple #1
0
        /// <summary>
        /// Releases the lock.
        /// </summary>
        public override void Release()
        {
            ICommandBuilder builder     = _sqlStorageProviderUtility.GetCommandBuilder2();
            DbConnection    connection  = builder.GetConnection(_connString);
            DbTransaction   transaction = _sqlStorageProviderUtility.BeginTransaction(connection);

            QueryBuilder queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.DeleteFrom("SearchIndexLock");

            query = queryBuilder.Where(query, "Wiki", WhereOperator.Equals, "Wiki");
            query = queryBuilder.AndWhere(query, "Name", WhereOperator.Equals, "Name");

            List <Parameter> parameters = new List <Parameter>(2);

            parameters.Add(new Parameter(ParameterType.String, "Wiki", _wiki));
            parameters.Add(new Parameter(ParameterType.String, "Name", _name));

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

            int rows = _sqlStorageProviderUtility.ExecuteNonQuery(command, false);

            if (rows == 1)
            {
                _sqlStorageProviderUtility.CommitTransaction(transaction);
            }
            else
            {
                _sqlStorageProviderUtility.RollbackTransaction(transaction);
            }
        }
Exemple #2
0
        public override void RenameFile(string from, string to)
        {
            ICommandBuilder builder     = _sqlStorageProviderUtility.GetCommandBuilder2();
            DbConnection    connection  = builder.GetConnection(_connString);
            DbTransaction   transaction = _sqlStorageProviderUtility.BeginTransaction(connection);

            QueryBuilder queryBuilder = new QueryBuilder(builder);

            if (!FileExists(transaction, from))
            {
                _sqlStorageProviderUtility.RollbackTransaction(transaction);
                throw new ArgumentException("File does not exist", "from");
            }
            if (FileExists(transaction, to))
            {
                _sqlStorageProviderUtility.RollbackTransaction(transaction);
                throw new ArgumentException("File already exists", "to");
            }

            string query = queryBuilder.Update("SearchIndex", new string[] { "Name" }, new string[] { "NewName" });

            query = queryBuilder.Where(query, "Wiki", WhereOperator.Equals, "Wiki");
            query = queryBuilder.AndWhere(query, "Name", WhereOperator.Equals, "OldName");

            List <Parameter> parameters = new List <Parameter>(3);

            parameters.Add(new Parameter(ParameterType.String, "Wiki", _wiki));
            parameters.Add(new Parameter(ParameterType.String, "NewName", to));
            parameters.Add(new Parameter(ParameterType.String, "OldName", from));

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

            int rows = _sqlStorageProviderUtility.ExecuteNonQuery(command, false);

            if (rows != 1)
            {
                _sqlStorageProviderUtility.RollbackTransaction(transaction);
            }
            else
            {
                _sqlStorageProviderUtility.CommitTransaction(transaction);

                // we delete and force a redownload, since we can't do this in an atomic way
                if (_cacheDirectory.FileExists(from))
                {
                    _cacheDirectory.DeleteFile(from);
                }

                // drop old cached data as it's wrong now
                if (_cacheDirectory.FileExists(from + ".blob"))
                {
                    _cacheDirectory.DeleteFile(from + ".blob");
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Obtains the lock.
        /// </summary>
        /// <returns><c>true</c> if the lock has been obtained, <c>false</c> otherwise.</returns>
        public override bool Obtain()
        {
            ICommandBuilder builder     = _sqlStorageProviderUtility.GetCommandBuilder2();
            DbConnection    connection  = builder.GetConnection(_connString);
            DbTransaction   transaction = _sqlStorageProviderUtility.BeginTransaction(connection);

            QueryBuilder queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.DeleteFrom("SearchIndexLock");

            query = queryBuilder.Where(query, "Wiki", WhereOperator.Equals, "Wiki");
            query = queryBuilder.AndWhere(query, "Name", WhereOperator.Equals, "Name");

            List <Parameter> parameters = new List <Parameter>(2);

            parameters.Add(new Parameter(ParameterType.String, "Wiki", _wiki));
            parameters.Add(new Parameter(ParameterType.String, "Name", _name));

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

            int rows = _sqlStorageProviderUtility.ExecuteNonQuery(command, false);

            if (rows != 0)
            {
                _sqlStorageProviderUtility.RollbackTransaction(transaction);
                return(false);                // Deletion command failed (0 is the only accepted value)
            }

            query = queryBuilder.InsertInto("SearchIndexLock",
                                            new string[] { "wiki", "Name", "Value" }, new string[] { "Wiki", "Name", "Value" });
            parameters = new List <Parameter>(3);
            parameters.Add(new Parameter(ParameterType.String, "Wiki", _wiki));
            parameters.Add(new Parameter(ParameterType.String, "Name", _name));
            parameters.Add(new Parameter(ParameterType.String, "Value", "locked"));

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

            rows = _sqlStorageProviderUtility.ExecuteNonQuery(command, false);

            if (rows == 1)
            {
                _sqlStorageProviderUtility.CommitTransaction(transaction);
            }
            else
            {
                _sqlStorageProviderUtility.RollbackTransaction(transaction);
            }

            return(rows == 1);
        }
Exemple #4
0
        /// <summary>
        /// Deletes a file.
        /// </summary>
        /// <param name="name">The name of the file to be deleted.</param>
        public override void DeleteFile(string name)
        {
            ICommandBuilder builder     = _sqlStorageProviderUtility.GetCommandBuilder2();
            DbConnection    connection  = builder.GetConnection(_connString);
            DbTransaction   transaction = _sqlStorageProviderUtility.BeginTransaction(connection);

            if (!FileExists(transaction, name))
            {
                _sqlStorageProviderUtility.RollbackTransaction(transaction);
                return;
            }

            QueryBuilder queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.DeleteFrom("SearchIndex");

            query = queryBuilder.Where(query, "Wiki", WhereOperator.Equals, "Wiki");
            query = queryBuilder.AndWhere(query, "Name", WhereOperator.Equals, "Name");

            List <Parameter> parameters = new List <Parameter>(2);

            parameters.Add(new Parameter(ParameterType.String, "Wiki", _wiki));
            parameters.Add(new Parameter(ParameterType.String, "Name", name));

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

            int rows = _sqlStorageProviderUtility.ExecuteNonQuery(command, false);

            if (rows != 1)
            {
                _sqlStorageProviderUtility.RollbackTransaction(transaction);
            }
            else
            {
                _sqlStorageProviderUtility.CommitTransaction(transaction);

                if (_cacheDirectory.FileExists(name + ".blob"))
                {
                    _cacheDirectory.DeleteFile(name + ".blob");
                }

                if (_cacheDirectory.FileExists(name))
                {
                    _cacheDirectory.DeleteFile(name);
                }
            }
        }
Exemple #5
0
        private bool FileExists(DbTransaction transaction, string name)
        {
            ICommandBuilder builder      = _sqlStorageProviderUtility.GetCommandBuilder2();
            QueryBuilder    queryBuilder = new QueryBuilder(builder);

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

            query = queryBuilder.Where(query, "Wiki", WhereOperator.Equals, "Wiki");
            query = queryBuilder.AndWhere(query, "Name", WhereOperator.Equals, "Name");

            List <Parameter> parameters = new List <Parameter>(2);

            parameters.Add(new Parameter(ParameterType.String, "Wiki", _wiki));
            parameters.Add(new Parameter(ParameterType.String, "Name", name));

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

            int count = _sqlStorageProviderUtility.ExecuteScalar <int>(command, -1, false);

            return(count == 1);
        }
Exemple #6
0
        /// <summary>
        /// Determines whether this instance is locked.
        /// </summary>
        /// <returns><c>true</c> if this instance is locked; otherwise, <c>false</c>.</returns>
        public override bool IsLocked()
        {
            ICommandBuilder builder      = _sqlStorageProviderUtility.GetCommandBuilder2();
            QueryBuilder    queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.SelectFrom("SearchIndexLock");

            query = queryBuilder.Where(query, "Wiki", WhereOperator.Equals, "Wiki");
            query = queryBuilder.AndWhere(query, "Name", WhereOperator.Equals, "Name");

            List <Parameter> parameters = new List <Parameter>(2);

            parameters.Add(new Parameter(ParameterType.String, "Wiki", _wiki));
            parameters.Add(new Parameter(ParameterType.String, "Name", _name));

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

            DbDataReader reader = _sqlStorageProviderUtility.ExecuteReader(command);

            if (reader != null)
            {
                string result = null;

                if (reader.Read())
                {
                    result = reader["Value"] as string;
                }

                _sqlStorageProviderUtility.CloseReader(command, reader);

                return(result == "locked");
            }
            else
            {
                return(false);
            }
        }
Exemple #7
0
        /// <summary>
        /// Get last time a file has been modified.
        /// </summary>
        /// <param name="name">The name of the file.</param>
        /// <returns>Last modified time.</returns>
        public override long FileModified(string name)
        {
            ICommandBuilder builder      = _sqlStorageProviderUtility.GetCommandBuilder2();
            QueryBuilder    queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.SelectFrom("SearchIndex", new string[] { "LastModified" });

            query = queryBuilder.Where(query, "Wiki", WhereOperator.Equals, "Wiki");
            query = queryBuilder.AndWhere(query, "Name", WhereOperator.Equals, "Name");

            List <Parameter> parameters = new List <Parameter>(3);

            parameters.Add(new Parameter(ParameterType.String, "Wiki", _wiki));
            parameters.Add(new Parameter(ParameterType.String, "Name", name));

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

            DbDataReader reader = _sqlStorageProviderUtility.ExecuteReader(command);

            if (reader != null)
            {
                long lastModified = 0;

                if (reader.Read())
                {
                    lastModified = ((DateTime)reader["LastModified"]).Ticks;
                }

                _sqlStorageProviderUtility.CloseReader(command, reader);

                return(lastModified);
            }
            else
            {
                return(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(nspace == null) 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>
        /// Gets the Backup/Revision numbers of a Page.
        /// </summary>
        /// <param name="connection">A database connection.</param>
        /// <param name="page">The Page to get the Backups of.</param>
        /// <returns>The Backup/Revision numbers.</returns>
        private int[] GetBackups(DbConnection connection, PageInfo page)
        {
            if(GetPage(connection, page.FullName) == null) {
                return null;
            }

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

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

            string query = queryBuilder.SelectFrom("PageContent", new string[] { "Revision" });
            query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
            query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");
            query = queryBuilder.AndWhere(query, "Revision", WhereOperator.GreaterThanOrEqualTo, "Revision");
            query = queryBuilder.OrderBy(query, new[] { "Revision" }, new[] { Ordering.Asc });

            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, "Revision", FirstRevision));

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

            DbDataReader reader = ExecuteReader(command);

            if(reader != null) {
                List<int> result = new List<int>(100);

                while(reader.Read()) {
                    result.Add((short)reader["Revision"]);
                }

                CloseReader(reader);

                return result.ToArray();
            }
            else return null;
        }
        /// <summary>
        /// Sets the number of times a file was retrieved.
        /// </summary>
        /// <param name="fullName">The full name of the file.</param>
        /// <param name="count">The count to set.</param>
        /// <exception cref="ArgumentNullException">If <paramref name="fullName"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty.</exception>
        /// <exception cref="ArgumentOutOfRangeException">If <paramref name="count"/> is less than zero.</exception>
        public void SetFileRetrievalCount(string fullName, int count)
        {
            if(fullName == null) throw new ArgumentNullException("fullName");
            if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");
            if(count < 0) throw new ArgumentOutOfRangeException("count", "Count must be greater than or equal to zero");

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

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

            string query = queryBuilder.Update("File", new string[] { "Downloads" }, new string[] { "Downloads" });
            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));
            parameters.Add(new Parameter(ParameterType.Int32, "Downloads", count));

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

            ExecuteNonQuery(command);
        }
        /// <summary>
        /// Stores 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>
        /// <param name="sourceStream">A Stream object used as <b>source</b> of a byte stream,
        /// i.e. the method reads from the Stream and stores the content properly.</param>
        /// <param name="overwrite"><c>true</c> to overwrite an existing Attachment.</param>
        /// <returns><c>true</c> if the Attachment is stored, <c>false</c> otherwise.</returns>
        /// <remarks>If <b>overwrite</b> is <c>false</c> and Attachment already exists, the method returns <c>false</c>.</remarks>
        /// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/>, <paramref name="name"/> or <paramref name="sourceStream"/> are <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if <paramref name="sourceStream"/> does not support reading.</exception>
        public bool StorePageAttachment(PageInfo pageInfo, string name, System.IO.Stream sourceStream, bool overwrite)
        {
            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", "name");
            if(sourceStream == null) throw new ArgumentNullException("sourceStream");
            if(!sourceStream.CanRead) throw new ArgumentException("Cannot read from Source Stream", "sourceStream");

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

            bool attachmentExists = AttachmentExists(transaction, pageInfo, name);

            if(attachmentExists && !overwrite) {
                RollbackTransaction(transaction);
                return false;
            }

            // To achieve decent performance, an UPDATE query is issued if the attachment exists,
            // otherwise an INSERT query is issued

            QueryBuilder queryBuilder = new QueryBuilder(builder);

            string query;
            List<Parameter> parameters;

            byte[] attachmentData = null;
            int size = Tools.ReadStream(sourceStream, ref attachmentData, MaxFileSize);
            if(size < 0) {
                RollbackTransaction(transaction);
                throw new ArgumentException("Source Stream contains too much data", "sourceStream");
            }

            if(attachmentExists) {
                query = queryBuilder.Update("Attachment", new string[] { "Size", "LastModified", "Data" }, new string[] { "Size", "LastModified", "Data" });
                query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
                query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");

                parameters = new List<Parameter>(5);
                parameters.Add(new Parameter(ParameterType.Int64, "Size", (long)size));
                parameters.Add(new Parameter(ParameterType.DateTime, "LastModified", DateTime.Now));
                parameters.Add(new Parameter(ParameterType.ByteArray, "Data", attachmentData));
                parameters.Add(new Parameter(ParameterType.String, "Name", name));
                parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
            }
            else {
                query = queryBuilder.InsertInto("Attachment", new string[] { "Name", "Page", "Size", "Downloads", "LastModified", "Data" },
                    new string[] { "Name", "Page", "Size", "Downloads", "LastModified", "Data" });

                parameters = new List<Parameter>(6);
                parameters.Add(new Parameter(ParameterType.String, "Name", name));
                parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
                parameters.Add(new Parameter(ParameterType.Int64, "Size", (long)size));
                parameters.Add(new Parameter(ParameterType.Int32, "Downloads", 0));
                parameters.Add(new Parameter(ParameterType.DateTime, "LastModified", DateTime.Now));
                parameters.Add(new Parameter(ParameterType.ByteArray, "Data", attachmentData));
            }

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

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

            return rows == 1;
        }
        /// <summary>
        /// Gets the content of a specific revision of a page.
        /// </summary>
        /// <param name="transaction">A database transaction.</param>
        /// <param name="page">The page.</param>
        /// <param name="revision">The revision.</param>
        /// <returns>The content.</returns>
        private PageContent GetContent(DbTransaction transaction, PageInfo page, int revision)
        {
            // Internal version to work with GetContent, GetBackupContent, GetDraft

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

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

            string query = queryBuilder.SelectFrom("PageContent", "PageKeyword", new string[] { "Page", "Namespace", "Revision" }, new string[] { "Page", "Namespace", "Revision" }, Join.LeftJoin,
                new string[] { "Title", "User", "LastModified", "Comment", "Content", "Description" }, new string[] { "Keyword" });
            query = queryBuilder.Where(query, "PageContent", "Page", WhereOperator.Equals, "Page");
            query = queryBuilder.AndWhere(query, "PageContent", "Namespace", WhereOperator.Equals, "Namespace");
            query = queryBuilder.AndWhere(query, "PageContent", "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.Int16, "Revision", (short)revision));

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

            DbDataReader reader = ExecuteReader(command);

            if(reader != null) {
                PageContent result = null;

                string title = null, user = null, comment = null, content = null, description = null;
                DateTime dateTime = DateTime.MinValue;
                List<string> keywords = new List<string>(10);

                while(reader.Read()) {
                    if(title == null) {
                        title = reader["PageContent_Title"] as string;
                        user = reader["PageContent_User"] as string;
                        dateTime = (DateTime)reader["PageContent_LastModified"];
                        comment = GetNullableColumn<string>(reader, "PageContent_Comment", "");
                        content = reader["PageContent_Content"] as string;
                        description = GetNullableColumn<string>(reader, "PageContent_Description", null);
                    }

                    if(!IsDBNull(reader, "PageKeyword_Keyword")) {
                        keywords.Add(reader["PageKeyword_Keyword"] as string);
                    }
                }

                if(title != null) {
                    result = new PageContent(page, title, user, dateTime, comment, content, keywords.ToArray(), description);
                }

                CloseReader(reader);

                return result;
            }
            else return null;
        }
        /// <summary>
        /// Renames a Page Attachment.
        /// </summary>
        /// <param name="pageInfo">The Page Info that owns the Attachment.</param>
        /// <param name="oldName">The old name of the Attachment.</param>
        /// <param name="newName">The new name of the Attachment.</param>
        /// <returns><c>true</c> if the Attachment is renamed, false otherwise.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/>, <paramref name="oldName"/> or <paramref name="newName"/> are <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="pageInfo"/>, <paramref name="oldName"/> or <paramref name="newName"/> are empty,
        /// or if the page or old attachment do not exist, or the new attachment name already exists.</exception>
        public bool RenamePageAttachment(PageInfo pageInfo, string oldName, string newName)
        {
            if(pageInfo == null) throw new ArgumentNullException("pageInfo");
            if(oldName == null) throw new ArgumentNullException("oldName");
            if(oldName.Length == 0) throw new ArgumentException("Old Name cannot be empty", "oldName");
            if(newName == null) throw new ArgumentNullException("newName");
            if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");

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

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

            QueryBuilder queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.Update("Attachment", new string[] { "Name" }, new string[] { "NewName" });
            query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "OldName");
            query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");

            List<Parameter> parameters = new List<Parameter>(3);
            parameters.Add(new Parameter(ParameterType.String, "NewName", newName));
            parameters.Add(new Parameter(ParameterType.String, "OldName", oldName));
            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>
        /// Determines whether a page attachment exists.
        /// </summary>
        /// <param name="transaction">A database transaction.</param>
        /// <param name="page">The page.</param>
        /// <param name="name">The attachment.</param>
        /// <returns><c>true</c> if the attachment exists, <c>false</c> otherwise.</returns>
        private bool AttachmentExists(DbTransaction transaction, PageInfo page, string name)
        {
            ICommandBuilder builder = GetCommandBuilder();
            QueryBuilder queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.SelectCountFrom("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", page.FullName));

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

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

            return count == 1;
        }
        /// <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>
        /// Gets a page.
        /// </summary>
        /// <param name="connection">A database connection.</param>
        /// <param name="fullName">The full name of the page.</param>
        /// <returns>The <see cref="T:PageInfo" />, or <c>null</c> if no page is found.</returns>
        private PageInfo GetPage(DbConnection connection, string fullName)
        {
            string nspace, name;
            NameTools.ExpandFullName(fullName, out nspace, out name);
            if(nspace == null) nspace = "";

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

            string query = queryBuilder.SelectFrom("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(connection, query, parameters);

            DbDataReader reader = ExecuteReader(command);

            if(reader != null) {
                PageInfo result = null;

                if(reader.Read()) {
                    result = new PageInfo(NameTools.GetFullName(reader["Namespace"] as string, reader["Name"] as string),
                        this, (DateTime)reader["CreationDateTime"]);
                }

                CloseReader(reader);

                return result;
            }
            else return null;
        }
        /// <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(nspace == null) 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++;
                }

                command = builder.GetCommand(connection, finalQuery, parameters);

                rows = ExecuteNonQuery(command, false);

                return rows == categories.Length;
            }
            else return true;
        }
        /// <summary>
        /// Gets a namespace.
        /// </summary>
        /// <param name="connection">A database connection.</param>
        /// <param name="name">The name of the namespace (cannot be <c>null</c> or empty).</param>
        /// <returns>The <see cref="T:NamespaceInfo" />, or <c>null</c> if no namespace is found.</returns>
        private NamespaceInfo GetNamespace(DbConnection connection, string name)
        {
            ICommandBuilder builder = GetCommandBuilder();
            QueryBuilder queryBuilder = new QueryBuilder(builder);

            // select ... from Namespace left join Page on Namespace.DefaultPage = Page.Name where Namespace.Name = <name> and (Namespace.DefaultPage is null or Page.Namespace = <name>)
            string query = queryBuilder.SelectFrom("Namespace", "Page", "DefaultPage", "Name", Join.LeftJoin, new string[] { "Name", "DefaultPage" }, new string[] { "CreationDateTime" });
            query = queryBuilder.Where(query, "Namespace", "Name", WhereOperator.Equals, "Name1");
            query = queryBuilder.AndWhere(query, "Namespace", "DefaultPage", WhereOperator.IsNull, null, true, false);
            query = queryBuilder.OrWhere(query, "Page", "Namespace", WhereOperator.Equals, "Name2", false, true);

            List<Parameter> parameters = new List<Parameter>(2);
            parameters.Add(new Parameter(ParameterType.String, "Name1", name));
            parameters.Add(new Parameter(ParameterType.String, "Name2", name));

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

            DbDataReader reader = ExecuteReader(command);

            if(reader != null) {
                NamespaceInfo result = null;

                if(reader.Read()) {
                    string realName = reader["Namespace_Name"] as string;
                    string page = GetNullableColumn<string>(reader, "Namespace_DefaultPage", null);
                    PageInfo defaultPage = string.IsNullOrEmpty(page) ? null :
                        new PageInfo(NameTools.GetFullName(realName, page), this, (DateTime)reader["Page_CreationDateTime"]);

                    result = new NamespaceInfo(realName, this, defaultPage);
                }

                CloseReader(reader);

                return result;
            }
            else return null;
        }
        /// <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(nspace == null) 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) {
                    command = builder.GetCommand(transaction, finalQuery, parameters);

                    rowsDone += ExecuteNonQuery(command, false);

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

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

                rowsDone += ExecuteNonQuery(command, false);
            }

            if(rowsDone == allMessages.Count) {
                foreach(Message msg in messages) {
                    IndexMessageTree(page, msg, transaction);
                }
                CommitTransaction(transaction);
                return true;
            }
            else {
                RollbackTransaction(transaction);
                return false;
            }
        }
        /// <summary>
        /// Gets the Page Messages.
        /// </summary>
        /// <param name="connection">A database connection.</param>
        /// <param name="page">The Page.</param>
        /// <returns>The list of the <b>first-level</b> Messages, containing the replies properly nested, sorted by date/time.</returns>
        private Message[] GetMessages(DbConnection connection, PageInfo page)
        {
            if(GetPage(connection, page.FullName) == null) return null;

            // 1. Load all messages in memory in a dictionary id->message
            // 2. Build tree using ParentID

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

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

            string query = queryBuilder.SelectFrom("Message", new string[] { "Id", "Parent", "Username", "Subject", "DateTime", "Body" });
            query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
            query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");
            query = queryBuilder.OrderBy(query, new string[] { "DateTime", "Id" }, new Ordering[] { Ordering.Asc, Ordering.Asc });

            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);

            DbDataReader reader = ExecuteReader(command);

            if(reader != null) {
                Dictionary<short, Message> allMessages = new Dictionary<short, Message>(50);
                List<short> ids = new List<short>(50);
                List<short?> parents = new List<short?>(50);

                while(reader.Read()) {
                    Message msg = new Message((short)reader["Id"], reader["Username"] as string, reader["Subject"] as string,
                        (DateTime)reader["DateTime"], reader["Body"] as string);

                    ids.Add((short)msg.ID);

                    // Import from V2: parent = -1, otherwise null
                    if(!IsDBNull(reader, "Parent")) {
                        short par = (short)reader["Parent"];
                        if(par >= 0) parents.Add(par);
                        else parents.Add(null);
                    }
                    else parents.Add(null);

                    allMessages.Add((short)msg.ID, msg);
                }

                CloseReader(reader);

                // Add messages to their parents and build the top-level messages list
                List<Message> result = new List<Message>(20);

                for(int i = 0; i < ids.Count; i++) {
                    short? currentParent = parents[i];
                    short currentId = ids[i];

                    if(currentParent.HasValue) {
                        List<Message> replies = new List<Message>(allMessages[currentParent.Value].Replies);
                        replies.Add(allMessages[currentId]);
                        allMessages[currentParent.Value].Replies = replies.ToArray();
                    }
                    else result.Add(allMessages[currentId]);
                }

                return result.ToArray();
            }
            else return null;
        }
        /// <summary>
        /// Renames a Category.
        /// </summary>
        /// <param name="category">The Category to rename.</param>
        /// <param name="newName">The new Name.</param>
        /// <returns>The correct CategoryInfo object.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="category"/> or <paramref name="newName"/> are <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="newName"/> is empty.</exception>
        public CategoryInfo RenameCategory(CategoryInfo category, string newName)
        {
            if(category == null) throw new ArgumentNullException("category");
            if(newName == null) throw new ArgumentNullException("newName");
            if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");

            string nspace = null;
            string name = null;
            NameTools.ExpandFullName(category.FullName, out nspace, out name);
            if(nspace == null) nspace = "";

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

            QueryBuilder queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.Update("Category", new string[] { "Name" }, new string[] { "NewName" });
            query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "OldName");
            query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");

            List<Parameter> parameters = new List<Parameter>(3);
            parameters.Add(new Parameter(ParameterType.String, "NewName", newName));
            parameters.Add(new Parameter(ParameterType.String, "OldName", name));
            parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));

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

            int rows = ExecuteNonQuery(command, false);

            if(rows > 0) {
                CategoryInfo result = GetCategory(transaction, NameTools.GetFullName(nspace, newName));
                CommitTransaction(transaction);
                return result;
            }
            else {
                RollbackTransaction(transaction);
                return null;
            }
        }
        /// <summary>
        /// Gets the details of a page attachment.
        /// </summary>
        /// <param name="pageInfo">The page that owns the attachment.</param>
        /// <param name="name">The name of the attachment, for example "myfile.jpg".</param>
        /// <returns>The details of the attachment, or <c>null</c> if the attachment does not exist.</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.</exception>
        public FileDetails GetPageAttachmentDetails(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();
            QueryBuilder queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.SelectFrom("Attachment", new string[] { "Size", "Downloads", "LastModified" });
            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(connString, query, parameters);

            DbDataReader reader = ExecuteReader(command);

            if(reader != null) {
                FileDetails details = null;

                if(reader.Read()) {
                    details = new FileDetails((long)reader["Size"],
                        (DateTime)reader["LastModified"], (int)reader["Downloads"]);
                }

                CloseReader(command, reader);

                return details;
            }
            else return null;
        }
        /// <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(nspace == null) 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>
        /// Gets a category.
        /// </summary>
        /// <param name="connection">A database connection.</param>
        /// <param name="fullName">The full name of the category.</param>
        /// <returns>The <see cref="T:CategoryInfo" />, or <c>null</c> if no category is found.</returns>
        private CategoryInfo GetCategory(DbConnection connection, string fullName)
        {
            string nspace = null;
            string name = null;
            NameTools.ExpandFullName(fullName, out nspace, out name);
            if(nspace == null) nspace = "";

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

            string query = queryBuilder.SelectFrom("Category", "CategoryBinding", new string[] { "Name", "Namespace" }, new string[] { "Category", "Namespace" }, Join.LeftJoin,
                new string[] { "Name", "Namespace" }, new string[] { "Page" });
            query = queryBuilder.Where(query, "Category", "Namespace", WhereOperator.Equals, "Namespace");
            query = queryBuilder.AndWhere(query, "Category", "Name", WhereOperator.Equals, "Name");

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

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

            DbDataReader reader = ExecuteReader(command);

            if(reader != null) {
                CategoryInfo result = null;
                List<string> pages = new List<string>(50);

                while(reader.Read()) {
                    if(result == null) result = new CategoryInfo(NameTools.GetFullName(reader["Category_Namespace"] as string, reader["Category_Name"] as string), this);

                    if(!IsDBNull(reader, "CategoryBinding_Page")) {
                        pages.Add(NameTools.GetFullName(reader["Category_Namespace"] as string, reader["CategoryBinding_Page"] as string));
                    }
                }

                CloseReader(reader);

                if(result != null) result.Pages = pages.ToArray();

                return result;
            }
            else return null;
        }
        /// <summary>
        /// Determines whether a file exists.
        /// </summary>
        /// <param name="transaction">A database transaction.</param>
        /// <param name="fullName">The file full name, for example "/file.txt" or "/directory/file.txt".</param>
        /// <returns><c>true</c> if the file exists, <c>false</c> otherwise.</returns>
        private bool FileExists(DbTransaction transaction, string fullName)
        {
            string directory, file;
            SplitFileFullName(fullName, out directory, out file);

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

            string query = queryBuilder.SelectCountFrom("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", file));
            parameters.Add(new Parameter(ParameterType.String, "Directory", directory));

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

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

            return count == 1;
        }
        /// <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(nspace == null) 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>
        /// Gets the details of a file.
        /// </summary>
        /// <param name="fullName">The full name of the file.</param>
        /// <returns>The details, or <c>null</c> if the file does not exist.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="fullName"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty.</exception>
        public FileDetails GetFileDetails(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();
            QueryBuilder queryBuilder = new QueryBuilder(builder);

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

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

            DbDataReader reader = ExecuteReader(command);

            if(reader != null) {
                FileDetails details = null;

                if(reader.Read()) {
                    details = new FileDetails((long)reader["Size"],
                        (DateTime)reader["LastModified"], (int)reader["Downloads"]);
                }

                CloseReader(command, reader);

                return details;
            }
            else return null;
        }
        /// <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(nspace == null) 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>
        /// Renames or moves a File.
        /// </summary>
        /// <param name="oldFullName">The old full name of the File.</param>
        /// <param name="newFullName">The new full name of the File.</param>
        /// <returns><c>true</c> if the File is renamed, <c>false</c> otherwise.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="oldFullName"/> or <paramref name="newFullName"/> are <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="oldFullName"/> or <paramref name="newFullName"/> are empty, or if the old file does not exist, or if the new file already exist.</exception>
        public bool RenameFile(string oldFullName, string newFullName)
        {
            if(oldFullName == null) throw new ArgumentNullException("oldFullName");
            if(oldFullName.Length == 0) throw new ArgumentException("Old Full Name cannot be empty", "oldFullName");
            if(newFullName == null) throw new ArgumentNullException("newFullName");
            if(newFullName.Length == 0) throw new ArgumentException("New Full Name cannot be empty", "newFullName");

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

            QueryBuilder queryBuilder = new QueryBuilder(builder);

            if(!FileExists(transaction, oldFullName)) {
                RollbackTransaction(transaction);
                throw new ArgumentException("File does not exist", "oldFullName");
            }
            if(FileExists(transaction, newFullName)) {
                RollbackTransaction(transaction);
                throw new ArgumentException("File already exists", "newFullPath");
            }

            string oldDirectory, newDirectory, oldFilename, newFilename;
            SplitFileFullName(oldFullName, out oldDirectory, out oldFilename);
            SplitFileFullName(newFullName, out newDirectory, out newFilename);

            string query = queryBuilder.Update("File", new string[] { "Name" }, new string[] { "NewName" });
            query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "OldName");
            query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "OldDirectory");

            List<Parameter> parameters = new List<Parameter>(3);
            parameters.Add(new Parameter(ParameterType.String, "NewName", newFilename));
            parameters.Add(new Parameter(ParameterType.String, "OldName", oldFilename));
            parameters.Add(new Parameter(ParameterType.String, "OldDirectory", oldDirectory));

            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 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(nspace == null) 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 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>
        /// <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 Attachment is retrieved, <c>false</c> otherwise.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/>, <paramref name="name"/> or <paramref name="destinationStream"/> are <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if <paramref name="destinationStream"/> does not support writing,
        /// or if the page does not have attachments or if the attachment does not exist.</exception>
        public bool RetrievePageAttachment(PageInfo pageInfo, string name, System.IO.Stream destinationStream, bool countHit)
        {
            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", "name");
            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(!AttachmentExists(transaction, pageInfo, name)) {
                RollbackTransaction(transaction);
                throw new ArgumentException("Attachment does not exist", "name");
            }

            QueryBuilder queryBuilder = new QueryBuilder(builder);

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

            DbDataReader reader = ExecuteReader(command);

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

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

                CloseReader(reader);

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

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

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

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

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

            CommitTransaction(transaction);

            return true;
        }
        /// <summary>
        /// Gets all the categories of a page.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <returns>The categories, sorted by name.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="page"/> is <c>null</c>.</exception>
        public CategoryInfo[] GetCategoriesForPage(PageInfo page)
        {
            if(page == null) throw new ArgumentNullException("page");

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

            string nspace, pageName;
            NameTools.ExpandFullName(page.FullName, out nspace, out pageName);
            if(nspace == null) nspace = "";

            string query = queryBuilder.SelectFrom("Category", "CategoryBinding", new string[] { "Name", "Namespace" }, new string[] { "Category", "Namespace" }, Join.LeftJoin,
                new string[] { "Name", "Namespace" }, new string[] { "Page" });
            query = queryBuilder.Where(query, "CategoryBinding", "Namespace", WhereOperator.Equals, "Namespace");
            query = queryBuilder.AndWhere(query, "CategoryBinding", "Page", WhereOperator.Equals, "Page");
            query = queryBuilder.OrderBy(query, new[] { "Category_Name" }, new[] { Ordering.Asc });

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

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

            DbDataReader reader = ExecuteReader(command);

            if(reader != null) {
                List<CategoryInfo> result = new List<CategoryInfo>(20);
                List<string> pages = new List<string>(50);

                string prevName = "|||";
                string name = null;

                while(reader.Read()) {
                    name = reader["Category_Name"] as string;

                    if(name != prevName) {
                        if(prevName != "|||") {
                            result[result.Count - 1].Pages = pages.ToArray();
                            pages.Clear();
                        }

                        result.Add(new CategoryInfo(NameTools.GetFullName(reader["Category_Namespace"] as string, name), this));
                    }

                    prevName = name;
                    if(!IsDBNull(reader, "CategoryBinding_Page")) {
                        pages.Add(NameTools.GetFullName(reader["Category_Namespace"] as string, reader["CategoryBinding_Page"] as string));
                    }
                }

                CloseReader(command, reader);

                if(result.Count > 0) result[result.Count - 1].Pages = pages.ToArray();

                return result.ToArray();
            }
            else return null;
        }
        /// <summary>
        /// Sets the number of times a page attachment was retrieved.
        /// </summary>
        /// <param name="pageInfo">The page.</param>
        /// <param name="name">The name of the attachment.</param>
        /// <param name="count">The count to set.</param>
        /// <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.</exception>
        /// <exception cref="ArgumentOutOfRangeException">If <paramref name="count"/> is less than zero.</exception>
        public void SetPageAttachmentRetrievalCount(PageInfo pageInfo, string name, int count)
        {
            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");
            if(count < 0) throw new ArgumentOutOfRangeException("Count must be greater than or equal to zero", "count");

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

            string query = queryBuilder.Update("Attachment", new string[] { "Downloads" }, new string[] { "Downloads" });
            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));
            parameters.Add(new Parameter(ParameterType.Int32, "Downloads", count));

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

            ExecuteNonQuery(command);
        }
        /// <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(nspace == null) 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>
        /// Gets the total number of Messages in a Page Discussion.
        /// </summary>
        /// <param name="page">The Page.</param>
        /// <returns>The number of messages.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="page"/> is <c>null</c>.</exception>
        public int GetMessageCount(PageInfo page)
        {
            if(page == null) throw new ArgumentNullException("page");

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

            if(GetPage(connection, page.FullName) == null) {
                CloseConnection(connection);
                return -1;
            }

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

            QueryBuilder queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.SelectCountFrom("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(connection, query, parameters);

            int count = ExecuteScalar<int>(command, 0);

            return count;
        }
        /// <summary>
        /// Renames a Page.
        /// </summary>
        /// <param name="page">The Page to rename.</param>
        /// <param name="newName">The new Name.</param>
        /// <returns>The correct <see cref="T:PageInfo"/> object.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="page"/> or <paramref name="newName"/> are <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="newName"/> is empty.</exception>
        public PageInfo RenamePage(PageInfo page, string newName)
        {
            if(page == null) throw new ArgumentNullException("page");
            if(newName == null) throw new ArgumentNullException("newName");
            if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");

            // Check
            // 1. Page is default page of its namespace
            // 2. New name already exists

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

            if(GetPage(transaction, page.FullName) == null) {
                RollbackTransaction(transaction);
                return null;
            }
            if(IsDefaultPage(transaction, page)) {
                RollbackTransaction(transaction);
                return null;
            }
            if(GetPage(transaction, NameTools.GetFullName(NameTools.GetNamespace(page.FullName), NameTools.GetLocalName(newName))) != null) {
                RollbackTransaction(transaction);
                return null;
            }

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

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

            CategoryInfo[] currCategories = GetCategories(transaction, nspace == "" ? null : GetNamespace(transaction, nspace));
            string lowerPageName = page.FullName.ToLowerInvariant();
            List<string> pageCategories = new List<string>(10);
            foreach(CategoryInfo cat in currCategories) {
                if(Array.Find(cat.Pages, (s) => { return s.ToLowerInvariant() == lowerPageName; }) != null) {
                    pageCategories.Add(NameTools.GetLocalName(cat.FullName));
                }
            }

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

            QueryBuilder queryBuilder = new QueryBuilder(builder);

            string query = queryBuilder.Update("Page", new string[] { "Name" }, new string[] { "NewName" });
            query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "OldName");
            query = queryBuilder.AndWhere(query, "Namespace", WhereOperator.Equals, "Namespace");

            List<Parameter> parameters = new List<Parameter>(3);
            parameters.Add(new Parameter(ParameterType.String, "NewName", newName));
            parameters.Add(new Parameter(ParameterType.String, "OldName", name));
            parameters.Add(new Parameter(ParameterType.String, "Namespace", nspace));

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

            int rows = ExecuteNonQuery(command, false);

            if(rows > 0) {
                PageInfo result = new PageInfo(NameTools.GetFullName(nspace, newName), this, page.CreationDateTime);

                RebindPage(transaction, result, pageCategories.ToArray());

                PageContent newContent = GetContent(transaction, result, CurrentRevision);

                IndexPage(newContent, transaction);
                foreach(Message msg in GetMessages(transaction, result)) {
                    IndexMessageTree(result, msg, transaction);
                }

                CommitTransaction(transaction);

                return result;
            }
            else {
                RollbackTransaction(transaction);
                return null;
            }
        }
        /// <summary>
        /// Gets all the sub-namespaces.
        /// </summary>
        /// <returns>The sub-namespaces, sorted by name.</returns>
        public NamespaceInfo[] GetNamespaces()
        {
            ICommandBuilder builder = GetCommandBuilder();
            QueryBuilder queryBuilder = new QueryBuilder(builder);

            // select ... from Namespace left join Page on Namespace.DefaultPage = Page.Name where Namespace.Name <> '' and (Namespace.DefaultPage is null or Page.Namespace <> '')
            string query = queryBuilder.SelectFrom("Namespace", "Page", "DefaultPage", "Name", Join.LeftJoin, new string[] { "Name", "DefaultPage" }, new string[] { "CreationDateTime" });
            query = queryBuilder.Where(query, "Namespace", "Name", WhereOperator.NotEquals, "Empty1");
            query = queryBuilder.AndWhere(query, "Namespace", "DefaultPage", WhereOperator.IsNull, null, true, false);
            query = queryBuilder.OrWhere(query, "Page", "Namespace", WhereOperator.NotEquals, "Empty2", false, true);
            query = queryBuilder.OrderBy(query, new[] { "Namespace_Name" }, new[] { Ordering.Asc });

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

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

            DbDataReader reader = ExecuteReader(command);

            if(reader != null) {
                List<NamespaceInfo> result = new List<NamespaceInfo>(10);

                while(reader.Read()) {
                    string realName = reader["Namespace_Name"] as string;
                    string page = GetNullableColumn<string>(reader, "Namespace_DefaultPage", null);
                    PageInfo defaultPage = string.IsNullOrEmpty(page) ? null :
                        new PageInfo(NameTools.GetFullName(realName, page), this, (DateTime)reader["Page_CreationDateTime"]);

                    // The query returns duplicate entries if the main page of two or more namespaces have the same name
                    if(result.Find(n => { return n.Name.Equals(realName); }) == null) {
                        result.Add(new NamespaceInfo(realName, this, defaultPage));
                    }
                }

                CloseReader(command, reader);

                return result.ToArray();
            }
            else return null;
        }
Exemple #38
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SqlIndexInput"/> class.
        /// </summary>
        /// <param name="sqlServerDirectory">The Sql Server Directory object.</param>
        /// <param name="sqlStorageProviderUtility">The SQL storage provider utility.</param>
        /// <param name="connString">The connection string.</param>
        /// <param name="wiki">The wiki.</param>
        /// <param name="name">The name of the file.</param>
        public SqlIndexInput(SqlDirectory sqlServerDirectory, ISqlStorageProviderUtility sqlStorageProviderUtility, string connString, string wiki, string name)
        {
            _sqlServerDirectory        = sqlServerDirectory;
            _sqlStorageProviderUtility = sqlStorageProviderUtility;

            bool fFileNeeded = false;

            if (!CacheDirectory.FileExists(name))
            {
                fFileNeeded = true;
            }
            if (name.Contains("segments"))
            {
                fFileNeeded = true;
            }

            if (fFileNeeded)
            {
                StreamOutput fileStream = _sqlServerDirectory.CreateCachedOutputAsStream(name);

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

                if (!FileExists(transaction, wiki, name))
                {
                    _sqlStorageProviderUtility.RollbackTransaction(transaction);
                    _sqlStorageProviderUtility.CloseDbConnection(connection);
                    throw new FileNotFoundException();
                }

                QueryBuilder queryBuilder = new QueryBuilder(builder);

                string query = queryBuilder.SelectFrom("SearchIndex", new string[] { "Size", "Data" });
                query = queryBuilder.Where(query, "Wiki", WhereOperator.Equals, "Wiki");
                query = queryBuilder.AndWhere(query, "Name", WhereOperator.Equals, "Name");

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

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

                DbDataReader reader = _sqlStorageProviderUtility.ExecuteReader(command);

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

                    if (reader.Read())
                    {
                        int read = _sqlStorageProviderUtility.ReadBinaryColumn(reader, "Data", fileStream);
                        done = (long)read == (long)reader["Size"];
                    }

                    _sqlStorageProviderUtility.CloseReader(reader);

                    if (!done)
                    {
                        _sqlStorageProviderUtility.RollbackTransaction(transaction);
                    }

                    _sqlStorageProviderUtility.CommitTransaction(transaction);
                }
                else
                {
                    _sqlStorageProviderUtility.RollbackTransaction(transaction);
                }
                fileStream.Flush();
                fileStream.Close();

                // and open it as an input
                _indexInput = CacheDirectory.OpenInput(name);
            }
            else
            {
                _indexInput = CacheDirectory.OpenInput(name);
            }
        }