diff --git a/uk/ac/sanger/artemis/chado/ChadoDAO.java b/uk/ac/sanger/artemis/chado/ChadoDAO.java
index 9fead21aa7378bcafd221877dfc4d05d9404c65f..c5d1e9666a36c0f1b1c9a1e2fbb97cebdcbd8763 100644
--- a/uk/ac/sanger/artemis/chado/ChadoDAO.java
+++ b/uk/ac/sanger/artemis/chado/ChadoDAO.java
@@ -242,6 +242,26 @@ public interface ChadoDAO
    */
   public int deleteFeatureDbxref(final String schema, final ChadoTransaction tsn)
                      throws SQLException;
+  
+  /**
+   * Insert a synonym for a feature.
+   * @param schema        schema/organism name or null
+   * @param tsn           the <code>ChadoTransaction</code>
+   * @return    number of rows changed
+   * @throws SQLException
+   */
+  public int insertFeatureAlias(final String schema, final ChadoTransaction tsn)
+                     throws SQLException;
+
+  /**
+   * Delete a synonym for a feature.
+   * @param schema        schema/organism name or null
+   * @param tsn           the <code>ChadoTransaction</code>
+   * @return    number of rows changed
+   * @throws SQLException
+   */
+  public int deleteFeatureAlias(final String schema, final ChadoTransaction tsn)
+                     throws SQLException;
 
   /**
    * 
diff --git a/uk/ac/sanger/artemis/chado/ChadoTransaction.java b/uk/ac/sanger/artemis/chado/ChadoTransaction.java
index 88ef2b125cc45419b05692936c86206c307eb47d..398e435bf583861b84336661d232e97a63a69f9a 100644
--- a/uk/ac/sanger/artemis/chado/ChadoTransaction.java
+++ b/uk/ac/sanger/artemis/chado/ChadoTransaction.java
@@ -57,6 +57,10 @@ public class ChadoTransaction
   public static final int INSERT_DBXREF = 7;
   public static final int DELETE_DBXREF = 8;
   
+  // SYNONYM TRANSACTIONS
+  public static final int INSERT_ALIAS = 9;
+  public static final int DELETE_ALIAS = 10;
+  
   /** properties store <i>e.g.</i> value=<quote>product=hypothetical protein</quote> */
   private List properties;
   /** constraint store <i>e.g.</i> type_id=21078 */
@@ -150,13 +154,22 @@ public class ChadoTransaction
                           final Timestamp lastmodified,
                           final Object feature_obj)
   {
-    this.type = type;
+    this.type   = type;
     this.dbxref = dbxref;
-    this.uniquename = uniquename;
+    this.uniquename   = uniquename;
     this.lastmodified = lastmodified;
-    this.feature_obj = feature_obj;
+    this.feature_obj  = feature_obj;
   }
 
+  public ChadoTransaction(final int type,
+                          final String uniquename,
+                          final Object feature_obj)
+  {
+    this.type = type;
+    this.uniquename  = uniquename;
+    this.feature_obj = feature_obj;
+  }
+  
   /**
    * The <code>Dbxref</code> used in a transaction.
    * @return  the feature dbxref
diff --git a/uk/ac/sanger/artemis/chado/ChadoTransactionManager.java b/uk/ac/sanger/artemis/chado/ChadoTransactionManager.java
index 28e29bde78fafec40c72a2918098626b86a38998..b9fe00025c4e58a984737ac34ba955233b5f3bc6 100644
--- a/uk/ac/sanger/artemis/chado/ChadoTransactionManager.java
+++ b/uk/ac/sanger/artemis/chado/ChadoTransactionManager.java
@@ -72,8 +72,15 @@ public class ChadoTransactionManager
               "Dbxref",
               "Ontology_term",
               "score", 
-              "gff_source",    // program or database
-              "gff_seqname" }; // seqID of coord system
+              "gff_source",      // program or database
+              "gff_seqname" };   // seqID of coord system
+           
+  private String synonym_tags[] =
+          {   "synonym",         // the rest are PSU synonyms
+              "gene",
+              "primary_name",
+              "reserved_name",
+              "obsolete_name" };
 
 
   /**
@@ -265,7 +272,7 @@ public class ChadoTransactionManager
       final String name = this_qualifier.getName();
 
       // ignore reserved tags
-      if(isReservedTag(name))
+      if(isReservedTag(name) || isSynonymTag(name))
         continue;
 
       final StringVector qualifier_values = this_qualifier.getValues();
@@ -303,6 +310,19 @@ public class ChadoTransactionManager
     return false;
   }
   
+  /**
+   * Determine if this is a GFF3 predefined tag.
+   * @param tag
+   * @return  true if the tag is a GFF3 predefined tag
+   */
+  private boolean isSynonymTag(final String tag)
+  {
+    for(int i=0; i<synonym_tags.length; i++)
+      if(tag.equals(synonym_tags[i]))
+        return true;
+    return false;
+  }
+  
   /**
    * Compare the old and new keys and qualifiers and find the qualifiers 
    * that have changed or been added and UPDATE, INSERT or DELETE accordingly.
@@ -348,11 +368,17 @@ public class ChadoTransactionManager
     {
       final Qualifier this_qualifier = (Qualifier)qualifiers_old.elementAt(qualifier_index);
       String name = this_qualifier.getName();
-      if(isReservedTag(name))
-        continue;
       
       if(!qualifiers_new.contains(name))
       {
+        if(isReservedTag(name) || isSynonymTag(name))
+        {
+          handleReservedTags(feature, uniquename, 
+              null,
+              this_qualifier);
+          continue;
+        }
+        
         // get the cvterm_id for this featureprop/qualifier
         Long lcvterm_id = DatabaseDocument.getCvtermID(name);
         if(lcvterm_id == null)   // chado doesn't recognise this
@@ -408,7 +434,7 @@ public class ChadoTransactionManager
           continue;
       }
 
-      if(isReservedTag(name))
+      if(isReservedTag(name) || isSynonymTag(name))
       {
         handleReservedTags(feature, uniquename, 
                            this_qualifier,
@@ -519,38 +545,69 @@ public class ChadoTransactionManager
                                   final Qualifier new_qualifier,
                                   final Qualifier old_qualifier)
   {  
-    final StringVector new_qualifier_strings =
-                 StreamQualifier.toStringVector(null, new_qualifier);
+    StringVector new_qualifier_strings = null;
     
-    final StringVector old_qualifier_strings =
-                 StreamQualifier.toStringVector(null, old_qualifier);
+    if(new_qualifier != null)
+      new_qualifier_strings = StreamQualifier.toStringVector(null, new_qualifier);
     
-    ChadoTransaction tsn;
+    StringVector old_qualifier_strings;
+    
+    if(old_qualifier != null)
+      old_qualifier_strings = StreamQualifier.toStringVector(null, old_qualifier);
+    else
+      old_qualifier_strings = new StringVector();
+    
+    final String qualifier_name;
+    
+    if(old_qualifier != null)
+      qualifier_name = old_qualifier.getName();
+    else
+      qualifier_name = new_qualifier.getName();
+    
+    ChadoTransaction tsn = null;
     // find tags that have been deleted
     for(int i = 0; i < old_qualifier_strings.size(); ++i)
     {
       String qualifier_string = (String)old_qualifier_strings.elementAt(i);
       
-      if(!new_qualifier_strings.contains(qualifier_string))
+      if( new_qualifier_strings == null ||
+         !new_qualifier_strings.contains(qualifier_string) )
       {
          int index = qualifier_string.indexOf("=");
          qualifier_string = qualifier_string.substring(index+1);
-         index = qualifier_string.lastIndexOf(":");
          
-         System.out.println(uniquename+"  in handleReservedTags() DELETE db="+
-             qualifier_string.substring(0,index)+" acc="+qualifier_string.substring(index+1));
-         Dbxref old_dbxref = new Dbxref();
-         old_dbxref.setName(qualifier_string.substring(0,index));
-         old_dbxref.setAccession(qualifier_string.substring(index+1));
-
-         tsn = new ChadoTransaction(ChadoTransaction.DELETE_DBXREF, 
-                                    uniquename, old_dbxref,
-                                    feature.getLastModified(),
-                                    feature);
+         if(qualifier_name.equals("Dbxref"))
+         {
+           index = qualifier_string.lastIndexOf(":");
+         
+           System.out.println(uniquename+"  in handleReservedTags() DELETE db="+
+               qualifier_string.substring(0,index)+" acc="+qualifier_string.substring(index+1));
+         
+           Dbxref old_dbxref = new Dbxref();
+           old_dbxref.setName(qualifier_string.substring(0,index));
+           old_dbxref.setAccession(qualifier_string.substring(index+1));
+
+           tsn = new ChadoTransaction(ChadoTransaction.DELETE_DBXREF, 
+                                      uniquename, old_dbxref,
+                                      feature.getLastModified(),
+                                      feature);
+         }
+         else if(isSynonymTag(qualifier_name))
+         {
+           System.out.println(uniquename+"  in handleReservedTags() DELETE "+qualifier_name+" "+
+                              qualifier_string);
+           tsn = new ChadoTransaction(ChadoTransaction.DELETE_ALIAS, 
+                                      uniquename, 
+                                      feature);
+           tsn.setConstraint("synonym.name", "'"+qualifier_string+"'");      
+         }
          sql.add(tsn);
       }
     }
     
+    if(new_qualifier_strings == null)
+      return;
+    
     // find tags that have been inserted
     for(int i = 0; i < new_qualifier_strings.size(); ++i)
     {
@@ -559,18 +616,33 @@ public class ChadoTransactionManager
       {
          int index = qualifier_string.indexOf("=");
          qualifier_string = qualifier_string.substring(index+1);
-         index = qualifier_string.lastIndexOf(":");
          
-         System.out.println(uniquename+"  in handleReservedTags() INSERT db="+
+         if(qualifier_name.equals("Dbxref"))
+         {
+           index = qualifier_string.lastIndexOf(":");
+         
+           System.out.println(uniquename+"  in handleReservedTags() INSERT db="+
              qualifier_string.substring(0,index)+" acc="+qualifier_string.substring(index+1));
-         Dbxref new_dbxref = new Dbxref();
-         new_dbxref.setName(qualifier_string.substring(0,index));
-         new_dbxref.setAccession(qualifier_string.substring(index+1));
-
-         tsn = new ChadoTransaction(ChadoTransaction.INSERT_DBXREF, 
-                                    uniquename, new_dbxref, 
-                                    feature.getLastModified(),
-                                    feature);
+           Dbxref new_dbxref = new Dbxref();
+           new_dbxref.setName(qualifier_string.substring(0,index));
+           new_dbxref.setAccession(qualifier_string.substring(index+1));
+
+           tsn = new ChadoTransaction(ChadoTransaction.INSERT_DBXREF, 
+                                      uniquename, new_dbxref, 
+                                      feature.getLastModified(),
+                                      feature);
+         }
+         else if(isSynonymTag(qualifier_name))
+         {
+           System.out.println(uniquename+"  in handleReservedTags() INSERT "+qualifier_name+" "+
+               qualifier_string);
+           tsn = new ChadoTransaction(ChadoTransaction.INSERT_ALIAS, 
+                                      uniquename, 
+                                      feature);
+           tsn.setConstraint("synonym.name", "'"+qualifier_string+"'");
+           Long lcvterm_id = DatabaseDocument.getCvtermID(qualifier_name);
+           tsn.addProperty("type_id", lcvterm_id.toString());
+         }
          sql.add(tsn);
       }
     }  
diff --git a/uk/ac/sanger/artemis/chado/IBatisDAO.java b/uk/ac/sanger/artemis/chado/IBatisDAO.java
index 104d78be73841e331201069adb0e0f69bf69939c..34e7aeff3d726a37fcfeab9a8c4de7eb5766d444 100644
--- a/uk/ac/sanger/artemis/chado/IBatisDAO.java
+++ b/uk/ac/sanger/artemis/chado/IBatisDAO.java
@@ -509,6 +509,32 @@ public class IBatisDAO implements ChadoDAO
     return sqlMap.delete("deleteFeatureDbxref", dbxref);
   }
   
+  /**
+   * Insert a synonym for a feature.
+   * @param schema        schema/organism name or null
+   * @param tsn           the <code>ChadoTransaction</code>
+   * @return    number of rows changed
+   * @throws SQLException
+   */
+  public int insertFeatureAlias(final String schema, final ChadoTransaction tsn)
+                     throws SQLException
+  {
+    return 0;  
+  }
+  
+  /**
+   * Delete a synonym for a feature.
+   * @param schema        schema/organism name or null
+   * @param tsn           the <code>ChadoTransaction</code>
+   * @return    number of rows changed
+   * @throws SQLException
+   */
+  public int deleteFeatureAlias(final String schema, final ChadoTransaction tsn)
+                     throws SQLException
+  {
+    return 0;  
+  }
+  
   /**
    * Write the time a feature was last modified
    * @param schema      schema/organism name or null
diff --git a/uk/ac/sanger/artemis/chado/JdbcDAO.java b/uk/ac/sanger/artemis/chado/JdbcDAO.java
index 421e9aef81249341048c97bac54b3a0826cde984..c954c225a013e4fc7c467c324256480adff15a0d 100644
--- a/uk/ac/sanger/artemis/chado/JdbcDAO.java
+++ b/uk/ac/sanger/artemis/chado/JdbcDAO.java
@@ -879,6 +879,102 @@ public class JdbcDAO
     return st.executeUpdate(sql);
   }
   
+  /**
+   * Insert a synonym for a feature.
+   * @param schema        schema/organism name or null
+   * @param tsn           the <code>ChadoTransaction</code>
+   * @return    number of rows changed
+   * @throws SQLException
+   */
+  public int insertFeatureAlias(final String schema, final ChadoTransaction tsn)
+                     throws SQLException
+  {
+    final String uniquename = tsn.getUniqueName();  
+    String sql;
+    
+    List constraints  = tsn.getConstraint();
+    String sqlAliasId = "SELECT synonym_id FROM "+schema+".synonym WHERE "+
+                        constraints.get(0);
+
+    appendToLogFile(sqlAliasId, sqlLog);
+    Statement st   = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
+                                          ResultSet.CONCUR_UPDATABLE);
+    ResultSet rs   = st.executeQuery(sqlAliasId);
+    boolean exists = rs.next();
+    
+    if(!exists)
+    {
+      // create a new synonym name
+      String synonym_name = (String)constraints.get(0);
+      int index = synonym_name.indexOf("=");
+      synonym_name = synonym_name.substring(index+1);
+      
+      String type_id = (String)tsn.getPropertiesValue().get(0);
+      
+      sql = "INSERT INTO "+schema+
+            ".synonym (name, type_id, synonym_sgml) values ( "+
+            synonym_name+","+type_id+","+synonym_name+")" ;
+
+      st.executeUpdate(sql);
+      appendToLogFile(sql, sqlLog);
+      
+      rs = st.executeQuery(sqlAliasId);
+      rs.next();
+      appendToLogFile(sqlAliasId, sqlLog);
+    }
+    
+    final int synonym_id = rs.getInt("synonym_id"); 
+    sql = "INSERT INTO "+schema+
+           ".feature_synonym ( synonym_id, feature_id, pub_id )"+
+           " values ( "+synonym_id+" ,"+
+               "(SELECT feature_id FROM "+schema+
+               ".feature WHERE  uniquename='"+uniquename+"'), "+
+               " 1)";
+ 
+    appendToLogFile(sql, sqlLog);
+    return st.executeUpdate(sql);
+  }
+  
+  /**
+   * Delete a synonym for a feature.
+   * @param schema        schema/organism name or null
+   * @param tsn           the <code>ChadoTransaction</code>
+   * @return    number of rows changed
+   * @throws SQLException
+   */
+  public int deleteFeatureAlias(final String schema, final ChadoTransaction tsn)
+                     throws SQLException
+  {
+    final String uniquename = tsn.getUniqueName();
+    List constraints = tsn.getConstraint();
+    String sql = "SELECT synonym_id FROM "+schema+".feature_synonym WHERE "+ 
+                 "synonym_id=(SELECT synonym_id FROM "+schema+".synonym WHERE "+
+                 constraints.get(0)+")";
+    
+    appendToLogFile(sql, sqlLog);
+    Statement st   = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
+                                          ResultSet.CONCUR_READ_ONLY);
+    ResultSet rs   = st.executeQuery(sql);
+    rs.last();
+    int nrows = rs.getRow();
+    final int synonym_id = rs.getInt("synonym_id"); 
+    
+    // check this name is not used some where else, 
+    // i.e. in more than one row
+    if(nrows>1)
+    {
+      sql = "DELETE FROM "+schema+".feature_synonym WHERE "+
+            "synonym_id="+synonym_id+" AND "+
+            "feature_id=(SELECT feature_id FROM "+schema+
+                     ".feature WHERE  uniquename='"+uniquename+"')";
+    }
+    else
+      sql = "DELETE FROM "+schema+".synonym WHERE synonym_id="+synonym_id;
+    
+    st   = conn.createStatement();
+    return st.executeUpdate(sql);
+  }
+  
   /**
    * Write the time a feature was last modified
    * @param schema      schema/organism name or null
diff --git a/uk/ac/sanger/artemis/util/DatabaseDocument.java b/uk/ac/sanger/artemis/util/DatabaseDocument.java
index f9dead2febbfe087bf721302fc2294459d208b05..1cf337902fe485aeac2f6ab90077232115f2d1a0 100644
--- a/uk/ac/sanger/artemis/util/DatabaseDocument.java
+++ b/uk/ac/sanger/artemis/util/DatabaseDocument.java
@@ -386,8 +386,6 @@ public class DatabaseDocument extends Document
       String name             = feat.getUniquename();
       String typeName         = getCvtermName(type_id);
 
-//      SimpleDateFormat date_format = new SimpleDateFormat("MM.dd.yyyy hh:mm:ss z");
-//      String timelastmodified = date_format.format(feat.getTimelastmodified());
       String timelastmodified = Long.toString(feat.getTimelastmodified().getTime());
       String feature_id       = Integer.toString(feat.getId());
 
@@ -466,6 +464,7 @@ public class DatabaseDocument extends Document
         }
       }
       
+      // append synonyms
       if(synonym != null &&
          synonym.containsKey(new Integer(feature_id)))
       {
@@ -479,9 +478,8 @@ public class DatabaseDocument extends Document
           this_buff.append(alias.getCvterm_name()+"=");
           this_buff.append(alias.getName());
           
-          //this_buff.append((String)v_synonyms.get(j));
           if(j<v_synonyms.size()-1)
-            this_buff.append(",");
+            this_buff.append(";");
         }
       }
       
@@ -778,6 +776,10 @@ public class DatabaseDocument extends Document
           dao.deleteFeatureDbxref(schema, tsn);
         else if(tsn.getType() == ChadoTransaction.INSERT_DBXREF)
           dao.insertFeatureDbxref(schema, tsn);
+        else if(tsn.getType() == ChadoTransaction.DELETE_ALIAS)
+          dao.deleteFeatureAlias(schema, tsn);
+        else if(tsn.getType() == ChadoTransaction.INSERT_ALIAS)
+          dao.insertFeatureAlias(schema, tsn);
       }