Repository: Unidata/netcdf-c (c)

Issue:
When renaming non-coord var to same name as dim without a var, hilarious confusion ensues...
This is broken out from #597 for clarity.

The following code produces an error:

```
      if (nc_create(file_name, NC_CLOBBER|NC_NETCDF4|NC_CLASSIC_MODEL, &ncid)) ERR;
      if (nc_def_dim(ncid, D1_NAME, DIM1_LEN, &dimid1)) ERR;
      if (nc_def_dim(ncid, D2_NAME, DIM1_LEN, &dimid2)) ERR;
      if (nc_def_var(ncid, D1_NAME, NC_INT, NDIM1, &dimid1, &varid1)) ERR;
      if (nc_def_var(ncid, D2_NAME, NC_INT, NDIM1, &dimid2, &varid2)) ERR;
      if (nc_close(ncid)) ERR;

      /* Open the file and rename the vars. */
      /* nc_set_log_level(4); */
      if (nc_open(file_name, NC_WRITE, &ncid)) ERR;
      if (nc_rename_var(ncid, varid1, TMP_NAME)) ERR;
      if (nc_rename_var(ncid, varid2, D1_NAME)) ERR;
      if (nc_close(ncid)) ERR;

```

This produces an error like this:
```
HDF5-DIAG: Error detected in HDF5 (1.10.4) thread 0:
  #000: H5D.c line 145 in H5Dcreate2(): unable to create dataset
    major: Dataset
    minor: Unable to initialize object
  #001: H5Dint.c line 326 in H5D__create_named(): unable to create and link to dataset
    major: Dataset
    minor: Unable to initialize object
  #002: H5L.c line 1572 in H5L_link_object(): unable to create new link to object
    major: Links
    minor: Unable to initialize object
  #003: H5L.c line 1813 in H5L__create_real(): can't insert link
    major: Links
    minor: Unable to insert object
  #004: H5Gtraverse.c line 851 in H5G_traverse(): internal path traversal failed
    major: Symbol table
    minor: Object not found
  #005: H5Gtraverse.c line 627 in H5G__traverse_real(): traversal operator failed
    major: Symbol table
    minor: Callback failed
  #006: H5L.c line 1608 in H5L__link_cb(): name already exists
    major: Links
    minor: Object already exists
ERROR: file nc4hdf.c, line 1788.
NetCDF: HDF error
HDF5-DIAG: Error detected in HDF5 (1.10.4) thread 0:
  #000: H5D.c line 145 in H5Dcreate2(): unable to create dataset
    major: Dataset
    minor: Unable to initialize object
  #001: H5Dint.c line 326 in H5D__create_named(): unable to create and link to dataset
    major: Dataset
    minor: Unable to initialize object
  #002: H5L.c line 1572 in H5L_link_object(): unable to create new link to object
    major: Links
    minor: Unable to initialize object
  #003: H5L.c line 1813 in H5L__create_real(): can't insert link
    major: Links
    minor: Unable to insert object
  #004: H5Gtraverse.c line 851 in H5G_traverse(): internal path traversal failed
    major: Symbol table
    minor: Object not found
  #005: H5Gtraverse.c line 627 in H5G__traverse_real(): traversal operator failed
    major: Symbol table
    minor: Callback failed
  #006: H5L.c line 1608 in H5L__link_cb(): name already exists
    major: Links
    minor: Object already exists
```

This is a tricky case. The second rename wants to rename varid2 to the name formerly used by varid1/dim1. However, varid2 is NOT a coord var of dimid2.

So in this case, a secret HDF5 name for varid2 must be used, since there is already a dim without a variable dataset to represent dimid2.

This condition must be detected and handled by nc_rename_var(). I have a PR that I will put up shortly with the fix.

Pull request description:
Fix for bug renaming non-coord var to same name as dim without a var
Part of #597.
Fixes #1297.

This is a fix for a tricky rename bug.

This PR includes all my pending PRs. It may be easier to review this code after some of them get merged.



Patch:
diff --git a/include/hdf5internal.h b/include/hdf5internal.h
index 764e06e59..0d8435c74 100644
--- a/include/hdf5internal.h
+++ b/include/hdf5internal.h
@@ -135,7 +135,14 @@ int nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *coord_var, NC_DIM_IN
 extern hid_t NC4_image_init(NC_FILE_INFO_T* h5);
 extern void NC4_image_finalize(void*);
 
-/* These functions are internal to the libhdf5 directory. */
+/* Create HDF5 dataset for dim without a coord var. */
+extern int nc4_create_dim_wo_var(NC_DIM_INFO_T *dim);
+
+/* Give a var a secret HDF5 name, for use when there is a dim of this
+ * name, but the var is not a coord var of that dim. */
+extern int nc4_give_var_secret_name(NC_VAR_INFO_T *var);
+
+/* Get the fill value for a var. */
 int nc4_get_fill_value(NC_FILE_INFO_T *h5, NC_VAR_INFO_T *var, void **fillp);
 
 
diff --git a/include/nc4internal.h b/include/nc4internal.h
index e750ec63d..4088062b5 100644
--- a/include/nc4internal.h
+++ b/include/nc4internal.h
@@ -153,7 +153,7 @@ typedef struct NC_VAR_INFO
    nc_bool_t created;           /* Variable has already been created (_not_ that it was just created) */
    nc_bool_t written_to;        /* True if variable has data written to it */
    struct NC_TYPE_INFO *type_info;
-   int atts_not_read;           /* If true, the atts have not yet been read. */
+   int atts_read;               /* If true, the atts have been read. */
    nc_bool_t meta_read;         /* True if this vars metadata has been completely read. */
    nc_bool_t coords_read;       /* True if this var has hidden coordinates att, and it has been read. */
    NCindex *att; 		/* NCindex<NC_ATT_INFO_T*> */
@@ -229,7 +229,7 @@ typedef struct NC_GRP_INFO
    void *format_grp_info;
    struct NC_FILE_INFO *nc4_info;
    struct NC_GRP_INFO *parent;
-   int atts_not_read;
+   int atts_read;               /* True if atts have been read for this group. */
    NCindex* children;		/* NCindex<struct NC_GRP_INFO*> */
    NCindex* dim;		/* NCindex<NC_DIM_INFO_T> * */
    NCindex* att;		/* NCindex<NC_ATT_INFO_T> * */
diff --git a/libhdf4/hdf4file.c b/libhdf4/hdf4file.c
index 3d8eb4b3a..f97ff37aa 100644
--- a/libhdf4/hdf4file.c
+++ b/libhdf4/hdf4file.c
@@ -415,6 +415,7 @@ nc4_var_list_add_full(NC_GRP_INFO_T* grp, const char* name, int ndims, nc_type x
    (*var)->created = NC_TRUE;
    (*var)->written_to = NC_TRUE;
    (*var)->format_var_info = format_var_info;
+   (*var)->atts_read = 1;
 
    /* Fill special type_info struct for variable type information. */
    if ((retval = nc4_set_var_type(xtype, endianness, type_size, type_name,
@@ -630,6 +631,7 @@ NC_HDF4_open(const char *path, int mode, int basepe, size_t *chunksizehintp,
    assert(nc4_info && nc4_info->root_grp);
    h5 = nc4_info;
    h5->no_write = NC_TRUE;
+   h5->root_grp->atts_read = 1;
 
    /* Allocate data to hold HDF4 specific file data. */
    if (!(hdf4_file = malloc(sizeof(NC_HDF4_FILE_INFO_T))))
diff --git a/libhdf5/hdf5attr.c b/libhdf5/hdf5attr.c
index 3aeb8ce97..978c33c2b 100644
--- a/libhdf5/hdf5attr.c
+++ b/libhdf5/hdf5attr.c
@@ -27,13 +27,14 @@ static int
 getattlist(NC_GRP_INFO_T *grp, int varid, NC_VAR_INFO_T **varp,
             NCindex **attlist)
 {
-   NC_VAR_INFO_T* var;
    int retval;
 
+   assert(grp && attlist);
+
    if (varid == NC_GLOBAL)
    {
       /* Do we need to read the atts? */
-      if (grp->atts_not_read)
+      if (!grp->atts_read)
          if ((retval = nc4_read_atts(grp, NULL)))
             return retval;
 
@@ -43,12 +44,14 @@ getattlist(NC_GRP_INFO_T *grp, int varid, NC_VAR_INFO_T **varp,
    }
    else
    {
+      NC_VAR_INFO_T *var;
+
       if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid)))
          return NC_ENOTVAR;
       assert(var->hdr.id == varid);
 
       /* Do we need to read the atts? */
-      if (var->atts_not_read)
+      if (!var->atts_read)
          if ((retval = nc4_read_atts(grp, var)))
             return retval;
 
diff --git a/libhdf5/hdf5create.c b/libhdf5/hdf5create.c
index b09f26fcc..cf2ef4010 100644
--- a/libhdf5/hdf5create.c
+++ b/libhdf5/hdf5create.c
@@ -66,6 +66,7 @@ nc4_create_file(const char *path, int cmode, size_t initialsz,
       BAIL(retval);
    nc4_info = NC4_DATA(nc);
    assert(nc4_info && nc4_info->root_grp);
+   nc4_info->root_grp->atts_read = 1;
 
    /* Add struct to hold HDF5-specific file metadata. */
    if (!(nc4_info->format_file_info = calloc(1, sizeof(NC_HDF5_FILE_INFO_T))))
diff --git a/libhdf5/hdf5file.c b/libhdf5/hdf5file.c
index a40c3b767..395d9fd64 100644
--- a/libhdf5/hdf5file.c
+++ b/libhdf5/hdf5file.c
@@ -727,7 +727,7 @@ NC4_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp)
    if (nattsp)
    {
       /* Do we need to read the atts? */
-      if (grp->atts_not_read)
+      if (!grp->atts_read)
          if ((retval = nc4_read_atts(grp, NULL)))
             return retval;
 
diff --git a/libhdf5/hdf5grp.c b/libhdf5/hdf5grp.c
index 058dab6f8..588273aa5 100644
--- a/libhdf5/hdf5grp.c
+++ b/libhdf5/hdf5grp.c
@@ -67,6 +67,11 @@ NC4_def_grp(int parent_ncid, const char *name, int *new_ncid)
       return retval;
    if (!(g->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T))))
       return NC_ENOMEM;
+
+   /* For new groups, there are no atts to read from file. */
+   g->atts_read = 1;
+
+   /* Return the ncid to the user. */
    if (new_ncid)
       *new_ncid = grp->nc4_info->controller->ext_ncid | g->hdr.id;
 
diff --git a/libhdf5/hdf5internal.c b/libhdf5/hdf5internal.c
index 7b74dc7b5..a88ed9b01 100644
--- a/libhdf5/hdf5internal.c
+++ b/libhdf5/hdf5internal.c
@@ -385,7 +385,7 @@ delete_dimscale_dataset(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T *dim)
  * @param dim Pointer to dimension info struct.
  *
  * @return ::NC_NOERR No error.
- * @author Quincey Koziol
+ * @author Quincey Koziol, Ed Hartnett
  */
 int
 nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim)
@@ -406,14 +406,14 @@ nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim)
    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
 
-   /* Detach dimscales from the [new] coordinate variable */
+   /* Detach dimscales from the [new] coordinate variable. */
    if (var->dimscale_attached)
    {
       int dims_detached = 0;
       int finished = 0;
       int d;
 
-      /* Loop over all dimensions for variable */
+      /* Loop over all dimensions for variable. */
       for (d = 0; d < var->ndims && !finished; d++)
       {
          /* Is there a dimscale attached to this axis? */
@@ -439,7 +439,8 @@ nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim)
 
                      /* Find dataset ID for dimension */
                      if (dim1->coord_var)
-                        dim_datasetid = ((NC_HDF5_VAR_INFO_T *)(dim1->coord_var->format_var_info))->hdf_datasetid;
+                        dim_datasetid = ((NC_HDF5_VAR_INFO_T *)
+                                         (dim1->coord_var->format_var_info))->hdf_datasetid;
                      else
                         dim_datasetid = hdf5_dim1->hdf_dimscaleid;
 
@@ -450,7 +451,8 @@ nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim)
                      if (dim_datasetid > 0)
                      {
                         LOG((3, "detaching scale from %s", var->hdr.name));
-                        if (H5DSdetach_scale(hdf5_var->hdf_datasetid, dim_datasetid, d) < 0)
+                        if (H5DSdetach_scale(hdf5_var->hdf_datasetid,
+                                             dim_datasetid, d) < 0)
                            BAIL(NC_EHDFERR);
                      }
                      var->dimscale_attached[d] = NC_FALSE;
@@ -462,7 +464,7 @@ nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim)
          }
       } /* next variable dimension */
 
-      /* Release & reset the array tracking attached dimscales */
+      /* Release & reset the array tracking attached dimscales. */
       free(var->dimscale_attached);
       var->dimscale_attached = NULL;
       need_to_reattach_scales++;
@@ -476,31 +478,32 @@ nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim)
          BAIL(NC_EHDFERR);
       hdf5_dim->hdf_dimscaleid = 0;
 
-      /* Now delete the dimscale's dataset
-         (it will be recreated later, if necessary) */
+      /* Now delete the dimscale's dataset (it will be recreated
+         later, if necessary). */
       if (H5Gunlink(hdf5_grp->hdf_grpid, dim->hdr.name) < 0)
          return NC_EDIMMETA;
    }
 
-   /* Attach variable to dimension */
+   /* Attach variable to dimension. */
    var->dimscale = NC_TRUE;
    dim->coord_var = var;
 
    /* Check if this variable used to be a coord. var */
    if (need_to_reattach_scales || (var->was_coord_var && grp != NULL))
    {
-      /* Reattach the scale everywhere it is used. */
-      /* (Recall that netCDF dimscales are always 1-D) */
+      /* Reattach the scale everywhere it is used. (Recall that netCDF
+       * dimscales are always 1-D) */
       if ((retval = rec_reattach_scales(grp->nc4_info->root_grp,
                                         var->dimids[0], hdf5_var->hdf_datasetid)))
          return retval;
 
-      /* Set state transition indicator (cancels earlier transition) */
+      /* Set state transition indicator (cancels earlier
+       * transition). */
       var->was_coord_var = NC_FALSE;
    }
-   else
-      /* Set state transition indicator */
-      var->became_coord_var = NC_TRUE;
+
+   /* Set state transition indicator */
+   var->became_coord_var = NC_TRUE;
 
 exit:
    return retval;
@@ -837,7 +840,7 @@ nc4_hdf5_find_grp_var_att(int ncid, int varid, const char *name, int attnum,
    if (varid == NC_GLOBAL)
    {
       /* Do we need to read the atts? */
-      if (my_grp->atts_not_read)
+      if (!my_grp->atts_read)
          if ((retval = nc4_read_atts(my_grp, NULL)))
             return retval;
 
@@ -849,7 +852,7 @@ nc4_hdf5_find_grp_var_att(int ncid, int varid, const char *name, int attnum,
          return NC_ENOTVAR;
 
       /* Do we need to read the var attributes? */
-      if (my_var->atts_not_read)
+      if (!my_var->atts_read)
          if ((retval = nc4_read_atts(my_grp, my_var)))
             return retval;
 
diff --git a/libhdf5/hdf5open.c b/libhdf5/hdf5open.c
index b0fae521f..70d9a0cc6 100644
--- a/libhdf5/hdf5open.c
+++ b/libhdf5/hdf5open.c
@@ -1308,7 +1308,7 @@ read_var(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
    H5Iinc_ref(hdf5_var->hdf_datasetid); /* Increment number of objects using ID */
    incr_id_rc++; /* Indicate that we've incremented the ref. count (for errors) */
    var->created = NC_TRUE;
-   var->atts_not_read = 1;   /* Don't read var atts until user asks for one. */
+   var->atts_read = 0;
 
    /* Try and read the dimids from the COORDINATES attribute. If it's
     * not present, we will have to do dimsscale matching to locate the
@@ -2112,9 +2112,9 @@ nc4_read_atts(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
 
    /* Remember that we have read the atts for this var or group. */
    if (var)
-      var->atts_not_read = 0;
+      var->atts_read = 1;
    else
-      grp->atts_not_read = 0;
+      grp->atts_read = 1;
 
    return NC_NOERR;
 }
@@ -2569,9 +2569,6 @@ rec_read_metadata(NC_GRP_INFO_T *grp)
          BAIL(retval);
    }
 
-   /* Defer the reading of global atts until someone asks for one. */
-   grp->atts_not_read = 1;
-
    /* When reading existing file, mark all variables as written. */
    for (i = 0; i < ncindexsize(grp->vars); i++)
       ((NC_VAR_INFO_T *)ncindexith(grp->vars, i))->written_to = NC_TRUE;
diff --git a/libhdf5/hdf5var.c b/libhdf5/hdf5var.c
index 46a8383e8..dc3176d70 100644
--- a/libhdf5/hdf5var.c
+++ b/libhdf5/hdf5var.c
@@ -220,6 +220,36 @@ nc4_find_default_chunksizes2(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
    return NC_NOERR;
 }
 
+/**
+ * @internal Give a var a secret HDF5 name. This is needed when a var
+ * is defined with the same name as a dim, but it is not a coord var
+ * of that dim. In that case, the var uses a secret name inside the
+ * HDF5 file.
+ *
+ * @param var Pointer to var info.
+ * @param name Name to use for base of secret name.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EMAXNAME Name too long to fit secret prefix.
+ * @returns ::NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+static int
+give_var_secret_name(NC_VAR_INFO_T *var, const char *name)
+{
+   /* Set a different hdf5 name for this variable to avoid name
+    * clash. */
+   if (strlen(name) + strlen(NON_COORD_PREPEND) > NC_MAX_NAME)
+      return NC_EMAXNAME;
+   if (!(var->hdf5_name = malloc((strlen(NON_COORD_PREPEND) +
+                                  strlen(name) + 1) * sizeof(char))))
+      return NC_ENOMEM;
+
+   sprintf(var->hdf5_name, "%s%s", NON_COORD_PREPEND, name);
+
+   return NC_NOERR;
+}
+
 /**
  * @internal This is called when a new netCDF-4 variable is defined
  * with nc_def_var().
@@ -410,6 +440,7 @@ NC4_def_var(int ncid, const char *name, nc_type xtype,
 
    var->is_new_var = NC_TRUE;
    var->meta_read = NC_TRUE;
+   var->atts_read = NC_TRUE;
 
    /* Point to the type, and increment its ref. count */
    var->type_info = type;
@@ -501,17 +532,8 @@ NC4_def_var(int ncid, const char *name, nc_type xtype,
     * and this var has the same name. */
    dim = (NC_DIM_INFO_T*)ncindexlookup(grp->dim,norm_name);
    if (dim && (!var->ndims || dimidsp[0] != dim->hdr.id))
-   {
-      /* Set a different hdf5 name for this variable to avoid name
-       * clash. */
-      if (strlen(norm_name) + strlen(NON_COORD_PREPEND) > NC_MAX_NAME)
-         BAIL(NC_EMAXNAME);
-      if (!(var->hdf5_name = malloc((strlen(NON_COORD_PREPEND) +
-                                     strlen(norm_name) + 1) * sizeof(char))))
-         BAIL(NC_ENOMEM);
-
-      sprintf(var->hdf5_name, "%s%s", NON_COORD_PREPEND, norm_name);
-   }
+      if ((retval = give_var_secret_name(var, var->hdr.name)))
+         BAIL(retval);
 
    /* If this is a coordinate var, it is marked as a HDF5 dimension
     * scale. (We found dim above.) Otherwise, allocate space to
@@ -1038,6 +1060,8 @@ NC4_rename_var(int ncid, int varid, const char *name)
    NC_HDF5_GRP_INFO_T *hdf5_grp;
    NC_FILE_INFO_T *h5;
    NC_VAR_INFO_T *var;
+   NC_DIM_INFO_T *other_dim;
+   int use_secret_name = 0;
    int retval = NC_NOERR;
 
    if (!name)
@@ -1081,10 +1105,31 @@ NC4_rename_var(int ncid, int varid, const char *name)
        (h5->cmode & NC_CLASSIC_MODEL))
       return NC_ENOTINDEFINE;
 
+   /* Is there another dim with this name, for which this var will not
+    * be a coord var? If so, we have to create a dim without a
+    * variable for the old name. */
+   if ((other_dim = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, name)) &&
+       strcmp(name, var->dim[0]->hdr.name))
+   {
+      /* Create a dim without var dataset for old dim. */
+      if ((retval = nc4_create_dim_wo_var(other_dim)))
+         return retval;
+
+      /* Give this var a secret HDF5 name so it can co-exist in file
+       * with dim wp var dataset. Base the secret name on the new var
+       * name. */
+      if ((retval = give_var_secret_name(var, name)))
+         return retval;
+      use_secret_name++;
+   }
+
    /* Change the HDF5 file, if this var has already been created
       there. */
    if (var->created)
    {
+      char *hdf5_name;
+      hdf5_name = use_secret_name ? var->hdf5_name: (char *)name;
+
       /* Do we need to read var metadata? */
       if (!var->meta_read)
          if ((retval = nc4_get_var_meta(var)))
@@ -1106,7 +1151,7 @@ NC4_rename_var(int ncid, int varid, const char *name)
       }
 
       LOG((3, "Moving dataset %s to %s", var->hdr.name, name));
-      if (H5Gmove(hdf5_grp->hdf_grpid, var->hdr.name, name) < 0)
+      if (H5Gmove(hdf5_grp->hdf_grpid, var->hdr.name, hdf5_name) < 0)
          BAIL(NC_EHDFERR);
    }
 
diff --git a/libhdf5/nc4hdf.c b/libhdf5/nc4hdf.c
index 679629451..9b68ff399 100644
--- a/libhdf5/nc4hdf.c
+++ b/libhdf5/nc4hdf.c
@@ -1708,7 +1708,8 @@ write_var(NC_VAR_INFO_T *var, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
 }
 
 /**
- * @internal Write a dimension.
+ * @internal Write a HDF5 dataset which is a dimension without a
+ * coordinate variable. This is a special 1-D dataset.
  *
  * @param dim Pointer to dim info struct.
  * @param grp Pointer to group info struct.
@@ -1719,79 +1720,109 @@ write_var(NC_VAR_INFO_T *var, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
  * @returns ::NC_EHDFERR HDF5 returned error.
  * @author Ed Hartnett
  */
-static int
-write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
+int
+nc4_create_dim_wo_var(NC_DIM_INFO_T *dim)
 {
-   hid_t spaceid = -1, create_propid = -1;
-   NC_HDF5_GRP_INFO_T *hdf5_grp;
    NC_HDF5_DIM_INFO_T *hdf5_dim;
-   hsize_t *new_size = NULL;
+   NC_HDF5_GRP_INFO_T *hdf5_grp;
+   hid_t spaceid = -1, create_propid = -1;
+   hsize_t dims[1], max_dims[1], chunk_dims[1] = {1};
+   char dimscale_wo_var[NC_MAX_NAME];
    int retval = NC_NOERR;
 
-   assert(dim && dim->format_dim_info && grp && grp->format_grp_info);
+   LOG((4, "%s: creating dim %s", __func__, dim->hdr.name));
+
+   /* Sanity check */
+   assert(!dim->coord_var);
 
    /* Get HDF5-specific dim and group info. */
+   hdf5_grp = (NC_HDF5_GRP_INFO_T *)dim->container->format_grp_info;
    hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
-   hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
 
-   /* If there's no dimscale dataset for this dim, create one,
-    * and mark that it should be hidden from netCDF as a
-    * variable. (That is, it should appear as a dimension
-    * without an associated variable.) */
-   if (!hdf5_dim->hdf_dimscaleid)
-   {
-      hsize_t dims[1], max_dims[1], chunk_dims[1] = {1};
-      char dimscale_wo_var[NC_MAX_NAME];
+   /* Create a property list. */
+   if ((create_propid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+      BAIL(NC_EHDFERR);
 
-      LOG((4, "%s: creating dim %s", __func__, dim->hdr.name));
+   /* Turn off recording of times associated with this object. */
+   if (H5Pset_obj_track_times(create_propid, 0) < 0)
+      BAIL(NC_EHDFERR);
 
-      /* Sanity check */
-      assert(!dim->coord_var);
+   /* Set size of dataset to size of dimension. */
+   dims[0] = dim->len;
+   max_dims[0] = dim->len;
 
-      /* Create a property list. If this dimension scale is
-       * unlimited (i.e. it's an unlimited dimension), then set
-       * up chunking, with a chunksize of 1. */
-      if ((create_propid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+   /* If this dimension scale is unlimited (i.e. it's an unlimited
+    * dimension), then set up chunking, with a chunksize of 1. */
+   if (dim->unlimited)
+   {
+      max_dims[0] = H5S_UNLIMITED;
+      if (H5Pset_chunk(create_propid, 1, chunk_dims) < 0)
          BAIL(NC_EHDFERR);
+   }
 
-      /* Turn off recording of times associated with this object. */
-      if (H5Pset_obj_track_times(create_propid,0)<0)
-         BAIL(NC_EHDFERR);
+   /* Set up space. */
+   if ((spaceid = H5Screate_simple(1, dims, max_dims)) < 0)
+      BAIL(NC_EHDFERR);
 
-      dims[0] = dim->len;
-      max_dims[0] = dim->len;
-      if (dim->unlimited)
-      {
-         max_dims[0] = H5S_UNLIMITED;
-         if (H5Pset_chunk(create_propid, 1, chunk_dims) < 0)
-            BAIL(NC_EHDFERR);
-      }
+   /* Turn on creation-order tracking. */
+   if (H5Pset_attr_creation_order(create_propid, H5P_CRT_ORDER_TRACKED|
+                                  H5P_CRT_ORDER_INDEXED) < 0)
+      BAIL(NC_EHDFERR);
 
-      /* Set up space. */
-      if ((spaceid = H5Screate_simple(1, dims, max_dims)) < 0)
-         BAIL(NC_EHDFERR);
+   /* Create the dataset that will be the dimension scale. */
+   LOG((4, "%s: about to H5Dcreate1 a dimscale dataset %s", __func__,
+        dim->hdr.name));
+   if ((hdf5_dim->hdf_dimscaleid = H5Dcreate2(hdf5_grp->hdf_grpid, dim->hdr.name,
+                                              H5T_IEEE_F32BE, spaceid,
+                                              H5P_DEFAULT, create_propid,
+                                              H5P_DEFAULT)) < 0)
+      BAIL(NC_EHDFERR);
 
-      /* Turn on creation-order tracking. */
-      if (H5Pset_attr_creation_order(create_propid, H5P_CRT_ORDER_TRACKED|
-                                     H5P_CRT_ORDER_INDEXED) < 0)
-         BAIL(NC_EHDFERR);
+   /* Indicate that this is a scale. Also indicate that not
+    * be shown to the user as a variable. It is hidden. It is
+    * a DIM WITHOUT A VARIABLE! */
+   sprintf(dimscale_wo_var, "%s%10d", DIM_WITHOUT_VARIABLE, (int)dim->len);
+   if (H5DSset_scale(hdf5_dim->hdf_dimscaleid, dimscale_wo_var) < 0)
+      BAIL(NC_EHDFERR);
 
-      /* Create the dataset that will be the dimension scale. */
-      LOG((4, "%s: about to H5Dcreate1 a dimscale dataset %s", __func__,
-           dim->hdr.name));
-      if ((hdf5_dim->hdf_dimscaleid = H5Dcreate2(hdf5_grp->hdf_grpid, dim->hdr.name,
-                                                 H5T_IEEE_F32BE, spaceid,
-                                                 H5P_DEFAULT, create_propid,
-                                                 H5P_DEFAULT)) < 0)
-         BAIL(NC_EHDFERR);
+exit:
+   if (spaceid > 0 && H5Sclose(spaceid) < 0)
+      BAIL2(NC_EHDFERR);
+   if (create_propid > 0 && H5Pclose(create_propid) < 0)
+      BAIL2(NC_EHDFERR);
+   return retval;
+}
 
-      /* Indicate that this is a scale. Also indicate that not
-       * be shown to the user as a variable. It is hidden. It is
-       * a DIM WITHOUT A VARIABLE! */
-      sprintf(dimscale_wo_var, "%s%10d", DIM_WITHOUT_VARIABLE, (int)dim->len);
-      if (H5DSset_scale(hdf5_dim->hdf_dimscaleid, dimscale_wo_var) < 0)
-         BAIL(NC_EHDFERR);
-   }
+/**
+ * @internal Write a dimension.
+ *
+ * @param dim Pointer to dim info struct.
+ * @param grp Pointer to group info struct.
+ * @param write_dimid
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EPERM Read-only file.
+ * @returns ::NC_EHDFERR HDF5 returned error.
+ * @author Ed Hartnett
+ */
+static int
+write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
+{
+   NC_HDF5_DIM_INFO_T *hdf5_dim;
+   int retval = NC_NOERR;
+
+   assert(dim && dim->format_dim_info && grp && grp->format_grp_info);
+
+   /* Get HDF5-specific dim and group info. */
+   hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+
+   /* If there's no dimscale dataset for this dim, create one,
+    * and mark that it should be hidden from netCDF as a
+    * variable. (That is, it should appear as a dimension
+    * without an associated variable.) */
+   if (!hdf5_dim->hdf_dimscaleid)
+      if ((retval = nc4_create_dim_wo_var(dim)))
+         BAIL(retval);
 
    /* Did we extend an unlimited dimension? */
    if (dim->extended)
@@ -1806,6 +1837,7 @@ write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
       if (v1)
       {
          NC_HDF5_VAR_INFO_T *hdf5_v1;
+         hsize_t *new_size;
          int d1;
 
          hdf5_v1 = (NC_HDF5_VAR_INFO_T *)v1->format_var_info;
@@ -1821,6 +1853,7 @@ write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
          }
          if (H5Dset_extent(hdf5_v1->hdf_datasetid, new_size) < 0)
             BAIL(NC_EHDFERR);
+         free(new_size);
       }
    }
 
@@ -1833,12 +1866,6 @@ write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
          BAIL(retval);
 
 exit:
-   if (spaceid > 0 && H5Sclose(spaceid) < 0)
-      BAIL2(NC_EHDFERR);
-   if (create_propid > 0 && H5Pclose(create_propid) < 0)
-      BAIL2(NC_EHDFERR);
-   if (new_size)
-      free(new_size);
 
    return retval;
 }


Test patch:
diff --git a/nc_test4/tst_rename2.c b/nc_test4/tst_rename2.c
index 5382e7813..e50ff29e5 100644
--- a/nc_test4/tst_rename2.c
+++ b/nc_test4/tst_rename2.c
@@ -25,11 +25,18 @@ See \ref copyright file for more info.
 #define DIM_X "x"
 #define DIM_Y "y"
 #define DIM_Z "z"
+#define VAR_NAME_START "lon"
+#define VAR_NAME_END "lo"
+#define DIM_NAME_START "lon"
+#define DIM_NAME_END "lo"
 
 #define DIM1_LEN 4
 #define NDIM1 1
 #define NDIM3 3
 #define NUM_ENDDEF_SETTINGS 2
+#define D1_NAME "d1"
+#define D2_NAME "d2"
+#define TMP_NAME "t1"
 
 int
 main(int argc, char **argv)
@@ -147,5 +154,124 @@ main(int argc, char **argv)
       SUMMARIZE_ERR;
 
    } /* next format */
+
+#define FILE_NAME1 "tst_dims_foo1.nc"
+#define DIM_NAME "lat_T42"
+#define VAR_NAME DIM_NAME
+#define DIM_NAME2 "lat"
+#define VAR_NAME2 DIM_NAME2
+#define RANK_lat_T42 1
+   fprintf(stderr,"*** test renaming with sync...");
+   {
+      int ncid, dimid, varid;
+      char file_name[NC_MAX_NAME + 1];
+      char name[NC_MAX_NAME + 1];
+
+      /* Create file with dim and associated coordinate var. */
+      sprintf(file_name, "%s_sync.nc", TEST_NAME);
+      if (nc_create(file_name, NC_CLOBBER|NC_NETCDF4|NC_CLASSIC_MODEL, &ncid)) ERR;
+      if (nc_def_dim(ncid, DIM_NAME_END, DIM1_LEN, &dimid)) ERR;
+      if (nc_def_var(ncid, DIM_NAME_END, NC_INT, NDIM1, &dimid, &varid)) ERR;
+      if (nc_close(ncid)) ERR;
+
+      if (nc_create(file_name, NC_CLOBBER|NC_NETCDF4|NC_CLASSIC_MODEL, &ncid)) ERR;
+      if (nc_def_dim(ncid, DIM_NAME_START, DIM1_LEN, &dimid)) ERR;
+      if (nc_def_var(ncid, DIM_NAME_END, NC_INT, NDIM1, &dimid, &varid)) ERR;
+      if (nc_close(ncid)) ERR;
+
+      if (nc_open(file_name, NC_WRITE, &ncid)) ERR;
+      if (nc_rename_dim(ncid, dimid, DIM_NAME_END)) ERR;
+      if (nc_close(ncid)) ERR;
+
+      /* Reopen file and check, */
+      if (nc_open(file_name, NC_WRITE, &ncid)) ERR;
+      if (nc_inq_dimid(ncid, DIM_NAME_END, &dimid)) ERR;
+      if (nc_inq_varid(ncid, DIM_NAME_END, &varid)) ERR;
+      if (nc_inq_dimname(ncid, dimid, name)) ERR;
+      if (strcmp(name, DIM_NAME_END)) ERR;
+      if (nc_inq_varname(ncid, varid, name)) ERR;
+      if (strcmp(name, DIM_NAME_END)) ERR;
+      if (nc_close(ncid)) ERR;
+   }
+   SUMMARIZE_ERR;
+   fprintf(stderr,"*** test renaming with sync...");
+   {
+      int ncid, dimid, varid;
+      char file_name[NC_MAX_NAME + 1];
+      char name[NC_MAX_NAME + 1];
+
+      /* Create file with dim and associated coordinate var. */
+      sprintf(file_name, "%s_sync.nc", TEST_NAME);
+      if (nc_create(file_name, NC_CLOBBER|NC_NETCDF4|NC_CLASSIC_MODEL, &ncid)) ERR;
+      if (nc_def_dim(ncid, DIM_NAME_START, DIM1_LEN, &dimid)) ERR;
+      if (nc_def_var(ncid, VAR_NAME_START, NC_INT, NDIM1, &dimid, &varid)) ERR;
+      if (nc_close(ncid)) ERR;
+
+      /* nc_set_log_level(4); */
+      /* Open the file and rename the var. */
+      if (nc_open(file_name, NC_WRITE, &ncid)) ERR;
+      if (nc_inq_dimid(ncid, DIM_NAME_START, &dimid)) ERR;
+      if (nc_inq_varid(ncid, VAR_NAME_START, &varid)) ERR;
+      if (nc_rename_var(ncid, varid, VAR_NAME_END)) ERR;
+
+      /* Sync to disk. Now the file has one dim and one var. The dim
+       * is a dimscale only dataset, and the var is a dataset with a
+       * dimscale attached pointing to the dim. */
+      /* if (nc_sync(ncid)) ERR; */
+      if (nc_close(ncid)) ERR;
+      if (nc_open(file_name, NC_WRITE, &ncid)) ERR;
+      /* Now rename the dim to the same name as the var. After this
+       * there will be one dataset, called DIM_NAME_END, which will be
+       * a dimscale. */
+      if (nc_rename_dim(ncid, dimid, DIM_NAME_END)) ERR;
+      if (nc_close(ncid)) ERR;
+
+      /* Reopen file and check, */
+      if (nc_open(file_name, NC_WRITE, &ncid)) ERR;
+      if (nc_inq_dimid(ncid, DIM_NAME_END, &dimid)) ERR;
+      if (nc_inq_varid(ncid, VAR_NAME_END, &varid)) ERR;
+      if (nc_inq_dimname(ncid, dimid, name)) ERR;
+      if (strcmp(name, DIM_NAME_END)) ERR;
+      if (nc_inq_varname(ncid, varid, name)) ERR;
+      if (strcmp(name, VAR_NAME_END)) ERR;
+      if (nc_close(ncid)) ERR;
+   }
+   SUMMARIZE_ERR;
+   fprintf(stderr,"*** test renaming non-coord var to same name as dim...");
+   {
+      int ncid, dimid1, dimid2, varid1, varid2;
+      int dimid_in, varid_in;
+      char file_name[NC_MAX_NAME + 1];
+
+      /* Create file with dim and associated coordinate var. */
+      sprintf(file_name, "%s_non_coord_to_dim.nc", TEST_NAME);
+      if (nc_create(file_name, NC_CLOBBER|NC_NETCDF4|NC_CLASSIC_MODEL, &ncid)) ERR;
+      if (nc_def_dim(ncid, D1_NAME, DIM1_LEN, &dimid1)) ERR;
+      if (nc_def_dim(ncid, D2_NAME, DIM1_LEN, &dimid2)) ERR;
+      if (nc_def_var(ncid, D1_NAME, NC_INT, NDIM1, &dimid1, &varid1)) ERR;
+      if (nc_def_var(ncid, D2_NAME, NC_INT, NDIM1, &dimid2, &varid2)) ERR;
+      if (nc_close(ncid)) ERR;
+
+      /* Open the file and rename the vars. */
+      /* nc_set_log_level(4); */
+      if (nc_open(file_name, NC_WRITE, &ncid)) ERR;
+      if (nc_rename_var(ncid, varid1, TMP_NAME)) ERR;
+      if (nc_rename_var(ncid, varid2, D1_NAME)) ERR;
+      if (nc_close(ncid)) ERR;
+
+      /* Reopen file and check, */
+      if (nc_open(file_name, NC_WRITE, &ncid)) ERR;
+      if (nc_inq_dimid(ncid, D1_NAME, &dimid_in)) ERR;
+      if (dimid_in != dimid1) ERR;
+      if (nc_inq_dimid(ncid, D2_NAME, &dimid_in)) ERR;
+      if (dimid_in != dimid2) ERR;
+      if (nc_inq_dimid(ncid, TMP_NAME, &dimid_in) != NC_EBADDIM) ERR;
+      if (nc_inq_varid(ncid, TMP_NAME, &varid_in)) ERR;
+      if (varid_in != varid1) ERR;
+      if (nc_inq_varid(ncid, D1_NAME, &varid_in)) ERR;
+      if (varid_in != varid2) ERR;
+      if (nc_close(ncid)) ERR;
+   }
+   SUMMARIZE_ERR;
    FINAL_RESULTS;
 }