use of org.exist.storage.btree.Value in project exist by eXist-db.
the class ConsistencyCheck method checkXMLTree.
/**
* Check the persistent DOM of a document. The method traverses the entire node tree and checks it for consistency, including node relationships,
* child and attribute counts etc.
*
* @param doc the document to check
* @return null if the document is consistent, an error report otherwise.
*/
public ErrorReport checkXMLTree(final DocumentImpl doc) {
final DOMFile domDb = ((NativeBroker) broker).getDOMFile();
return new DOMTransaction<ErrorReport>(this, domDb, () -> broker.getBrokerPool().getLockManager().acquireBtreeWriteLock(domDb.getLockName()), doc) {
public ErrorReport start() {
EmbeddedXMLStreamReader reader = null;
try {
final Node root = doc.getFirstChild();
reader = (EmbeddedXMLStreamReader) broker.getXMLStreamReader((NodeHandle) root, true);
boolean attribsAllowed = false;
int expectedAttribs = 0;
int attributeCount = 0;
while (reader.hasNext()) {
final int status = reader.next();
final NodeId nodeId = (NodeId) reader.getProperty(EmbeddedXMLStreamReader.PROPERTY_NODE_ID);
if ((status != XMLStreamReader.END_ELEMENT) && !elementStack.isEmpty()) {
final ElementNode parent = elementStack.peek();
parent.childCount++;
// test parent-child relation
if (!nodeId.isChildOf(parent.elem.getNodeId())) {
return new ErrorReport.ResourceError(ErrorReport.NODE_HIERARCHY, "Node " + nodeId + " is not a child of " + parent.elem.getNodeId());
}
// test sibling relation
if ((parent.prevSibling != null) && !(nodeId.isSiblingOf(parent.prevSibling) && (nodeId.compareTo(parent.prevSibling) > 0))) {
return new ErrorReport.ResourceError(ErrorReport.INCORRECT_NODE_ID, "Node " + nodeId + " is not a sibling of " + parent.prevSibling);
}
parent.prevSibling = nodeId;
}
switch(status) {
case XMLStreamReader.ATTRIBUTE:
{
attributeCount++;
break;
}
case XMLStreamReader.END_ELEMENT:
{
if (elementStack.isEmpty()) {
return new org.exist.backup.ErrorReport.ResourceError(ErrorReport.NODE_HIERARCHY, "Error in node hierarchy: received END_ELEMENT event " + "but stack was empty!");
}
final ElementNode lastElem = elementStack.pop();
if (lastElem.childCount != lastElem.elem.getChildCount()) {
return new ErrorReport.ResourceError(org.exist.backup.ErrorReport.NODE_HIERARCHY, "Element reports incorrect child count: expected " + lastElem.elem.getChildCount() + " but found " + lastElem.childCount);
}
break;
}
case XMLStreamReader.START_ELEMENT:
{
if (nodeId.getTreeLevel() <= defaultIndexDepth) {
// check dom.dbx btree, which maps the node
// id to the node's storage address
// look up the node id and check if the
// returned storage address is correct
final NativeBroker.NodeRef nodeRef = new NativeBroker.NodeRef(doc.getDocId(), nodeId);
try {
final long p = domDb.findValue(nodeRef);
if (p != reader.getCurrentPosition()) {
final Value v = domDb.get(p);
if (v == null) {
return new ErrorReport.IndexError(ErrorReport.DOM_INDEX, "Failed to access node " + nodeId + " through dom.dbx index. Wrong storage address. Expected: " + p + "; got: " + reader.getCurrentPosition() + " - ", doc.getDocId());
}
}
} catch (final Exception e) {
e.printStackTrace();
return new ErrorReport.IndexError(ErrorReport.DOM_INDEX, "Failed to access node " + nodeId + " through dom.dbx index.", e, doc.getDocId());
}
}
final IStoredNode node = reader.getNode();
if (node.getNodeType() != Node.ELEMENT_NODE) {
return new org.exist.backup.ErrorReport.ResourceError(ErrorReport.INCORRECT_NODE_TYPE, "Expected an element node, received node of type " + node.getNodeType());
}
elementStack.push(new ElementNode((ElementImpl) node));
attribsAllowed = true;
attributeCount = 0;
expectedAttribs = reader.getAttributeCount();
break;
}
default:
{
if (attribsAllowed) {
if (attributeCount != expectedAttribs) {
return new org.exist.backup.ErrorReport.ResourceError(ErrorReport.INCORRECT_NODE_TYPE, "Wrong number of attributes. Expected: " + expectedAttribs + "; found: " + attributeCount);
}
}
attribsAllowed = false;
break;
}
}
}
if (!elementStack.isEmpty()) {
return new org.exist.backup.ErrorReport.ResourceError(ErrorReport.NODE_HIERARCHY, "Error in node hierarchy: reached end of tree but " + "stack was not empty!");
}
return null;
} catch (final IOException | XMLStreamException e) {
e.printStackTrace();
return new org.exist.backup.ErrorReport.ResourceError(ErrorReport.RESOURCE_ACCESS_FAILED, e.getMessage(), e);
} finally {
elementStack.clear();
if (reader != null) {
try {
reader.close();
} catch (final XMLStreamException e) {
e.printStackTrace();
}
}
}
}
}.run();
}
use of org.exist.storage.btree.Value in project exist by eXist-db.
the class WriteOverflowPageLoggable method read.
@Override
public void read(final ByteBuffer in) {
pageNum = in.getInt();
nextPage = in.getInt();
final int len = in.getShort();
final byte[] data = new byte[len];
in.get(data);
value = new Value(data);
}
use of org.exist.storage.btree.Value in project exist by eXist-db.
the class BFile method get.
/**
* Retrieve value at logical address pointer from page
*
* @param page the data page
* @param pointer the pointer to the value
*
* @return the value or null if there is no value
*
* @throws IOException if an I/O error occurs
*/
protected Value get(final DataPage page, final long pointer) throws IOException {
final short tid = StorageAddress.tidFromPointer(pointer);
final int offset = page.findValuePosition(tid);
final byte[] data = page.getData();
if (offset < 0 || offset > data.length) {
LOG.error("wrong pointer (tid: {}{}) in file {}; offset = {}", tid, page.getPageInfo(), FileUtils.fileName(getFile()), offset);
return null;
}
final int l = ByteConversion.byteToInt(data, offset);
if (l + 6 > data.length) {
LOG.error("{} wrong data length in page {}: expected={}; found={}", FileUtils.fileName(getFile()), page.getPageNum(), l + 6, data.length);
return null;
}
dataCache.add(page.getFirstPage());
final Value v = new Value(data, offset + 4, l);
v.setAddress(pointer);
return v;
}
use of org.exist.storage.btree.Value in project exist by eXist-db.
the class RawNodeIterator method next.
@Override
public Value next() {
Value nextValue = null;
try (final ManagedLock<ReentrantLock> domFileLock = lockManager.acquireBtreeReadLock(db.getLockName())) {
db.setOwnerObject(broker);
long backLink = 0;
do {
final DOMFile.DOMFilePageHeader pageHeader = page.getPageHeader();
// Next value larger than length of the current page?
if (offset >= pageHeader.getDataLength()) {
// Load next page in chain
long nextPage = pageHeader.getNextDataPage();
if (nextPage == Paged.Page.NO_PAGE) {
SanityCheck.TRACE("Bad link to next page " + page.page.getPageInfo() + "; previous: " + pageHeader.getPreviousDataPage() + "; offset = " + offset + "; lastTupleID = " + lastTupleID);
// TODO : throw exception here ? -pb
return null;
}
pageNum = nextPage;
page = db.getDOMPage(nextPage);
db.addToBuffer(page);
offset = 0;
}
// Extract the tuple id
lastTupleID = ByteConversion.byteToShort(page.data, offset);
offset += DOMFile.LENGTH_TID;
// Check if this is just a link to a relocated node
if (ItemId.isLink(lastTupleID)) {
// Skip this
offset += DOMFile.LENGTH_FORWARD_LOCATION;
continue;
}
// Read data length
short valueLength = ByteConversion.byteToShort(page.data, offset);
offset += DOMFile.LENGTH_DATA_LENGTH;
if (valueLength < 0) {
LOG.error("Got negative length{} at offset {}!!!", valueLength, offset);
LOG.debug(db.debugPageContents(page));
// TODO : throw an exception right now ?
}
if (ItemId.isRelocated(lastTupleID)) {
// found a relocated node. Read the original address
backLink = ByteConversion.byteToLong(page.data, offset);
offset += DOMFile.LENGTH_ORIGINAL_LOCATION;
}
// Overflow page? load the overflow value
if (valueLength == DOMFile.OVERFLOW) {
valueLength = DOMFile.LENGTH_OVERFLOW_LOCATION;
final long overflow = ByteConversion.byteToLong(page.data, offset);
offset += DOMFile.LENGTH_OVERFLOW_LOCATION;
try {
final byte[] odata = db.getOverflowValue(overflow);
nextValue = new Value(odata);
} catch (final Exception e) {
LOG.error("Exception while loading overflow value: {}; originating page: {}", e.getMessage(), page.page.getPageInfo());
}
// normal node
} else {
try {
nextValue = new Value(page.data, offset, valueLength);
offset += valueLength;
} catch (final Exception e) {
LOG.error("Error while deserializing node: {}", e.getMessage(), e);
LOG.error("Reading from offset: {}; len = {}", offset, valueLength);
LOG.debug(db.debugPageContents(page));
throw new RuntimeException(e);
}
}
if (nextValue == null) {
LOG.error("illegal node on page {}; tupleID = {}; next = {}; prev = {}; offset = {}; len = {}", page.getPageNum(), ItemId.getId(lastTupleID), page.getPageHeader().getNextDataPage(), page.getPageHeader().getPreviousDataPage(), offset - valueLength, page.getPageHeader().getDataLength());
// TODO : throw exception here ? -pb
return null;
}
if (ItemId.isRelocated(lastTupleID)) {
nextValue.setAddress(backLink);
} else {
nextValue.setAddress(StorageAddress.createPointer((int) pageNum, ItemId.getId(lastTupleID)));
}
} while (nextValue == null);
return nextValue;
} catch (final LockException e) {
LOG.error("Failed to acquire read lock on {}", FileUtils.fileName(db.getFile()));
// TODO : throw exception here ? -pb
return null;
}
}
use of org.exist.storage.btree.Value in project exist by eXist-db.
the class EmbeddedXMLStreamReader method readAttributes.
private void readAttributes() {
if (attributes == null) {
final ElementEvent parent = elementStack.peek();
final int count = getAttributeCount();
attributes = new AttrList();
for (int i = 0; i < count; i++) {
final Value v = iterator.next();
AttrImpl.addToList(broker, v.data(), v.start(), v.getLength(), attributes);
parent.incrementChild();
}
}
}
Aggregations