package org.basex.core.cmd; import static org.basex.core.Text.*; import java.io.IOException; import org.basex.build.Builder; import org.basex.build.DiskBuilder; import org.basex.build.Parser; import org.basex.core.BaseXException; import org.basex.core.CommandBuilder; import org.basex.core.Commands.Cmd; import org.basex.core.Context; import org.basex.core.User; import org.basex.data.Data; import org.basex.data.DiskData; import org.basex.data.MetaData; import org.basex.index.IndexToken.IndexType; import org.basex.io.IO; import org.basex.io.IOFile; import org.basex.io.serial.BuilderSerializer; import org.basex.io.serial.Serializer; import org.basex.util.Util; import org.basex.util.list.IntList; /** * Evaluates the 'optimize all' command and rebuilds all data structures of * the currently opened database. This effectively eliminates all fragmentation * and can lead to significant space savings after updates. * * @author BaseX Team 2005-12, BSD License * @author Leo Woerteler */ public final class OptimizeAll extends ACreate { /** Current pre value. */ int pre; /** Data size. */ private int size; /** * Default constructor. */ public OptimizeAll() { super(DATAREF | User.WRITE); } @Override protected boolean run() { try { final Data data = context.data(); optimizeAll(data, context, this); final Open open = new Open(data.meta.name); return open.run(context) ? info(DB_OPTIMIZED_X, data.meta.name, perf) : error(open.info()); } catch(final IOException ex) { Util.debug(ex); return error(Util.message(ex)); } } @Override public double prog() { return (double) pre / size; } @Override public boolean stoppable() { return false; } @Override public String det() { return CREATE_STATS_D; } @Override public void build(final CommandBuilder cb) { cb.init(Cmd.OPTIMIZE + " " + ALL); } /** * Optimizes all data structures. Recreates the database, drops the * old instance and renames the recreated instance. * @param data disk data * @param ctx database context * @param cmd command reference, or {@code null} * @throws IOException I/O Exception during index rebuild * @throws BaseXException database exception */ public static void optimizeAll(final Data data, final Context ctx, final OptimizeAll cmd) throws IOException { if(!(data instanceof DiskData)) throw new BaseXException(NO_MAINMEM); final DiskData old = (DiskData) data; final MetaData m = old.meta; if(cmd != null) cmd.size = m.size; // check if database is also pinned by other users if(ctx.datas.pins(m.name) > 1) throw new BaseXException(DB_PINNED_X, m.name); // find unique temporary database name final String tname = ctx.mprop.random(m.name); // build database and index structures final DiskBuilder builder = new DiskBuilder(tname, new DBParser(old, cmd), ctx); try { final DiskData d = builder.build(); if(m.createtext) create(IndexType.TEXT, d, cmd); if(m.createattr) create(IndexType.ATTRIBUTE, d, cmd); if(m.createftxt) create(IndexType.FULLTEXT, d, cmd); if(m.createpath) create(IndexType.PATH, d, cmd); d.meta.filesize = m.filesize; d.meta.users = m.users; d.meta.dirty = true; // move binary files final IOFile bin = data.meta.binaries(); if(bin.exists()) bin.rename(d.meta.binaries()); final IOFile upd = old.updateFile(); if(upd.exists()) Copy.copy(upd.file(), d.updateFile().file()); d.close(); } finally { try { builder.close(); } catch(final IOException ex) { Util.debug(ex); } } Close.close(data, ctx); // drop old database and rename temporary to final name // usually, no exceptions should be thrown here anymore if(!DropDB.drop(m.name, ctx.mprop)) throw new BaseXException(DB_NOT_DROPPED_X, m.name); if(!AlterDB.alter(tname, m.name, ctx.mprop)) throw new BaseXException(DB_NOT_RENAMED_X, tname); } /** * Parser for rebuilding existing databases. * * @author BaseX Team 2005-12, BSD License * @author Leo Woerteler */ private static final class DBParser extends Parser { /** Disk data. */ private final DiskData data; /** Calling command (can be {@code null}). */ final OptimizeAll cmd; /** * Constructor. * @param d disk data * @param c calling command (can be {@code null}) */ DBParser(final DiskData d, final OptimizeAll c) { super(d.meta.original.isEmpty() ? null : IO.get(d.meta.original)); data = d; cmd = c; } @Override public void parse(final Builder build) throws IOException { final Serializer ser = new BuilderSerializer(build) { @Override protected void startOpen(final byte[] t) throws IOException { super.startOpen(t); if(cmd != null) cmd.pre++; } @Override protected void openDoc(final byte[] name) throws IOException { super.openDoc(name); if(cmd != null) cmd.pre++; } }; final IntList il = data.resources.docs(); for(int i = 0, is = il.size(); i < is; i++) ser.node(data, il.get(i)); } } }