Przeglądaj źródła

AddNodes: Split node creation into _raw _addRefs and _finish

The first two, _raw and _addRefs, are used in the user-facing _begin.
_raw has been split out for internal use during the bootstrap of ns0.
Julius Pfrommer 6 lat temu
rodzic
commit
1a8ec8cb70

+ 10 - 7
src/server/ua_server_internal.h

@@ -385,15 +385,18 @@ UA_Discovery_removeRecord(UA_Server *server, const UA_String *servername,
 
 /* Creates a new node in the nodestore. */
 UA_StatusCode
-Operation_addNode_begin(UA_Server *server, UA_Session *session, void *nodeContext,
-                        const UA_AddNodesItem *item, const UA_NodeId *parentNodeId,
-                        const UA_NodeId *referenceTypeId,
-                        UA_NodeId *outNewNodeId);
+AddNode_raw(UA_Server *server, UA_Session *session, void *nodeContext,
+            const UA_AddNodesItem *item, UA_NodeId *outNewNodeId);
 
-/* Children, references, type-checking, constructors. */
+/* Check the reference to the parent node; Add references. */
 UA_StatusCode
-Operation_addNode_finish(UA_Server *server, UA_Session *session,
-                         const UA_NodeId *nodeId);
+AddNode_addRefs(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
+                const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
+                const UA_NodeId *typeDefinitionId);
+
+/* Type-check type-definition; Run the constructors */
+UA_StatusCode
+AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId);
 
 /**********************/
 /* Create Namespace 0 */

+ 18 - 28
src/server/ua_server_ns0.c

@@ -15,15 +15,10 @@
 #include "ua_subscription.h"
 #include "ua_session.h"
 
-
-/*****************/
-/* Node Creation */
-/*****************/
-
 static UA_StatusCode
-addNode_begin(UA_Server *server, UA_NodeClass nodeClass,
-              UA_UInt32 nodeId, char *name, void *attributes,
-              const UA_DataType *attributesType) {
+addNode_raw(UA_Server *server, UA_NodeClass nodeClass,
+            UA_UInt32 nodeId, char *name, void *attributes,
+            const UA_DataType *attributesType) {
     UA_AddNodesItem item;
     UA_AddNodesItem_init(&item);
     item.nodeClass = nodeClass;
@@ -32,24 +27,19 @@ addNode_begin(UA_Server *server, UA_NodeClass nodeClass,
     item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
     item.nodeAttributes.content.decoded.data = attributes;
     item.nodeAttributes.content.decoded.type = attributesType;
-    UA_NodeId parentNode = UA_NODEID_NULL;
-    UA_NodeId referenceType = UA_NODEID_NULL;
-    return Operation_addNode_begin(server, &adminSession, NULL, &item, &parentNode, &referenceType, NULL);
+    return AddNode_raw(server, &adminSession, NULL, &item, NULL);
 }
 
 static UA_StatusCode
 addNode_finish(UA_Server *server, UA_UInt32 nodeId,
                UA_UInt32 parentNodeId, UA_UInt32 referenceTypeId) {
-    UA_NodeId sourceId = UA_NODEID_NUMERIC(0, nodeId);
-    UA_NodeId refTypeId = UA_NODEID_NUMERIC(0, referenceTypeId);
-    UA_ExpandedNodeId targetId = UA_EXPANDEDNODEID_NUMERIC(0, parentNodeId);
+    const UA_NodeId sourceId = UA_NODEID_NUMERIC(0, nodeId);
+    const UA_NodeId refTypeId = UA_NODEID_NUMERIC(0, referenceTypeId);
+    const UA_ExpandedNodeId targetId = UA_EXPANDEDNODEID_NUMERIC(0, parentNodeId);
     UA_StatusCode retval = UA_Server_addReference(server, sourceId, refTypeId, targetId, UA_FALSE);
     if (retval != UA_STATUSCODE_GOOD)
         return retval;
-
-
-    UA_NodeId node = UA_NODEID_NUMERIC(0, nodeId);
-    return Operation_addNode_finish(server, &adminSession, &node);
+    return AddNode_finish(server, &adminSession, &sourceId);
 }
 
 static UA_StatusCode
@@ -100,16 +90,16 @@ UA_Server_createNS0_base(UA_Server *server) {
     references_attr.isAbstract = true;
     references_attr.symmetric = true;
     references_attr.inverseName = UA_LOCALIZEDTEXT("", "References");
-    ret |= addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_REFERENCES, "References",
-                         &references_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]);
+    ret |= addNode_raw(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_REFERENCES, "References",
+                       &references_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]);
 
     UA_ReferenceTypeAttributes hassubtype_attr = UA_ReferenceTypeAttributes_default;
     hassubtype_attr.displayName = UA_LOCALIZEDTEXT("", "HasSubtype");
     hassubtype_attr.isAbstract = false;
     hassubtype_attr.symmetric = false;
     hassubtype_attr.inverseName = UA_LOCALIZEDTEXT("", "HasSupertype");
-    ret |= addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_HASSUBTYPE, "HasSubtype",
-                         &hassubtype_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]);
+    ret |= addNode_raw(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_HASSUBTYPE, "HasSubtype",
+                       &hassubtype_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]);
 
     ret |= addReferenceTypeNode(server, "HierarchicalReferences", NULL,
                          UA_NS0ID_HIERARCHICALREFERENCES, true, false, UA_NS0ID_REFERENCES);
@@ -167,8 +157,8 @@ UA_Server_createNS0_base(UA_Server *server) {
     UA_DataTypeAttributes basedatatype_attr = UA_DataTypeAttributes_default;
     basedatatype_attr.displayName = UA_LOCALIZEDTEXT("", "BaseDataType");
     basedatatype_attr.isAbstract = true;
-    ret |= addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NS0ID_BASEDATATYPE, "BaseDataType",
-                         &basedatatype_attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES]);
+    ret |= addNode_raw(server, UA_NODECLASS_DATATYPE, UA_NS0ID_BASEDATATYPE, "BaseDataType",
+                       &basedatatype_attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES]);
 
     /*****************/
     /* VariableTypes */
@@ -179,8 +169,8 @@ UA_Server_createNS0_base(UA_Server *server) {
     basevar_attr.isAbstract = true;
     basevar_attr.valueRank = -2;
     basevar_attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
-    ret |= addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NS0ID_BASEVARIABLETYPE, "BaseVariableType",
-                         &basevar_attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES]);
+    ret |= addNode_raw(server, UA_NODECLASS_VARIABLETYPE, UA_NS0ID_BASEVARIABLETYPE, "BaseVariableType",
+                       &basevar_attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES]);
 
     UA_VariableTypeAttributes bdv_attr = UA_VariableTypeAttributes_default;
     bdv_attr.displayName = UA_LOCALIZEDTEXT("", "BaseDataVariableType");
@@ -197,8 +187,8 @@ UA_Server_createNS0_base(UA_Server *server) {
 
     UA_ObjectTypeAttributes baseobj_attr = UA_ObjectTypeAttributes_default;
     baseobj_attr.displayName = UA_LOCALIZEDTEXT("", "BaseObjectType");
-    ret |= addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NS0ID_BASEOBJECTTYPE, "BaseObjectType",
-                  &baseobj_attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES]);
+    ret |= addNode_raw(server, UA_NODECLASS_OBJECTTYPE, UA_NS0ID_BASEOBJECTTYPE, "BaseObjectType",
+                       &baseobj_attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES]);
 
     UA_ObjectTypeAttributes folder_attr = UA_ObjectTypeAttributes_default;
     folder_attr.displayName = UA_LOCALIZEDTEXT("", "FolderType");

+ 142 - 137
src/server/ua_services_nodemanagement.c

@@ -74,10 +74,6 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
        UA_NodeId_isNull(referenceTypeId))
         return UA_STATUSCODE_GOOD;
 
-    /* Omit checks during bootstrap */
-    if(server->bootstrapNS0)
-        return UA_STATUSCODE_GOOD;
-
     /* See if the parent exists */
     const UA_Node *parent = UA_Nodestore_get(server, parentNodeId);
     if(!parent) {
@@ -149,8 +145,7 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
 static UA_StatusCode
 typeCheckVariableNode(UA_Server *server, UA_Session *session,
                       const UA_VariableNode *node,
-                      const UA_VariableTypeNode *vt,
-                      const UA_NodeId *parentNodeId) {
+                      const UA_VariableTypeNode *vt) {
     /* The value might come from a datasource, so we perform a
      * regular read. */
     UA_DataValue value;
@@ -183,33 +178,23 @@ typeCheckVariableNode(UA_Server *server, UA_Session *session,
         return UA_STATUSCODE_BADTYPEMISMATCH;
     }
 
-    /* If variable node is created below BaseObjectType and has its default valueRank of -2,
-     * skip the test */
-    const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
-
-    // TODO handle subtypes of parent reference types
-    if(node->valueRank != vt->valueRank &&
-       node->valueRank != UA_VariableAttributes_default.valueRank &&
-       !isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes,
-                     parentReferences, UA_PARENT_REFERENCES_COUNT)) {
-        /* Check valueRank against the vt */
-        if(!compatibleValueRanks(node->valueRank, vt->valueRank)) {
-            UA_LOG_INFO_SESSION(server->config.logger, session,
-                                "AddNodes: The value rank is incomatible with the value rank of the VariableType");
-            return UA_STATUSCODE_BADTYPEMISMATCH;
-        }
+    /* Check valueRank against the vt */
+    if(!compatibleValueRanks(node->valueRank, vt->valueRank)) {
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "AddNodes: The value rank is incomatible with the value rank of the VariableType");
+        return UA_STATUSCODE_BADTYPEMISMATCH;
     }
 
     /* Check array dimensions against the vt */
     if(!compatibleArrayDimensions(vt->arrayDimensionsSize, vt->arrayDimensions,
                                   node->arrayDimensionsSize, node->arrayDimensions)) {
-        UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: The array dimensions are incomatible with the array dimensions of the VariableType");
+        UA_LOG_INFO_SESSION(server->config.logger, session, "AddNodes: The array dimensions are "
+                            "incomatible with the array dimensions of the VariableType");
         return UA_STATUSCODE_BADTYPEMISMATCH;
     }
 
     /* Typecheck the value */
-    if(!server->bootstrapNS0 && value.hasValue) {
+    if(value.hasValue) {
         /* If the type-check failed write the same value again. The
          * write-service tries to convert to the correct type... */
         if(!compatibleValue(server, &node->dataType, node->valueRank,
@@ -232,62 +217,92 @@ static const UA_NodeId baseObjectType =
 static const UA_NodeId hasTypeDefinition =
     {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASTYPEDEFINITION}};
 
-/* Use attributes from the variable type wherever required */
+/* Use attributes from the variable type wherever required. Reload the node if
+ * changes were made. */
 static UA_StatusCode
 useVariableTypeAttributes(UA_Server *server, UA_Session *session,
-                          UA_VariableNode *node, const UA_AddNodesItem *item) {
-    const UA_VariableAttributes *attributes = (const UA_VariableAttributes*)
-        item->nodeAttributes.content.decoded.data;
-
-    /* Select the type definition */
-    const UA_NodeId *typeDefinition;
-    if(node->nodeClass == UA_NODECLASS_VARIABLE)
-        typeDefinition = &item->typeDefinition.nodeId;
-    else /* UA_NODECLASS_VARIABLETYPE */
-        typeDefinition = &item->parentNodeId.nodeId;
-
-    /* Replace an empty typeDefinition with the most permissive default */
-    if(UA_NodeId_isNull(typeDefinition))
-        typeDefinition = &baseDataVariableType;
-
-    const UA_VariableTypeNode *vt = (const UA_VariableTypeNode*)
-        UA_Nodestore_get(server, typeDefinition);
-    if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE) {
-        UA_Nodestore_release(server, (const UA_Node*)vt);
-        return UA_STATUSCODE_BADTYPEMISMATCH;
-    }
+                          const UA_VariableNode **node_ptr, const UA_VariableTypeNode *vt) {
+    const UA_VariableNode *node = *node_ptr;
+    UA_Boolean modified = false;
 
     /* If no value is set, see if the vt provides one and copy it. This needs to
      * be done before copying the datatype from the vt, as setting the datatype
      * triggers a typecheck. */
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if(!attributes->value.type) {
+    UA_DataValue orig;
+    UA_DataValue_init(&orig);
+    UA_StatusCode retval = readValueAttribute(server, session, node, &orig);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    if(orig.value.type) {
+        /* A value is present */
+        UA_DataValue_deleteMembers(&orig);
+    } else {
         UA_LOG_DEBUG_SESSION(server->config.logger, session,
                              "AddNodes: No value given; Copy the value "
                              "from the TypeDefinition");
-        UA_DataValue vt_value;
-        UA_DataValue_init(&vt_value);
-        retval = readValueAttribute(server, session,
-                                    (const UA_VariableNode*)vt, &vt_value);
-        if(retval == UA_STATUSCODE_GOOD && vt_value.hasValue) {
-            retval = UA_Variant_copy(&vt_value.value, &node->value.data.value.value);
-            node->value.data.value.hasValue = true;
+        UA_WriteValue v;
+        UA_WriteValue_init(&v);
+        retval = readValueAttribute(server, session, (const UA_VariableNode*)vt, &v.value);
+        if(retval == UA_STATUSCODE_GOOD && v.value.hasValue) {
+            v.nodeId = node->nodeId;
+            v.attributeId = UA_ATTRIBUTEID_VALUE;
+            retval = UA_Server_writeWithSession(server, session, &v);
+            modified = true;
         }
-        UA_DataValue_deleteMembers(&vt_value);
+        UA_DataValue_deleteMembers(&v.value);
+
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
     }
 
     /* If no datatype is given, use the datatype of the vt */
-    if(retval == UA_STATUSCODE_GOOD && UA_NodeId_isNull(&node->dataType)) {
+    if(UA_NodeId_isNull(&node->dataType)) {
         UA_LOG_INFO_SESSION(server->config.logger, session, "AddNodes: "
                             "No datatype given; Copy the datatype attribute "
                             "from the TypeDefinition");
-        retval = UA_NodeId_copy(&vt->dataType, &node->dataType);
+        UA_WriteValue v;
+        UA_WriteValue_init(&v);
+        v.nodeId = node->nodeId;
+        v.attributeId = UA_ATTRIBUTEID_DATATYPE;
+        v.value.hasValue = true;
+        UA_Variant_setScalar(&v.value.value, (void*)(uintptr_t)&vt->dataType, &UA_TYPES[UA_TYPES_NODEID]);
+        retval = UA_Server_writeWithSession(server, session, &v);
+        modified = true;
+
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
     }
 
-    /* TODO: If the vt has arraydimensions but this variable does not, copy */
+    /* Use the ArrayDimensions of the vt */
+    if(node->arrayDimensionsSize == 0 && vt->arrayDimensionsSize > 0) {
+        UA_WriteValue v;
+        UA_WriteValue_init(&v);
+        v.nodeId = node->nodeId;
+        v.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
+        v.value.hasValue = true;
+        UA_Variant_setArray(&v.value.value, vt->arrayDimensions, vt->arrayDimensionsSize,
+                            &UA_TYPES[UA_TYPES_UINT32]);
+        retval = UA_Server_writeWithSession(server, session, &v);
+        modified = true;
 
-    UA_Nodestore_release(server, (const UA_Node*)vt);
-    return retval;
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+    }
+
+    /* If the node was modified, update the pointer to the new version */
+    if(modified) {
+        const UA_VariableNode *updated = (const UA_VariableNode*)
+            UA_Nodestore_get(server, &node->nodeId);
+
+        if(!updated)
+            return UA_STATUSCODE_BADINTERNALERROR;
+
+        UA_Nodestore_release(server, (const UA_Node*)node);
+        *node_ptr = updated;
+    }
+
+    return UA_STATUSCODE_GOOD;
 }
 
 /* Search for an instance of "browseName" in node searchInstance. Used during
@@ -439,11 +454,6 @@ static void deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize, UA_
     }
 }
 
-static UA_StatusCode
-AddNode_typeCheckAddRefs(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
-                         const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
-                         const UA_NodeId *typeDefinitionId);
-
 static UA_StatusCode
 copyChildNode(UA_Server *server, UA_Session *session,
               const UA_NodeId *destinationNodeId,
@@ -527,8 +537,8 @@ copyChildNode(UA_Server *server, UA_Session *session,
         copyChildNodes(server, session, &rd->nodeId.nodeId, &newNodeId);
 
         /* Add the parent reference */
-        retval = AddNode_typeCheckAddRefs(server, session, &newNodeId, destinationNodeId,
-                                          &rd->referenceTypeId, typeId);
+        retval = AddNode_addRefs(server, session, &newNodeId, destinationNodeId,
+                                 &rd->referenceTypeId, typeId);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_Nodestore_remove(server, &node->nodeId);
             UA_Nodestore_release(server, type);
@@ -538,7 +548,7 @@ copyChildNode(UA_Server *server, UA_Session *session,
         /* Call addnode_finish, this recursively adds additional members, the type
          * definition and so on of the base type of this child, if they are not yet
          * in the destination */
-        retval |= Operation_addNode_finish(server, session, &newNodeId);
+        retval |= AddNode_finish(server, session, &newNodeId);
         UA_NodeId_deleteMembers(&newNodeId);
         UA_Nodestore_release(server, type);
     }
@@ -663,18 +673,16 @@ removeDeconstructedNode(UA_Server *server, UA_Session *session,
 
 static const UA_NodeId hasSubtype = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}};
 
-static UA_StatusCode
-AddNode_typeCheckAddRefs(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
-                         const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
-                         const UA_NodeId *typeDefinitionId) {
+UA_StatusCode
+AddNode_addRefs(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
+                const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
+                const UA_NodeId *typeDefinitionId) {
     /* Get the node */
+    const UA_Node *type = NULL;
     const UA_Node *node = UA_Nodestore_get(server, nodeId);
     if(!node)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
 
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    const UA_Node *type = NULL;
-
     /* Use the typeDefinition as parent for type-nodes */
     if(node->nodeClass == UA_NODECLASS_VARIABLETYPE ||
        node->nodeClass == UA_NODECLASS_OBJECTTYPE ||
@@ -690,12 +698,9 @@ AddNode_typeCheckAddRefs(UA_Server *server, UA_Session *session, const UA_NodeId
         }
     }
 
-    if(server->bootstrapNS0)
-        goto get_type;
-
     /* Check parent reference. Objects may have no parent. */
-    retval = checkParentReference(server, session, node->nodeClass,
-                                  parentNodeId, referenceTypeId);
+    UA_StatusCode retval = checkParentReference(server, session, node->nodeClass,
+                                                parentNodeId, referenceTypeId);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
                             "AddNodes: The parent reference is invalid "
@@ -716,7 +721,6 @@ AddNode_typeCheckAddRefs(UA_Server *server, UA_Session *session, const UA_NodeId
             typeDefinitionId = &baseObjectType;
     }
 
- get_type:
     /* Get the node type. There must be a typedefinition for variables, objects
      * and type-nodes. See the above checks. */
     if(!UA_NodeId_isNull(typeDefinitionId)) {
@@ -724,7 +728,7 @@ AddNode_typeCheckAddRefs(UA_Server *server, UA_Session *session, const UA_NodeId
         type = UA_Nodestore_get(server, typeDefinitionId);
         if(!type) {
             UA_LOG_INFO_SESSION(server->config.logger, session,
-                                "AddNodes: Node type not found in nodestore");
+                                "AddNodes: Node type not found");
             retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
             goto cleanup;
         }
@@ -767,7 +771,7 @@ AddNode_typeCheckAddRefs(UA_Server *server, UA_Session *session, const UA_NodeId
 
         /* See if the type has the correct node class. For type-nodes, we know
          * that type has the same nodeClass from checkParentReference. */
-        if(!server->bootstrapNS0 && node->nodeClass == UA_NODECLASS_VARIABLE) {
+        if(node->nodeClass == UA_NODECLASS_VARIABLE) {
             if(((const UA_VariableTypeNode*)type)->isAbstract) {
                 /* Abstract variable is allowed if parent is a children of a base data variable */
                 const UA_NodeId variableTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
@@ -787,7 +791,7 @@ AddNode_typeCheckAddRefs(UA_Server *server, UA_Session *session, const UA_NodeId
             }
         }
 
-        if(!server->bootstrapNS0 && node->nodeClass == UA_NODECLASS_OBJECT) {
+        if(node->nodeClass == UA_NODECLASS_OBJECT) {
             if(((const UA_ObjectTypeNode*)type)->isAbstract) {
                 /* Object node created of an abstract ObjectType. Only allowed
                  * if within BaseObjectType folder */
@@ -805,21 +809,6 @@ AddNode_typeCheckAddRefs(UA_Server *server, UA_Session *session, const UA_NodeId
         }
     }
 
-    /* Check if all attributes hold the constraints of the type now. The initial
-     * attributes must type-check. The constructor might change the attributes
-     * again. Then, the changes are type-checked by the normal write service. */
-    if(type && (node->nodeClass == UA_NODECLASS_VARIABLE ||
-                node->nodeClass == UA_NODECLASS_VARIABLETYPE)) {
-        retval = typeCheckVariableNode(server, session, (const UA_VariableNode*)node,
-                                       (const UA_VariableTypeNode*)type, parentNodeId);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_INFO_SESSION(server->config.logger, session,
-                                "AddNodes: Type-checking the variable node "
-                                "failed with error code %s", UA_StatusCode_name(retval));
-            goto cleanup;
-        }
-    }
-
     /* Add reference to the parent */
     if(!UA_NodeId_isNull(parentNodeId)) {
         if(UA_NodeId_isNull(referenceTypeId)) {
@@ -858,11 +847,9 @@ AddNode_typeCheckAddRefs(UA_Server *server, UA_Session *session, const UA_NodeId
 
 /* Create the node and add it to the nodestore. But don't typecheck and add
  * references so far */
-static UA_StatusCode
+UA_StatusCode
 AddNode_raw(UA_Server *server, UA_Session *session, void *nodeContext,
             const UA_AddNodesItem *item, UA_NodeId *outNewNodeId) {
-    UA_assert(outNewNodeId);
-
     /* Do not check access for server */
     if(session != &adminSession && server->config.accessControl.allowAddNode &&
        !server->config.accessControl.allowAddNode(server, &server->config.accessControl,
@@ -908,23 +895,6 @@ AddNode_raw(UA_Server *server, UA_Session *session, void *nodeContext,
         return retval;
     }
 
-    /* Use attributes from the typedefinition */
-    if(!server->bootstrapNS0 &&
-       (node->nodeClass == UA_NODECLASS_VARIABLE ||
-        node->nodeClass == UA_NODECLASS_VARIABLETYPE)) {
-        /* Use attributes from the type. The value and value constraints are the
-         * same for the variable and variabletype attribute structs. */
-        retval = useVariableTypeAttributes(server, session,
-                                           (UA_VariableNode*)node, item);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_INFO_SESSION(server->config.logger, session,
-                                "AddNodes: Using attributes from the variable type "
-                                "failed with error code %s", UA_StatusCode_name(retval));
-            UA_Nodestore_delete(server, node);
-            return retval;
-        }
-    }
-
     /* Add the node to the nodestore */
     retval = UA_Nodestore_insert(server, node, outNewNodeId);
     if(retval != UA_STATUSCODE_GOOD)
@@ -936,7 +906,7 @@ AddNode_raw(UA_Server *server, UA_Session *session, void *nodeContext,
 }
 
 /* Prepare the node, then add it to the nodestore */
-UA_StatusCode
+static UA_StatusCode
 Operation_addNode_begin(UA_Server *server, UA_Session *session, void *nodeContext,
                         const UA_AddNodesItem *item, const UA_NodeId *parentNodeId,
                         const UA_NodeId *referenceTypeId, UA_NodeId *outNewNodeId) {
@@ -953,10 +923,11 @@ Operation_addNode_begin(UA_Server *server, UA_Session *session, void *nodeContex
         return retval;
 
     /* Typecheck and add references to parent and type definition */
-    retval = AddNode_typeCheckAddRefs(server, session, outNewNodeId, parentNodeId,
-                                      referenceTypeId, &item->typeDefinition.nodeId);
+    retval = AddNode_addRefs(server, session, outNewNodeId, parentNodeId,
+                             referenceTypeId, &item->typeDefinition.nodeId);
     if(retval != UA_STATUSCODE_GOOD)
         UA_Server_deleteNode(server, *outNewNodeId, UA_TRUE);
+
     if(outNewNodeId == &newId)
         UA_NodeId_deleteMembers(&newId);
     return retval;
@@ -964,7 +935,7 @@ Operation_addNode_begin(UA_Server *server, UA_Session *session, void *nodeContex
 
 /* Children, references, type-checking, constructors. */
 UA_StatusCode
-Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId) {
+AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
     /* Get the node */
@@ -976,27 +947,61 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
 
     /* Instantiate variables and objects */
     if(node->nodeClass == UA_NODECLASS_VARIABLE ||
+       node->nodeClass == UA_NODECLASS_VARIABLETYPE ||
        node->nodeClass == UA_NODECLASS_OBJECT) {
         /* Get the type node */
         type = getNodeType(server, node);
         if(!type) {
+            if(server->bootstrapNS0)
+                goto constructor;
             UA_LOG_INFO_SESSION(server->config.logger, session,
-                                "AddNodes: Node type not found in nodestore");
+                                "AddNodes: Node type not found");
             retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
             goto cleanup;
         }
 
+        /* Use attributes from the type. The value and value constraints are the
+         * same for the variable and variabletype attribute structs. */
+        if(node->nodeClass == UA_NODECLASS_VARIABLE ||
+           node->nodeClass == UA_NODECLASS_VARIABLETYPE) {
+            retval = useVariableTypeAttributes(server, session,
+                                               (const UA_VariableNode**)&node,
+                                               (const UA_VariableTypeNode*)type);
+            if(retval != UA_STATUSCODE_GOOD) {
+                UA_LOG_INFO_SESSION(server->config.logger, session,
+                                    "AddNodes: Using attributes from the variable type "
+                                    "failed with error code %s", UA_StatusCode_name(retval));
+                goto cleanup;
+            }
+
+            /* Check if all attributes hold the constraints of the type now. The initial
+             * attributes must type-check. The constructor might change the attributes
+             * again. Then, the changes are type-checked by the normal write service. */
+            retval = typeCheckVariableNode(server, session, (const UA_VariableNode*)node,
+                                           (const UA_VariableTypeNode*)type);
+            if(retval != UA_STATUSCODE_GOOD) {
+                UA_LOG_INFO_SESSION(server->config.logger, session,
+                                    "AddNodes: Type-checking the variable node "
+                                    "failed with error code %s", UA_StatusCode_name(retval));
+                goto cleanup;
+            }
+        }
+
         /* Add (mandatory) child nodes from the type definition */
-        retval = addChildren(server, session, node, type);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_INFO_SESSION(server->config.logger, session,
-                                "AddNodes: Adding child nodes failed with error code %s",
-                                UA_StatusCode_name(retval));
-            goto cleanup;
+        if(node->nodeClass == UA_NODECLASS_VARIABLE ||
+           node->nodeClass == UA_NODECLASS_OBJECT) {
+            retval = addChildren(server, session, node, type);
+            if(retval != UA_STATUSCODE_GOOD) {
+                UA_LOG_INFO_SESSION(server->config.logger, session,
+                                    "AddNodes: Adding child nodes failed with error code %s",
+                                    UA_StatusCode_name(retval));
+                goto cleanup;
+            }
         }
     }
 
     /* Call the constructor(s) */
+ constructor:
     retval = callConstructors(server, session, node, type);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
@@ -1024,7 +1029,7 @@ Operation_addNode(UA_Server *server, UA_Session *session, void *nodeContext,
 
     /* AddNodes_finish */
     result->statusCode =
-        Operation_addNode_finish(server, session, &result->addedNodeId);
+        AddNode_finish(server, session, &result->addedNodeId);
 
     /* If finishing failed, the node was deleted */
     if(result->statusCode != UA_STATUSCODE_GOOD)
@@ -1107,7 +1112,7 @@ UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
 
 UA_StatusCode
 UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId) {
-    return Operation_addNode_finish(server, &adminSession, &nodeId);
+    return AddNode_finish(server, &adminSession, &nodeId);
 }
 
 /****************/
@@ -1536,13 +1541,13 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requested
         goto cleanup;
 
     /* Typecheck and add references to parent and type definition */
-    retval = AddNode_typeCheckAddRefs(server, &adminSession, outNewNodeId, &parentNodeId,
-                                      &referenceTypeId, &typeDefinition);
+    retval = AddNode_addRefs(server, &adminSession, outNewNodeId, &parentNodeId,
+                             &referenceTypeId, &typeDefinition);
     if(retval != UA_STATUSCODE_GOOD)
         goto cleanup;
 
     /* Call the constructors */
-    retval = Operation_addNode_finish(server, &adminSession, outNewNodeId);
+    retval = AddNode_finish(server, &adminSession, outNewNodeId);
 
  cleanup:
     if(outNewNodeId == &newNodeId)
@@ -1658,7 +1663,7 @@ UA_Server_addMethodNodeEx_finish(UA_Server *server, const UA_NodeId nodeId,
     retval |= UA_Server_setMethodNode_callback(server, nodeId, method);
 
     /* Call finish to add the parent reference */
-    retval |= Operation_addNode_finish(server, &adminSession, &nodeId);
+    retval |= AddNode_finish(server, &adminSession, &nodeId);
 
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Server_deleteNode(server, nodeId, true);