static int ldb_kv_rename(struct ldb_kv_context *ctx)
{
	struct ldb_module *module = ctx->module;
	void *data = ldb_module_get_private(module);
	struct ldb_kv_private *ldb_kv =
	    talloc_get_type(data, struct ldb_kv_private);
	struct ldb_request *req = ctx->req;
	struct ldb_message *msg;
	int ret = LDB_SUCCESS;
	struct ldb_val  key, key_old;
	struct ldb_dn *db_dn;
	bool valid_dn = false;

	ldb_request_set_state(req, LDB_ASYNC_PENDING);

	if (ldb_kv_cache_load(ctx->module) != 0) {
		return LDB_ERR_OPERATIONS_ERROR;
	}

	msg = ldb_msg_new(ctx);
	if (msg == NULL) {
		return LDB_ERR_OPERATIONS_ERROR;
	}

	/* Check the new DN is reasonable */
	valid_dn = ldb_dn_validate(req->op.rename.newdn);
	if (valid_dn == false) {
		ldb_asprintf_errstring(ldb_module_get_ctx(module),
				       "Invalid New DN: %s",
				       ldb_dn_get_linearized(req->op.rename.newdn));
		return LDB_ERR_INVALID_DN_SYNTAX;
	}

	/* we need to fetch the old record to re-add under the new name */
	ret = ldb_kv_search_dn1(module, req->op.rename.olddn, msg, 0);
	if (ret == LDB_ERR_INVALID_DN_SYNTAX) {
		ldb_asprintf_errstring(ldb_module_get_ctx(module),
				       "Invalid Old DN: %s",
				       ldb_dn_get_linearized(req->op.rename.newdn));
		return ret;
	} else if (ret != LDB_SUCCESS) {
		/* not finding the old record is an error */
		return ret;
	}

	/* We need to, before changing the DB, check if the new DN
	 * exists, so we can return this error to the caller with an
	 * unmodified DB
	 *
	 * Even in GUID index mode we use ltdb_key_dn() as we are
	 * trying to figure out if this is just a case rename
	 */
	key = ldb_kv_key_dn(msg, req->op.rename.newdn);
	if (!key.data) {
		talloc_free(msg);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	key_old = ldb_kv_key_dn(msg, req->op.rename.olddn);
	if (!key_old.data) {
		talloc_free(msg);
		talloc_free(key.data);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	/*
	 * Only declare a conflict if the new DN already exists,
	 * and it isn't a case change on the old DN
	 */
	if (key_old.length != key.length
	    || memcmp(key.data, key_old.data, key.length) != 0) {
		ret = ldb_kv_search_base(
		    module, msg, req->op.rename.newdn, &db_dn);
		if (ret == LDB_SUCCESS) {
			ret = LDB_ERR_ENTRY_ALREADY_EXISTS;
		} else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
			ret = LDB_SUCCESS;
		}
	}

	/* finding the new record already in the DB is an error */

	if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
		ldb_asprintf_errstring(ldb_module_get_ctx(module),
				       "Entry %s already exists",
				       ldb_dn_get_linearized(req->op.rename.newdn));
	}
	if (ret != LDB_SUCCESS) {
		talloc_free(key_old.data);
		talloc_free(key.data);
		talloc_free(msg);
		return ret;
	}

	talloc_free(key_old.data);
	talloc_free(key.data);


	ret = ldb_kv_sub_transaction_start(ldb_kv);
	if (ret != LDB_SUCCESS) {
		talloc_free(msg);
		return ret;
	}
	ret = ldb_kv_rename_internal(module, req, msg);
	if (ret != LDB_SUCCESS) {
		int r = ldb_kv_sub_transaction_cancel(ldb_kv);
		if (r != LDB_SUCCESS) {
			ldb_debug(
				ldb_module_get_ctx(module),
				LDB_DEBUG_FATAL,
				__location__
				": Unable to roll back sub transaction");
		}
		talloc_free(msg);
		ldb_kv->operation_failed = true;
		return ret;
	}
	ret = ldb_kv_sub_transaction_commit(ldb_kv);
	talloc_free(msg);

	return ret;
}