Skip to content

Commit

Permalink
[FIX] Storage: correct abortion of CREATE commands. Closes #1334
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristianGruen committed Aug 18, 2016
1 parent f9a7afc commit d78ac40
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 197 deletions.
18 changes: 10 additions & 8 deletions basex-core/src/main/java/org/basex/build/Builder.java
Expand Up @@ -69,9 +69,17 @@ public abstract class Builder extends Job {
* @throws IOException I/O exception
*/
final void parse() throws IOException {
// add document node and parse document
parser.parse(this);
final Performance perf = Prop.debug ? new Performance() : null;
Util.debug(shortInfo() + DOTS);
try {
// add document node and parse document
parser.parse(this);
} finally {
parser.close();
}
meta.lastid = meta.size - 1;

if(Prop.debug) Util.errln(" " + perf + " (" + Performance.getMemory() + ')');
}

/**
Expand Down Expand Up @@ -196,12 +204,6 @@ public final double progressInfo() {
*/
public abstract DataClip dataClip() throws IOException;

/**
* Closes open references.
* @throws IOException I/O exception
*/
public abstract void close() throws IOException;

/**
* Adds a document node to the database.
* @param value name of the document
Expand Down
78 changes: 26 additions & 52 deletions basex-core/src/main/java/org/basex/build/DiskBuilder.java
@@ -1,6 +1,5 @@
package org.basex.build;

import static org.basex.core.Text.*;
import static org.basex.data.DataText.*;

import java.io.*;
Expand All @@ -23,7 +22,7 @@
* @author BaseX Team 2005-16, BSD License
* @author Christian Gruen
*/
public final class DiskBuilder extends Builder implements Closeable {
public final class DiskBuilder extends Builder {
/** Database table. */
private DataOutput tout;
/** Database texts. */
Expand All @@ -35,8 +34,6 @@ public final class DiskBuilder extends Builder implements Closeable {

/** Static options. */
private final StaticOptions sopts;
/** Closed flag. */
private boolean closed;
/** Debug counter. */
private int c;

Expand Down Expand Up @@ -72,67 +69,44 @@ public DiskData build() throws IOException {
elemNames = new Names(meta);
attrNames = new Names(meta);
try {
tout = new DataOutput(new TableOutput(meta, DATATBL));
xout = new DataOutput(meta.dbfile(DATATXT), bs);
vout = new DataOutput(meta.dbfile(DATAATV), bs);
sout = new DataOutput(meta.dbfile(DATATMP), bs);

final Performance perf = Prop.debug ? new Performance() : null;
Util.debug(shortInfo() + DOTS);
parse();
if(Prop.debug) Util.errln(" " + perf + " (" + Performance.getMemory() + ')');

} catch(final IOException ex) {
try { close(); } catch(final IOException ignore) { }
throw ex;
}
close();

// copy temporary values into database table
try(final DataInput in = new DataInput(meta.dbfile(DATATMP))) {
final TableAccess ta = new TableDiskAccess(meta, true);
try {
for(; spos < ssize; ++spos) ta.write4(in.readNum(), 8, in.readNum());
tout = new DataOutput(new TableOutput(meta, DATATBL));
xout = new DataOutput(meta.dbfile(DATATXT), bs);
vout = new DataOutput(meta.dbfile(DATAATV), bs);
sout = new DataOutput(meta.dbfile(DATATMP), bs);
parse();
} finally {
ta.close();
if(tout != null) tout.close();
if(xout != null) xout.close();
if(vout != null) vout.close();
if(sout != null) sout.close();
}
}
meta.dbfile(DATATMP).delete();

// return database instance
return new DiskData(meta, elemNames, attrNames, path, nspaces);
}
// copy temporary values into database table
try(final DataInput in = new DataInput(meta.dbfile(DATATMP))) {
final TableAccess ta = new TableDiskAccess(meta, true);
try {
for(; spos < ssize; ++spos) ta.write4(in.readNum(), 8, in.readNum());
} finally {
ta.close();
}
}
meta.dbfile(DATATMP).delete();

@Override
public void abort() {
try {
close();
} catch(final IOException ex) {
Util.debug(ex);
// return database instance
return new DiskData(meta, elemNames, attrNames, path, nspaces);

} catch(final Throwable th) {
DropDB.drop(meta.name, sopts);
throw th;
}
if(meta != null) DropDB.drop(meta.name, sopts);
}

@Override
public DataClip dataClip() throws IOException {
return new DataClip(build());
}

@Override
public void close() throws IOException {
if(closed) return;
closed = true;
if(tout != null) tout.close();
if(xout != null) xout.close();
if(vout != null) vout.close();
if(sout != null) sout.close();
parser.close();
tout = null;
xout = null;
vout = null;
sout = null;
}

@Override
protected void addDoc(final byte[] value) throws IOException {
tout.write1(Data.DOC);
Expand Down
18 changes: 2 additions & 16 deletions basex-core/src/main/java/org/basex/build/MemBuilder.java
@@ -1,7 +1,5 @@
package org.basex.build;

import static org.basex.core.Text.*;

import java.io.*;

import org.basex.data.*;
Expand Down Expand Up @@ -72,16 +70,10 @@ public DataClip dataClip() throws IOException {
init();
meta.assign(parser);
try {
final Performance perf = Prop.debug ? new Performance() : null;
Util.debug(shortInfo() + DOTS);
parse();
if(Prop.debug) Util.errln(" " + perf + " (" + Performance.getMemory() + ')');

} catch(final IOException ex) {
try { close(); } catch(final IOException ignore) { }
throw ex;
} finally {
if(data.meta.updindex) data.idmap.finish(data.meta.lastid);
}
close();
return new DataClip(data);
}

Expand All @@ -105,12 +97,6 @@ public Data data() {
return data;
}

@Override
public void close() throws IOException {
parser.close();
if(data.meta.updindex) data.idmap.finish(data.meta.lastid);
}

@Override
protected void addDoc(final byte[] value) {
data.doc(0, value);
Expand Down
3 changes: 0 additions & 3 deletions basex-core/src/main/java/org/basex/core/Command.java
Expand Up @@ -253,17 +253,14 @@ public final boolean run(final Context ctx, final OutputStream os) {
return run();
} catch(final JobException ex) {
// job was interrupted by the user or server
abort();
return error(INTERRUPTED);
} catch(final OutOfMemoryError ex) {
// out of memory
Performance.gc(2);
abort();
Util.debug(ex);
return error(OUT_OF_MEM + (perm == Perm.CREATE ? H_OUT_OF_MEM : ""));
} catch(final Throwable ex) {
// any other unexpected error
abort();
return error(Util.bug(ex) + Prop.NL + info);
} finally {
// flushes the output
Expand Down
5 changes: 2 additions & 3 deletions basex-core/src/main/java/org/basex/core/cmd/CreateDB.java
Expand Up @@ -96,8 +96,9 @@ protected boolean run() {
if(context.pinned(name)) return error(DB_PINNED_X, name);

// create disk-based instance
final DiskBuilder builder = pushJob(new DiskBuilder(name, parser, soptions, options));
try {
pushJob(new DiskBuilder(name, parser, soptions, options)).build().close();
builder.build().close();
} finally {
popJob();
}
Expand All @@ -122,13 +123,11 @@ protected boolean run() {
} catch(final JobException ex) {
throw ex;
} catch(final IOException ex) {
abort();
return error(Util.message(ex));
} catch(final Exception ex) {
// known exceptions:
// - IllegalArgumentException (UTF8, zip files)
Util.stack(ex);
abort();
return error(NOT_PARSED_X, parser.source);
}
}
Expand Down
102 changes: 55 additions & 47 deletions basex-core/src/main/java/org/basex/core/cmd/OptimizeAll.java
Expand Up @@ -27,11 +27,6 @@
* @author Leo Woerteler
*/
public final class OptimizeAll extends ACreate {
/** Current pre value. */
private int pre;
/** Data size. */
private int size;

/**
* Default constructor.
*/
Expand Down Expand Up @@ -69,21 +64,12 @@ public void databases(final LockResult lr) {
lr.write.add(DBLocking.CONTEXT);
}

@Override
public double progressInfo() {
return (double) pre / size;
}

@Override
public boolean stoppable() {
// database will be closed after optimize call
return false;
}

@Override
public String detailedInfo() {
return CREATE_STATS_D;
}

@Override
public void build(final CmdBuilder cb) {
cb.init(Cmd.OPTIMIZE + " " + S_ALL);
Expand Down Expand Up @@ -131,35 +117,46 @@ public static void optimizeAll(final Data data, final Context context,
options.set(MainOptions.MAXCATS, ometa.maxcats);

// build database and index structures
if(cmd != null) cmd.size = ometa.size;
final StaticOptions sopts = context.soptions;
final String tmpName = sopts.randomDbName(name);
final DBParser parser = new DBParser(odata, options, cmd);
try(final DiskBuilder builder = new DiskBuilder(tmpName, parser, sopts, options)) {
final DiskData ndata = builder.build();
final MetaData nmeta = ndata.meta;
try {
// adopt original meta data, create new index structures
nmeta.createtext = ometa.createtext;
nmeta.createattr = ometa.createattr;
nmeta.createtoken = ometa.createtoken;
nmeta.createft = ometa.createft;
nmeta.original = ometa.original;
nmeta.filesize = ometa.filesize;
nmeta.time = ometa.time;
nmeta.dirty = true;
CreateIndex.create(ndata, cmd);

// move binary files
final IOFile bin = ometa.binaries();
if(bin.exists()) bin.rename(nmeta.binaries());
} finally {
ndata.close();
}
}
final DBParser parser = new DBParser(odata, options);
final DiskBuilder builder = new DiskBuilder(tmpName, parser, sopts, options);
if(cmd != null) cmd.pushJob(builder);

// close old database instance, drop it and rename temporary database
// create new database with identical contents
final DiskData ndata;
try {
ndata = builder.build();
} finally {
if(cmd != null) cmd.popJob();
}
Close.close(odata, context);

// adopt original meta data, create new index structures
final MetaData nmeta = ndata.meta;
nmeta.createtext = ometa.createtext;
nmeta.createattr = ometa.createattr;
nmeta.createtoken = ometa.createtoken;
nmeta.createft = ometa.createft;
nmeta.original = ometa.original;
nmeta.filesize = ometa.filesize;
nmeta.time = ometa.time;
nmeta.dirty = true;
try {
CreateIndex.create(ndata, cmd);
} catch(final Throwable th) {
// index creation failed: delete temporary database
DropDB.drop(tmpName, sopts);
throw th;
} finally {
ndata.close();
}

// move binary files
final IOFile bin = ometa.binaries();
if(bin.exists()) bin.rename(nmeta.binaries());

// drop old database, rename temporary database
if(!DropDB.drop(name, sopts)) throw new BaseXException(DB_NOT_DROPPED_X, name);
if(!AlterDB.alter(tmpName, name, sopts)) throw new BaseXException(DB_NOT_RENAMED_X, tmpName);
}
Expand All @@ -173,19 +170,20 @@ public static void optimizeAll(final Data data, final Context context,
private static final class DBParser extends Parser {
/** Disk data. */
private final DiskData data;
/** Calling command (may be {@code null}). */
final OptimizeAll cmd;
/** Data size. */
private final int size;
/** Current pre value. */
private int pre;

/**
* Constructor.
* @param data disk data
* @param options main options
* @param cmd calling command (may be {@code null})
*/
DBParser(final DiskData data, final MainOptions options, final OptimizeAll cmd) {
DBParser(final DiskData data, final MainOptions options) {
super(data.meta.original.isEmpty() ? null : IO.get(data.meta.original), options);
this.data = data;
this.cmd = cmd;
this.size = data.meta.size;
}

@Override
Expand All @@ -194,19 +192,29 @@ public void parse(final Builder build) throws IOException {
@Override
protected void startOpen(final QNm name) throws IOException {
super.startOpen(name);
if(cmd != null) cmd.pre++;
pre++;
}

@Override
protected void openDoc(final byte[] name) throws IOException {
super.openDoc(name);
if(cmd != null) cmd.pre++;
pre++;
}
};

final IntList il = data.resources.docs();
final int is = il.size();
for(int i = 0; i < is; i++) ser.serialize(new DBNode(data, il.get(i)));
}

@Override
public double progressInfo() {
return (double) pre / size;
}

@Override
public String detailedInfo() {
return CREATE_STATS_D;
}
}
}

0 comments on commit d78ac40

Please sign in to comment.