blob: 72493a1dc761cd017e71846487aefd20cdbf4a1d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.iotdb.db.metadata.mnode;
import org.apache.iotdb.db.exception.metadata.AlignedTimeseriesException;
import org.apache.iotdb.db.exception.metadata.MetadataException;
import org.apache.iotdb.db.metadata.MetaUtils;
import org.apache.iotdb.db.metadata.PartialPath;
import org.apache.iotdb.db.metadata.logfile.MLogWriter;
import org.apache.iotdb.db.metadata.template.Template;
import org.apache.iotdb.db.qp.physical.sys.MNodePlan;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
/**
* This class is the implementation of Metadata Node. One MNode instance represents one node in the
* Metadata Tree
*/
public class InternalMNode extends MNode {
private static final long serialVersionUID = -770028375899514063L;
/**
* use in Measurement Node so it's protected suppress warnings reason: volatile for double
* synchronized check
*
* <p>This will be a ConcurrentHashMap instance
*/
@SuppressWarnings("squid:S3077")
protected transient volatile Map<String, IMNode> children = null;
// schema template
protected Template schemaTemplate = null;
/** Constructor of MNode. */
public InternalMNode(IMNode parent, String name) {
super(parent, name);
}
/** check whether the MNode has a child with the name */
@Override
public boolean hasChild(String name) {
return (children != null && children.containsKey(name));
}
/** get the child with the name */
@Override
public IMNode getChild(String name) {
IMNode child = null;
if (children != null) {
child = children.get(name);
}
return child;
}
/**
* add a child to current mnode
*
* @param name child's name
* @param child child's node
*/
@Override
public void addChild(String name, IMNode child) {
/* use cpu time to exchange memory
* measurementNode's children should be null to save memory
* add child method will only be called when writing MTree, which is not a frequent operation
*/
if (children == null) {
// double check, children is volatile
synchronized (this) {
if (children == null) {
children = new ConcurrentHashMap<>();
}
}
}
child.setParent(this);
children.putIfAbsent(name, child);
}
/**
* Add a child to the current mnode.
*
* <p>This method will not take the child's name as one of the inputs and will also make this
* Mnode be child node's parent. All is to reduce the probability of mistaken by users and be more
* convenient for users to use. And the return of this method is used to conveniently construct a
* chain of time series for users.
*
* @param child child's node
* @return return the MNode already added
*/
public IMNode addChild(IMNode child) {
/* use cpu time to exchange memory
* measurementNode's children should be null to save memory
* add child method will only be called when writing MTree, which is not a frequent operation
*/
if (children == null) {
// double check, children is volatile
synchronized (this) {
if (children == null) {
children = new ConcurrentHashMap<>();
}
}
}
child.setParent(this);
children.putIfAbsent(child.getName(), child);
return child;
}
/** delete a child */
@Override
public void deleteChild(String name) {
if (children != null) {
children.remove(name);
}
}
/**
* replace a child of this mnode
*
* @param oldChildName measurement name
* @param newChildNode new child node
*/
@Override
public void replaceChild(String oldChildName, IMNode newChildNode) {
IMNode oldChildNode = this.getChild(oldChildName);
if (oldChildNode == null) {
return;
}
// newChildNode builds parent-child relationship
Map<String, IMNode> grandChildren = oldChildNode.getChildren();
if (!grandChildren.isEmpty()) {
newChildNode.setChildren(grandChildren);
grandChildren.forEach(
(grandChildName, grandChildNode) -> grandChildNode.setParent(newChildNode));
}
if (newChildNode.isEntity() && oldChildNode.isEntity()) {
Map<String, IMeasurementMNode> grandAliasChildren =
((IEntityMNode) oldChildNode).getAliasChildren();
if (!grandAliasChildren.isEmpty()) {
((IEntityMNode) newChildNode).setAliasChildren(grandAliasChildren);
grandAliasChildren.forEach(
(grandAliasChildName, grandAliasChild) -> grandAliasChild.setParent(newChildNode));
}
((IEntityMNode) newChildNode).setUseTemplate(oldChildNode.isUseTemplate());
}
newChildNode.setSchemaTemplate(oldChildNode.getSchemaTemplate());
newChildNode.setParent(this);
this.deleteChild(oldChildName);
this.addChild(newChildNode.getName(), newChildNode);
}
@Override
public IMNode getChildOfAlignedTimeseries(String name) throws MetadataException {
IMNode node = null;
// for aligned timeseries
List<String> measurementList = MetaUtils.getMeasurementsInPartialPath(new PartialPath(name));
for (String measurement : measurementList) {
IMNode nodeOfMeasurement = getChild(measurement);
if (node == null) {
node = nodeOfMeasurement;
} else {
if (node != nodeOfMeasurement) {
throw new AlignedTimeseriesException(
"Cannot get node of children in different aligned timeseries", name);
}
}
}
return node;
}
@Override
public Map<String, IMNode> getChildren() {
if (children == null) {
return Collections.emptyMap();
}
return children;
}
@Override
public void setChildren(Map<String, IMNode> children) {
this.children = children;
}
/**
* get upper template of this node, remember we get nearest template alone this node to root
*
* @return upper template
*/
@Override
public Template getUpperTemplate() {
IMNode cur = this;
while (cur != null) {
if (cur.getSchemaTemplate() != null) {
return cur.getSchemaTemplate();
}
cur = cur.getParent();
}
return null;
}
@Override
public Template getSchemaTemplate() {
return schemaTemplate;
}
@Override
public void setSchemaTemplate(Template schemaTemplate) {
this.schemaTemplate = schemaTemplate;
}
/** get the count of all MeasurementMNode whose ancestor is current node */
@Override
public int getMeasurementMNodeCount() {
if (children == null) {
return 0;
}
int measurementMNodeCount = 0;
for (IMNode child : children.values()) {
measurementMNodeCount += child.getMeasurementMNodeCount();
}
return measurementMNodeCount;
}
@Override
public void serializeTo(MLogWriter logWriter) throws IOException {
serializeChildren(logWriter);
logWriter.serializeMNode(this);
}
void serializeChildren(MLogWriter logWriter) throws IOException {
if (children == null) {
return;
}
for (Entry<String, IMNode> entry : children.entrySet()) {
entry.getValue().serializeTo(logWriter);
}
}
public static InternalMNode deserializeFrom(MNodePlan plan) {
return new InternalMNode(null, plan.getName());
}
}