diff --git a/lib/modules/Sql.pmod/sql_util.pmod b/lib/modules/Sql.pmod/sql_util.pmod
index 0691914d73bc127beec3ae848bfd3b9339cff6b6..4093d79aa6bdaaf0da73d5928eeda87f408fbdf4 100644
--- a/lib/modules/Sql.pmod/sql_util.pmod
+++ b/lib/modules/Sql.pmod/sql_util.pmod
@@ -1,5 +1,5 @@
 /*
- * $Id: sql_util.pmod,v 1.13 2005/04/10 03:38:54 nilsson Exp $
+ * $Id: sql_util.pmod,v 1.14 2006/08/09 13:04:38 grubba Exp $
  *
  * Some SQL utility functions.
  * They are kept here to avoid circular references.
@@ -59,3 +59,85 @@ string emulate_bindings(string query, mapping(string|int:mixed)|void bindings,
 			  });
   return replace(query,k,v);
 }
+
+//! Result object wrapper performing utf8 decoding of all fields.
+//!
+//! Useful for eg Mysql connections which have been set to utf8-mode
+//! using eg @expr{"SET NAMES 'utf8'"@}.
+class UnicodeWrapper (
+		      //! The wrapped result object.
+		      static object master_result
+		      )
+{
+  //! Returns the number of rows in the result.
+  int num_rows()
+  {
+    return master_result->num_rows();
+  }
+
+  //! Returns the number of fields in the result.
+  int num_fields()
+  {
+    return master_result->num_fields();
+  }
+
+  //! Returns @expr{1@} if there are no more rows in the result.
+  int(0..1) eof()
+  {
+    return master_result->eof();
+  }
+
+  //! Cached @[fetch_fields()] result.
+  static array(int|mapping(string:mixed)) field_info;
+
+  //! Returns Information about the fields in the result.
+  //!
+  //! The following fields are converted from UTF8 if present:
+  //! @mapping
+  //!   @member string "name"
+  //!     The name of the field. Always present.
+  //!   @member string "table"
+  //!     The table the field is from. Not present from all databases.
+  //!   @member string "default"
+  //!     The default value for the column. Not available from all databases.
+  //! @endmapping
+  array(int|mapping(string:mixed)) fetch_fields()
+  {
+    if (!field_info) {
+      field_info = master_result->fetch_fields();
+      foreach(field_info, int|mapping(string:mixed) field) {
+	if (mappingp(field)) {
+	  field->name = utf8_to_string(field->name);
+	  if (field->table) {
+	    field->table = utf8_to_string(field->table);
+	  }
+	  if (field->default) {
+	    field->default = utf8_to_string(field->default);
+	  }
+	}
+      }
+    }
+    return field_info;
+  }
+
+  //! Skip ahead the specified number of rows.
+  void seek(int rows)
+  {
+    master_result->seek(rows);
+  }
+
+  //! Fetch the next row from the result.
+  //!
+  //! All strings in the result are decoded from UTF8.
+  int|array(string) fetch_row()
+  {
+    int|array(string) row = master_result->fetch_row();
+    if (!arrayp(row)) return row;
+    foreach(row; int i; string|int val) {
+      if (stringp(val)) {
+	row[i] = utf8_to_string(val);
+      }
+    }
+    return row;
+  }
+}