diff --git a/src/knot/dnssec/nsec-chain.c b/src/knot/dnssec/nsec-chain.c
index d838e4de3068bce6de2d218f39d6bb62b443d98f..4f212c884af6fc2f7335de123f62de29d7c7887a 100644
--- a/src/knot/dnssec/nsec-chain.c
+++ b/src/knot/dnssec/nsec-chain.c
@@ -280,14 +280,16 @@ int knot_nsec_changeset_remove(const knot_node_t *n,
 bool knot_nsec_empty_nsec_and_rrsigs_in_node(const knot_node_t *n)
 {
 	assert(n);
-	const knot_rrset_t **rrsets = knot_node_rrsets_no_copy(n);
+	knot_rrset_t **rrsets = knot_node_rrsets(n);
 	for (int i = 0; i < n->rrset_count; ++i) {
 		if (rrsets[i]->type != KNOT_RRTYPE_NSEC &&
 		    rrsets[i]->type != KNOT_RRTYPE_RRSIG) {
+			knot_node_free_rrset_array(n, rrsets);
 			return false;
 		}
 	}
 
+	knot_node_free_rrset_array(n, rrsets);
 	return true;
 }
 
diff --git a/src/knot/dnssec/nsec-chain.h b/src/knot/dnssec/nsec-chain.h
index a21c683713d7a85d1e79fe8eceb134783aad82c7..2abbe4d33bcabec2e92acefcd25b315e9e405a04 100644
--- a/src/knot/dnssec/nsec-chain.h
+++ b/src/knot/dnssec/nsec-chain.h
@@ -63,7 +63,7 @@ typedef int (*chain_iterate_create_cb)(knot_node_t *, knot_node_t *,
 inline static void bitmap_add_node_rrsets(bitmap_t *bitmap,
                                           const knot_node_t *node)
 {
-	const knot_rrset_t **node_rrsets = knot_node_rrsets_no_copy(node);
+	knot_rrset_t **node_rrsets = knot_node_rrsets(node);
 	for (int i = 0; i < node->rrset_count; i++) {
 		const knot_rrset_t *rr = node_rrsets[i];
 		if (rr->type != KNOT_RRTYPE_NSEC &&
@@ -71,6 +71,7 @@ inline static void bitmap_add_node_rrsets(bitmap_t *bitmap,
 			bitmap_add_type(bitmap, rr->type);
 		}
 	}
+	knot_node_free_rrset_array(node, node_rrsets);
 }
 
 /*!
diff --git a/src/knot/dnssec/nsec3-chain.c b/src/knot/dnssec/nsec3-chain.c
index 6c6069e0a3b7ba9e9fb7b03c11ecfef5ee747d52..06bbfdadd94971554403584bb5d68a943f177783 100644
--- a/src/knot/dnssec/nsec3-chain.c
+++ b/src/knot/dnssec/nsec3-chain.c
@@ -88,7 +88,7 @@ static bool are_nsec3_nodes_equal(const knot_node_t *a, const knot_node_t *b)
  */
 static bool node_should_be_signed_nsec3(const knot_node_t *n)
 {
-	knot_rrset_t **node_rrsets = knot_node_get_rrsets_no_copy(n);
+	knot_rrset_t **node_rrsets = knot_node_rrsets(n);
 	for (int i = 0; i < n->rrset_count; i++) {
 		if (node_rrsets[i]->type == KNOT_RRTYPE_NSEC ||
 		    node_rrsets[i]->type == KNOT_RRTYPE_RRSIG) {
diff --git a/src/knot/dnssec/zone-sign.c b/src/knot/dnssec/zone-sign.c
index 82633b9b0b8e3fc9109073e87a124013f6d97c72..3cbc1e16784220aa6e16f0fbe5d40acd98ac61e3 100644
--- a/src/knot/dnssec/zone-sign.c
+++ b/src/knot/dnssec/zone-sign.c
@@ -494,8 +494,9 @@ static int sign_node_rrsets(const knot_node_t *node,
 	int result = KNOT_EOK;
 	const knot_rrset_t *rrsigs = knot_node_rrset(node, KNOT_RRTYPE_RRSIG);
 
+	knot_rrset_t **node_rrsets = knot_node_rrsets(node);
 	for (int i = 0; i < node->rrset_count; i++) {
-		const knot_rrset_t *rrset = node->rrset_tree[i];
+		const knot_rrset_t *rrset = node_rrsets[i];
 		if (rrset->type == KNOT_RRTYPE_RRSIG) {
 			continue;
 		}
@@ -522,6 +523,8 @@ static int sign_node_rrsets(const knot_node_t *node,
 		}
 	}
 
+	knot_node_free_rrset_array(node, node_rrsets);
+
 	return remove_standalone_rrsigs(node, rrsigs, changeset);
 }
 
diff --git a/src/knot/nameserver/axfr.c b/src/knot/nameserver/axfr.c
index 3878d773a7f9e13312b9747cdb381a6a2279ce54..3d9418606da8c01e41842420fe1702ea41c3557c 100644
--- a/src/knot/nameserver/axfr.c
+++ b/src/knot/nameserver/axfr.c
@@ -36,22 +36,24 @@ static int put_rrsets(knot_pkt_t *pkt, knot_node_t *node, struct axfr_proc *stat
 	int i = state->cur_rrset;
 	int rrset_count = knot_node_rrset_count(node);
 	unsigned flags = KNOT_PF_NOTRUNC;
-	const knot_rrset_t **rrset = knot_node_rrsets_no_copy(node);
+	knot_rrset_t **rrsets = knot_node_rrsets(node);
 
 	/* Append all RRs. */
 	for (;i < rrset_count; ++i) {
-		if (rrset[i]->type == KNOT_RRTYPE_SOA) {
+		if (rrsets[i]->type == KNOT_RRTYPE_SOA) {
 			continue;
 		}
-		ret = knot_pkt_put(pkt, 0, rrset[i], flags);
+		ret = knot_pkt_put(pkt, 0, rrsets[i], flags);
 
 		/* If something failed, remember the current RR for later. */
 		if (ret != KNOT_EOK) {
 			state->cur_rrset = i;
+			knot_node_free_rrset_array(node, rrsets);
 			return ret;
 		}
 	}
 
+	knot_node_free_rrset_array(node, rrsets);
 	state->cur_rrset = 0;
 	return ret;
 }
diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c
index 357e3fa3960b5d529ab2af57ad8224b486ec5c89..f607e87b400db5bfde2fbf3e5eec60ceb542032f 100644
--- a/src/knot/nameserver/internet.c
+++ b/src/knot/nameserver/internet.c
@@ -154,7 +154,7 @@ static int put_rrsig(const knot_dname_t *sig_owner, uint16_t type,
 static int put_answer(knot_pkt_t *pkt, uint16_t type, struct query_data *qdata)
 {
 	const knot_rrset_t *rrset = NULL;
-	knot_rrset_t **rrsets = knot_node_get_rrsets_no_copy(qdata->node);
+	knot_rrset_t **rrsets = knot_node_rrsets(qdata->node);
 
 	/* Wildcard expansion or exact match, either way RRSet owner is
 	 * is QNAME. We can fake name synthesis by setting compression hint to
@@ -174,6 +174,7 @@ static int put_answer(knot_pkt_t *pkt, uint16_t type, struct query_data *qdata)
 		    (qdata->zone->conf->disable_any)) {
 			dbg_ns("%s: ANY/UDP disabled for this zone TC=1\n", __func__);
 			knot_wire_set_tc(pkt->wire);
+			knot_node_free_rrset_array(qdata->node, rrsets);
 			return KNOT_ESPACE;
 		}
 		for (unsigned i = 0; i < knot_node_rrset_count(qdata->node); ++i) {
@@ -192,6 +193,7 @@ static int put_answer(knot_pkt_t *pkt, uint16_t type, struct query_data *qdata)
 		break;
 	}
 
+	knot_node_free_rrset_array(qdata->node, rrsets);
 	return ret;
 }
 
diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c
index 85f56427741d43933532174cc9f6958ce8f95c73..a7b43f184151a044ecfcb820b76ec6e96ef7b032 100644
--- a/src/knot/updates/ddns.c
+++ b/src/knot/updates/ddns.c
@@ -1525,7 +1525,7 @@ static int knot_ddns_process_rem_all(knot_node_t *node,
 	 */
 	int ret = 0;
 	// The copy of rrsets is important here.
-	knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+	knot_rrset_t **rrsets = knot_node_rrsets(node);
 	int count = knot_node_rrset_count(node);
 
 	if (rrsets == NULL && count != 0) {
diff --git a/src/knot/zone/node.c b/src/knot/zone/node.c
index 3c4d4e690e873e55f9bc6e155e5298b9ab7f4845..b7920118d8a6ecd8af712e25f04888cd3bdf7598 100644
--- a/src/knot/zone/node.c
+++ b/src/knot/zone/node.c
@@ -23,9 +23,11 @@
 #include "libknot/common.h"
 #include "knot/zone/node.h"
 #include "libknot/rrset.h"
+#include "libknot/rr.h"
 #include "libknot/rdata.h"
 #include "common/descriptor.h"
 #include "common/debug.h"
+#include "common/mempattern.h"
 
 /*----------------------------------------------------------------------------*/
 /* Non-API functions                                                          */
@@ -67,6 +69,40 @@ static inline void knot_node_flags_clear(knot_node_t *node, uint8_t flag)
 	node->flags &= ~flag;
 }
 
+int rr_data_from(const knot_rrset_t *rrset, struct rr_data *data, mm_ctx_t *mm)
+{
+	int ret = knot_rrs_copy(&data->rrs, &rrset->rrs, mm);
+	if (ret != KNOT_EOK) {
+		free(data);
+		return ret;
+	}
+	data->type = rrset->type;
+	return KNOT_EOK;
+}
+
+static knot_rrset_t *rrset_from_rr_data(const knot_node_t *n, size_t pos,
+                                        mm_ctx_t *mm)
+{
+	struct rr_data data = n->rrs[pos];
+	knot_dname_t *dname_copy = knot_dname_copy(n->owner);
+	if (dname_copy == NULL) {
+		return NULL;
+	}
+	knot_rrset_t *rrset = knot_rrset_new(n->owner, data.type, KNOT_CLASS_IN, mm);
+	if (rrset == NULL) {
+		knot_dname_free(&dname_copy);
+		return NULL;
+	}
+
+	int ret = knot_rrs_copy(&rrset->rrs, &data.rrs, mm);
+	if (ret != KNOT_EOK) {
+		knot_rrset_free(&rrset, mm);
+		return NULL;
+	}
+
+	return rrset;
+}
+
 /*----------------------------------------------------------------------------*/
 /* API functions                                                              */
 /*----------------------------------------------------------------------------*/
@@ -90,7 +126,7 @@ knot_node_t *knot_node_new(const knot_dname_t *owner, knot_node_t *parent,
 	}
 
 	knot_node_set_parent(ret, parent);
-	ret->rrset_tree = NULL;
+	ret->rrs = NULL;
 	ret->flags = flags;
 
 	assert(ret->children == 0);
@@ -104,13 +140,16 @@ int knot_node_add_rrset_no_merge(knot_node_t *node, knot_rrset_t *rrset)
 		return KNOT_EINVAL;
 	}
 
-	size_t nlen = (node->rrset_count + 1) * sizeof(knot_rrset_t*);
-	void *p = realloc(node->rrset_tree, nlen);
+	size_t nlen = (node->rrset_count + 1) * sizeof(struct rr_data);
+	void *p = realloc(node->rrs, nlen);
 	if (p == NULL) {
 		return KNOT_ENOMEM;
 	}
-	node->rrset_tree = p;
-	node->rrset_tree[node->rrset_count] = rrset;
+	node->rrs = p;
+	int ret = rr_data_from(rrset, node->rrs + node->rrset_count, NULL);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
 	++node->rrset_count;
 
 	return KNOT_EOK;
@@ -123,8 +162,11 @@ int knot_node_add_rrset_replace(knot_node_t *node, knot_rrset_t *rrset)
 	}
 
 	for (uint16_t i = 0; i < node->rrset_count; ++i) {
-		if (node->rrset_tree[i]->type == rrset->type) {
-		node->rrset_tree[i] = rrset;
+		if (node->rrs[i].type == rrset->type) {
+			int ret = rr_data_from(rrset, &node->rrs[i], NULL);
+			if (ret != KNOT_EOK) {
+				return ret;
+			}
 		}
 	}
 
@@ -139,28 +181,33 @@ int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset,
 	}
 
 	for (uint16_t i = 0; i < node->rrset_count; ++i) {
-		if (node->rrset_tree[i]->type == rrset->type) {
-			if (out_rrset) {
-				*out_rrset = node->rrset_tree[i];
+		if (node->rrs[i].type == rrset->type) {
+			// TODO this is obviously a workaround
+			knot_rrset_t *node_rrset = rrset_from_rr_data(node, i, NULL);
+			if (node_rrset == NULL) {
+				return KNOT_ENOMEM;
 			}
 			int merged, deleted_rrs;
-			int ret = knot_rrset_merge_sort(node->rrset_tree[i],
+			int ret = knot_rrset_merge_sort(node_rrset,
 			                                rrset, &merged,
 			                                &deleted_rrs, NULL);
 			if (ret != KNOT_EOK) {
+				knot_rrset_free(&node_rrset, NULL);
 				return ret;
-			} else if (merged || deleted_rrs) {
-				return 1;
 			} else {
-				return 0;
+				knot_rrs_clear(&node->rrs[i].rrs, NULL);
+				knot_rrs_copy(&node->rrs[i].rrs, &rrset->rrs, NULL);
+				knot_rrset_free(&node_rrset, NULL);
+				if (merged || deleted_rrs) {
+					return 1;
+				} else {
+					return 0;
+				}
 			}
 		}
 	}
 
 	// New RRSet (with one RR)
-	if (out_rrset) {
-		*out_rrset = rrset;
-	}
 	return knot_node_add_rrset_no_merge(node, rrset);
 }
 
@@ -180,10 +227,9 @@ knot_rrset_t *knot_node_get_rrset(const knot_node_t *node, uint16_t type)
 		return NULL;
 	}
 
-	knot_rrset_t **rrs = node->rrset_tree;
 	for (uint16_t i = 0; i < node->rrset_count; ++i) {
-		if (rrs[i]->type == type) {
-			return rrs[i];
+		if (node->rrs[i].type == type) {
+			return rrset_from_rr_data(node, i, NULL);
 		}
 	}
 
@@ -198,30 +244,16 @@ knot_rrset_t *knot_node_remove_rrset(knot_node_t *node, uint16_t type)
 		return NULL;
 	}
 
-	uint16_t i = 0;
-	knot_rrset_t *ret = NULL;
-	knot_rrset_t **rrs = node->rrset_tree;
-	for (; i < node->rrset_count && ret == NULL; ++i) {
-		if (rrs[i]->type == type) {
-			ret = rrs[i];
-			memmove(rrs + i, rrs + i + 1, (node->rrset_count - i - 1) * sizeof(knot_rrset_t *));
+	for (int i = 0; i < node->rrset_count; ++i) {
+		if (node->rrs[i].type == type) {
+			knot_rrset_t *ret = rrset_from_rr_data(node, i, NULL);
+			memmove(node->rrs + i, node->rrs + i + 1, (node->rrset_count - i - 1) * sizeof(struct rr_data));
 			--node->rrset_count;
+			return ret;
 		}
 	}
 
-	return ret;
-}
-
-/*----------------------------------------------------------------------------*/
-
-void knot_node_remove_all_rrsets(knot_node_t *node)
-{
-	if (node == NULL) {
-		return;
-	}
-
-	// remove RRSets but do not delete them
-	node->rrset_count = 0;
+	return NULL;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -237,7 +269,7 @@ short knot_node_rrset_count(const knot_node_t *node)
 
 /*----------------------------------------------------------------------------*/
 
-knot_rrset_t **knot_node_get_rrsets(const knot_node_t *node)
+knot_rrset_t **knot_node_rrsets(const knot_node_t *node)
 {
 	if (node == NULL || node->rrset_count == 0) {
 		return NULL;
@@ -246,7 +278,13 @@ knot_rrset_t **knot_node_get_rrsets(const knot_node_t *node)
 	size_t rrlen = node->rrset_count * sizeof(knot_rrset_t*);
 	knot_rrset_t **cpy = malloc(rrlen);
 	if (cpy != NULL) {
-		memcpy(cpy, node->rrset_tree, rrlen);
+		for (int i = 0; i < node->rrset_count; ++i) {
+			cpy[i] = rrset_from_rr_data(node, i, NULL);
+			if (cpy[i] == NULL) {
+				knot_node_free_rrset_array(node, cpy);
+				return NULL;
+			}
+		}
 	}
 
 	return cpy;
@@ -254,31 +292,6 @@ knot_rrset_t **knot_node_get_rrsets(const knot_node_t *node)
 
 /*----------------------------------------------------------------------------*/
 
-const knot_rrset_t **knot_node_rrsets(const knot_node_t *node)
-{
-	if (node == NULL) {
-		return NULL;
-	}
-
-	return (const knot_rrset_t **)knot_node_get_rrsets(node);
-}
-
-knot_rrset_t **knot_node_get_rrsets_no_copy(const knot_node_t *node)
-{
-	if (node == NULL) {
-		return NULL;
-	}
-
-	return node->rrset_tree;
-}
-
-const knot_rrset_t **knot_node_rrsets_no_copy(const knot_node_t *node)
-{
-	return (const knot_rrset_t **)knot_node_get_rrsets_no_copy(node);
-}
-
-/*----------------------------------------------------------------------------*/
-
 const knot_node_t *knot_node_parent(const knot_node_t *node)
 {
 	if (node == NULL) {
@@ -624,12 +637,24 @@ void knot_node_free_rrsets(knot_node_t *node)
 		return;
 	}
 
-	knot_rrset_t **rrs = node->rrset_tree;
 	for (uint16_t i = 0; i < node->rrset_count; ++i) {
-		knot_rrset_free(&(rrs[i]), NULL);
+		knot_rrs_clear(&node->rrs[i].rrs, NULL);
 	}
 }
 
+void knot_node_free_rrset_array(const knot_node_t *node, knot_rrset_t **rrsets)
+{
+	if (node == NULL) {
+		return;
+	}
+
+	for (uint16_t i = 0; i < node->rrset_count; ++i) {
+		knot_rrset_free(&rrsets[i], NULL);
+	}
+
+	free(rrsets);
+}
+
 /*----------------------------------------------------------------------------*/
 
 void knot_node_free(knot_node_t **node)
@@ -640,10 +665,10 @@ void knot_node_free(knot_node_t **node)
 
 	dbg_node_detail("Freeing node: %p\n", *node);
 
-	if ((*node)->rrset_tree != NULL) {
+	if ((*node)->rrs != NULL) {
 		dbg_node_detail("Freeing RRSets.\n");
-		free((*node)->rrset_tree);
-		(*node)->rrset_tree = NULL;
+		free((*node)->rrs);
+		(*node)->rrs = NULL;
 		(*node)->rrset_count = 0;
 	}
 
@@ -675,14 +700,14 @@ int knot_node_shallow_copy(const knot_node_t *from, knot_node_t **to)
 	(*to)->owner = knot_dname_copy(from->owner);
 
 	// copy RRSets
-	size_t rrlen = sizeof(knot_rrset_t*) * from->rrset_count;
-	(*to)->rrset_tree = malloc(rrlen);
-	if ((*to)->rrset_tree == NULL) {
+	size_t rrlen = sizeof(struct rr_data) * from->rrset_count;
+	(*to)->rrs = malloc(rrlen);
+	if ((*to)->rrs == NULL) {
 		free(*to);
 		*to = NULL;
 		return KNOT_ENOMEM;
 	}
-	memcpy((*to)->rrset_tree, from->rrset_tree, rrlen);
+	memcpy((*to)->rrs, from->rrs, rrlen);
 
 	return KNOT_EOK;
 }
@@ -709,3 +734,18 @@ bool knot_node_rrtype_is_signed(const knot_node_t *node, uint16_t type)
 
 	return false;
 }
+
+bool knot_node_rrtype_exists(const knot_node_t *node, uint16_t type)
+{
+	if (node == NULL) {
+		return false;
+	}
+
+	for (uint i = 0; i < node->rrset_count; ++i) {
+		if (node->rrs[i].type == type) {
+			return true;
+		}
+	}
+
+	return false;
+}
diff --git a/src/knot/zone/node.h b/src/knot/zone/node.h
index 4bcc4cdc47c2a7b209dbd2e70feed1bb1231726f..9c4cf3a15fd51154b6bc6ba0375021502e6178b4 100644
--- a/src/knot/zone/node.h
+++ b/src/knot/zone/node.h
@@ -30,11 +30,18 @@
 
 #include "libknot/dname.h"
 #include "libknot/rrset.h"
+#include "libknot/rr.h"
 
 /*! \brief RRSet count in node if there is only NSEC (and possibly its RRSIG).*/
 #define KNOT_NODE_RRSET_COUNT_ONLY_NSEC 2
 
 /*----------------------------------------------------------------------------*/
+
+struct rr_data {
+	uint16_t type;
+	knot_rrs_t rrs;
+};
+
 /*!
  * \brief Structure representing one node in a domain name tree, i.e. one domain
  *        name in a zone.
@@ -45,8 +52,8 @@ struct knot_node {
 	knot_dname_t *owner; /*!< Domain name being the owner of this node. */
 	struct knot_node *parent; /*!< Parent node in the name hierarchy. */
 
-	/*! \brief Type-ordered list of RRSets belonging to this node. */
-	knot_rrset_t **rrset_tree;
+	/*! \brief Type-ordered array of RRSets belonging to this node. */
+	struct rr_data *rrs;
 
 	/*! \brief Wildcard node being the direct descendant of this node. */
 	struct knot_node *wildcard_child;
@@ -160,8 +167,6 @@ knot_rrset_t *knot_node_get_rrset(const knot_node_t *node, uint16_t type);
 
 knot_rrset_t *knot_node_remove_rrset(knot_node_t *node, uint16_t type);
 
-void knot_node_remove_all_rrsets(knot_node_t *node);
-
 /*!
  * \brief Returns number of RRSets in the node.
  *
@@ -178,21 +183,7 @@ short knot_node_rrset_count(const knot_node_t *node);
  *
  * \return Newly allocated array of RRSets or NULL if an error occured.
  */
-knot_rrset_t **knot_node_get_rrsets(const knot_node_t *node);
-
-/*!
- * \brief Returns all RRSets from the node.
- *
- * \note This function is identical to knot_node_get_rrsets(), only it returns
- *       non-modifiable data.
- *
- * \param node Node to get the RRSets from.
- *
- * \return Newly allocated array of RRSets or NULL if an error occured.
- */
-const knot_rrset_t **knot_node_rrsets(const knot_node_t *node);
-const knot_rrset_t **knot_node_rrsets_no_copy(const knot_node_t *node);
-knot_rrset_t **knot_node_get_rrsets_no_copy(const knot_node_t *node);
+knot_rrset_t **knot_node_rrsets(const knot_node_t *node);
 
 int knot_node_count_rrsets(const knot_node_t *node);
 
@@ -391,6 +382,7 @@ void knot_node_clear_empty(knot_node_t *node);
  * \param node Node to be destroyed.
  */
 void knot_node_free_rrsets(knot_node_t *node);
+void knot_node_free_rrset_array(const knot_node_t *node, knot_rrset_t **rrsets);
 
 /*!
  * \brief Destroys the node structure.
@@ -418,6 +410,8 @@ int knot_node_shallow_copy(const knot_node_t *from, knot_node_t **to);
  */
 bool knot_node_rrtype_is_signed(const knot_node_t *node, uint16_t type);
 
+bool knot_node_rrtype_exists(const knot_node_t *node, uint16_t type);
+
 #endif /* _KNOT_NODE_H_ */
 
 /*! @} */
diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c
index 7341143332074577b555e283de8dc28aea750d35..f60d73fad65d79d91d4187b4b67e43f73cf5c6bf 100644
--- a/src/knot/zone/semantic-check.c
+++ b/src/knot/zone/semantic-check.c
@@ -771,7 +771,7 @@ static int check_nsec3_node_in_zone(knot_zone_contents_t *zone,
 	}
 	
 	/* Check that the node only contains NSEC3 and RRSIG. */
-	const knot_rrset_t **rrsets = knot_node_rrsets_no_copy(nsec3_node);
+	knot_rrset_t **rrsets = knot_node_rrsets(nsec3_node);
 	if (rrsets == NULL) {
 		return KNOT_ENOMEM;
 	}
@@ -786,6 +786,7 @@ static int check_nsec3_node_in_zone(knot_zone_contents_t *zone,
 		}
 	}
 
+	knot_node_free_rrset_array(nsec3_node, rrsets);
 	free(array);
 
 	return KNOT_EOK;
@@ -981,7 +982,7 @@ static int semantic_checks_dnssec(knot_zone_contents_t *zone,
 	char auth = !knot_node_is_non_auth(node);
 	char deleg = knot_node_is_deleg_point(node);
 	uint rrset_count = knot_node_rrset_count(node);
-	const knot_rrset_t **rrsets = knot_node_rrsets_no_copy(node);
+	knot_rrset_t **rrsets = knot_node_rrsets(node);
 	const knot_rrset_t *dnskey_rrset =
 		knot_node_rrset(knot_zone_contents_apex(zone),
 				  KNOT_RRTYPE_DNSKEY);
@@ -1019,6 +1020,7 @@ static int semantic_checks_dnssec(knot_zone_contents_t *zone,
 					             "Could not create type "
 					             "array. Reason: %s.\n",
 					             knot_strerror(ret));
+					knot_node_free_rrset_array(node, rrsets);
 					return ret;
 				}
 
@@ -1090,11 +1092,14 @@ static int semantic_checks_dnssec(knot_zone_contents_t *zone,
 				              "Checking of NSEC3 node "
 				              "failed. Reason: %s.\n",
 				              knot_strerror(ret));
+				knot_node_free_rrset_array(node, rrsets);
 				return ret;
 			}
 		}
 	}
 
+	knot_node_free_rrset_array(node, rrsets);
+
 	return KNOT_EOK;
 }
 
diff --git a/src/knot/zone/zone-contents.c b/src/knot/zone/zone-contents.c
index 31ca1080dce9a098dbae2b35e0b152480d5dcb3e..087fb825e792055df34c151601d7ae2aab18514c 100644
--- a/src/knot/zone/zone-contents.c
+++ b/src/knot/zone/zone-contents.c
@@ -326,22 +326,25 @@ static int adjust_additional(knot_node_t **tnode, void *data)
 	assert(data != NULL);
 	assert(tnode != NULL);
 
-	int ret = KNOT_EOK;
-	knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
-	knot_node_t *node = *tnode;
-	knot_rrset_t **rrset = node->rrset_tree;
+	// TODO store elsewhere
+	return KNOT_EOK;
+
+//	int ret = KNOT_EOK;
+//	knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
+//	knot_node_t *node = *tnode;
+//	knot_rrset_t **rrset = knot_node_rrsets(node);
 
 	/* Lookup additional records for specific nodes. */
-	for(uint16_t i = 0; i < node->rrset_count; ++i) {
-		if (rrset_additional_needed(rrset[i]->type)) {
-			ret = discover_additionals(rrset[i], args->zone);
-			if (ret != KNOT_EOK) {
-				break;
-			}
-		}
-	}
+//	for(uint16_t i = 0; i < node->rrset_count; ++i) {
+//		if (rrset_additional_needed(rrset[i]->type)) {
+//			ret = discover_additionals(rrset[i], args->zone);
+//			if (ret != KNOT_EOK) {
+//				break;
+//			}
+//		}
+//	}
 
-	return ret;
+//	return ret;
 }
 
 /*----------------------------------------------------------------------------*/
diff --git a/src/knot/zone/zone-create.c b/src/knot/zone/zone-create.c
index b0e6bca0dff3ee53524a2e92839b9f6dbe662fbd..55af09ff8b562aac0229b3f70651f4e10a8239a8 100644
--- a/src/knot/zone/zone-create.c
+++ b/src/knot/zone/zone-create.c
@@ -104,16 +104,16 @@ int zcreator_step(zcreator_t *zc, knot_rrset_t *rr)
 		knot_rrset_free(&rr, NULL);
 	}
 	assert(node);
-	assert(zone_rrset);
+//	assert(zone_rrset);
 
 	// Do RRSet and node semantic checks
 	bool sem_fatal_error = false;
 	err_handler_t err_handler;
 	err_handler_init(&err_handler);
-	ret = sem_check_rrset(node, zone_rrset, &err_handler);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
+//	ret = sem_check_rrset(node, zone_rrset, &err_handler);
+//	if (ret != KNOT_EOK) {
+//		return ret;
+//	}
 	ret = sem_check_node_plain(zc->z, node,
 	                           &err_handler, true,
 	                           &sem_fatal_error);
diff --git a/src/knot/zone/zone-diff.c b/src/knot/zone/zone-diff.c
index cc667dab502c328a36f1e67899f559dd2cc4ba5c..8effef15d0e07c1b1187f6ec2b658d71ecc95207 100644
--- a/src/knot/zone/zone-diff.c
+++ b/src/knot/zone/zone-diff.c
@@ -202,7 +202,7 @@ static int knot_zone_diff_add_node(const knot_node_t *node,
 	}
 
 	/* Add all rrsets from node. */
-	const knot_rrset_t **rrsets = knot_node_rrsets(node);
+	knot_rrset_t **rrsets = knot_node_rrsets(node);
 	if (rrsets == NULL) {
 		/* Empty non-terminals - legal case. */
 		dbg_zonediff_detail("zone_diff: Node has no RRSets.\n");
@@ -236,7 +236,7 @@ static int knot_zone_diff_remove_node(knot_changeset_t *changeset,
 
 	dbg_zonediff("zone_diff: remove_node: Removing node: ...\n");
 
-	const knot_rrset_t **rrsets = knot_node_rrsets(node);
+	knot_rrset_t **rrsets = knot_node_rrsets(node);
 	if (rrsets == NULL) {
 		dbg_zonediff_verb("zone_diff: remove_node: "
 		                  "Nothing to remove.\n");
@@ -449,7 +449,7 @@ static int knot_zone_diff_node(knot_node_t **node_ptr, void *data)
 	dbg_zonediff_detail("zone_diff: diff_node: Node %p is present in "
 	              "both trees.\n", node_owner);
 	/* The nodes are in both trees, we have to diff each RRSet. */
-	const knot_rrset_t **rrsets = knot_node_rrsets(node);
+	knot_rrset_t **rrsets = knot_node_rrsets(node);
 	if (rrsets == NULL) {
 		dbg_zonediff("zone_diff: Node in first tree has no RRSets.\n");
 		/*
diff --git a/src/knot/zone/zone-dump.c b/src/knot/zone/zone-dump.c
index ee6b916ca30c3447199ed80a04854f14e1b7e801..ff4f13d264dd26979d6bc09719c961e9d0f781c2 100644
--- a/src/knot/zone/zone-dump.c
+++ b/src/knot/zone/zone-dump.c
@@ -56,7 +56,7 @@ static int apex_node_dump_text(knot_node_t *node, dump_params_t *params)
 		params->buf[0] = '\0';
 	}
 
-	const knot_rrset_t **rrsets = knot_node_rrsets_no_copy(node);
+	knot_rrset_t **rrsets = knot_node_rrsets(node);
 
 	// Dump other records.
 	for (uint16_t i = 0; i < node->rrset_count; i++) {
@@ -73,6 +73,7 @@ static int apex_node_dump_text(knot_node_t *node, dump_params_t *params)
 
 		if (knot_rrset_txt_dump(rrsets[i], params->buf, params->buflen,
 		                        params->style) < 0) {
+			knot_node_free_rrset_array(node, rrsets);
 			return KNOT_ENOMEM;
 		}
 		params->rr_count +=  knot_rrset_rr_count(rrsets[i]);
@@ -80,6 +81,7 @@ static int apex_node_dump_text(knot_node_t *node, dump_params_t *params)
 		params->buf[0] = '\0';
 	}
 
+	knot_node_free_rrset_array(node, rrsets);
 	return KNOT_EOK;
 }
 
@@ -94,7 +96,7 @@ static int node_dump_text(knot_node_t *node, void *data)
 		return KNOT_EOK;
 	}
 
-	const knot_rrset_t **rrsets = knot_node_rrsets_no_copy(node);
+	knot_rrset_t **rrsets = knot_node_rrsets(node);
 
 	// Dump non-apex rrsets.
 	for (uint16_t i = 0; i < node->rrset_count; i++) {
@@ -123,6 +125,7 @@ static int node_dump_text(knot_node_t *node, void *data)
 
 		if (knot_rrset_txt_dump(rrsets[i], params->buf, params->buflen,
 		                        params->style) < 0) {
+			knot_node_free_rrset_array(node, rrsets);
 			return KNOT_ENOMEM;
 		}
 		params->rr_count += knot_rrset_rr_count(rrsets[i]);
@@ -130,6 +133,7 @@ static int node_dump_text(knot_node_t *node, void *data)
 		params->buf[0] = '\0';
 	}
 
+	knot_node_free_rrset_array(node, rrsets);
 	return KNOT_EOK;
 }
 
diff --git a/src/libknot/rr.c b/src/libknot/rr.c
index c07277951ce1f590539e00cbfba62931036d864d..5cf73c2b59f0509864edacd620ae222fb3597e4c 100644
--- a/src/libknot/rr.c
+++ b/src/libknot/rr.c
@@ -222,5 +222,21 @@ void knot_rrs_clear(knot_rrs_t *rrs, mm_ctx_t *mm)
 	}
 }
 
+int knot_rrs_copy(knot_rrs_t *dst, const knot_rrs_t *src, mm_ctx_t *mm)
+{
+	if (dst == NULL || src == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	dst->rr_count = src->rr_count;
+	size_t src_size = knot_rrs_size(src);
+	dst->data = mm_alloc(mm, src_size);
+	if (dst->data == NULL) {
+		ERR_ALLOC_FAILED;
+		return KNOT_ENOMEM;
+	}
 
+	memcpy(dst->data, src->data, src_size);
+	return KNOT_EOK;
+}
 
diff --git a/src/libknot/rr.h b/src/libknot/rr.h
index 50a05a2b81f4f197b7baef75dd2c62f59832a6df..0583c9aa02d1c207abfb8530c5552e3a5b720a79 100644
--- a/src/libknot/rr.h
+++ b/src/libknot/rr.h
@@ -31,4 +31,5 @@ uint8_t* knot_rrs_create_rr_at_pos(knot_rrs_t *rrs,
 int knot_rrs_remove_rr_at_pos(knot_rrs_t *rrs, size_t pos, mm_ctx_t *mm);
 void knot_rrs_free(knot_rrs_t *rrs, mm_ctx_t *mm);
 void knot_rrs_clear(knot_rrs_t *rrs, mm_ctx_t *mm);
+int knot_rrs_copy(knot_rrs_t *dst, const knot_rrs_t *src, mm_ctx_t *mm);
 
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
index f967bb0f571191a9b16ccfad579e03933fb2f634..3850fc9853dfaeb879bf914de3ead94605cdacbe 100644
--- a/src/libknot/rrset.c
+++ b/src/libknot/rrset.c
@@ -867,15 +867,12 @@ int knot_rrset_copy(const knot_rrset_t *from, knot_rrset_t **to, mm_ctx_t *mm)
 		return KNOT_ENOMEM;
 	}
 
-	(*to)->rrs.rr_count = from->rrs.rr_count;
-	size_t data_size = knot_rrs_size(&from->rrs);
-	(*to)->rrs.data = mm_alloc(mm, data_size);
-	if ((*to)->rrs.data == NULL) {
-		ERR_ALLOC_FAILED;
+	int ret = knot_rrs_copy(&(*to)->rrs, &from->rrs, mm);
+	if (ret != KNOT_EOK) {
 		knot_rrset_free(to, mm);
-		return KNOT_ENOMEM;
+		return ret;
 	}
-	memcpy((*to)->rrs.data, from->rrs.data, data_size);
+
 	(*to)->additional = NULL; /* Never copy. */
 
 	return KNOT_EOK;