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

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

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

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

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

			int rows = ExecuteNonQuery(command);

			return rows > 0;
		}
		/// <summary>
		/// 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;
		}