view src/main/java/nl/cwi/monetdb/util/SQLRestore.java @ 350:54137aeb1f92

Update Copyright year.
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Thu, 02 Jan 2020 14:42:27 +0100 (2020-01-02)
parents 8a96a4a13528
children
line wrap: on
line source
/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0.  If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V.
 */

package nl.cwi.monetdb.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;

import nl.cwi.monetdb.mcl.io.BufferedMCLReader;
import nl.cwi.monetdb.mcl.io.BufferedMCLWriter;
import nl.cwi.monetdb.mcl.net.MapiSocket;

/**
 * Use this class to restore an SQL dump file.
 */
public final class SQLRestore {

	private final String _host;
	private final int _port;
	private final String _user;
	private final String _password;
	private final String _dbName;

	public SQLRestore(final String host, final int port, final String user, final String password, final String dbName) throws IOException {
		if (host == null || user == null || password == null || dbName == null)
			throw new NullPointerException();
		_host = host;
		_port = port;
		_user = user;
		_password = password;
		_dbName = dbName;
	}

	private static final class ServerResponseReader implements Runnable {
		private final BufferedMCLReader _is;
		private final AtomicBoolean _errorState = new AtomicBoolean(false);
		private String _errorMessage = null;

		ServerResponseReader(final BufferedMCLReader is) {
			_is = is;
		}

		public void run() {
			try {
				while (true) {
					final String line = _is.readLine();
					if (line == null)
						break;
					final int result = _is.getLineType();
					switch (result) {
					case BufferedMCLReader.ERROR:
						_errorMessage = line;
						_errorState.set(true);
						return;
					default:
						// do nothing...
					}
				}
			} catch (IOException e) {
				_errorMessage = e.getMessage();
				_errorState.set(true);
			} finally {
				try {
					_is.close();
				} catch (IOException e) {
					// ignore errors
				}
			}
		}

		/**
		 * @return whether the server has responded with an error. Any
		 *         error is regarded as fatal.
		 */
		public boolean inErrorState() {
			return _errorState.get();
		}

		/**
		 * @return the error message if inErrorState() is true. Behaviour is
		 *		not defined if called before inErrorState is true.
		 */
		public String getErrorMessage() {
			return _errorMessage;
		}
	}

	/**
	 * Restores a given SQL dump to the database.
	 *
	 * @param source file object
	 * @throws IOException when IO exception occurred
	 */
	public void restore(final File source) throws IOException {
		final MapiSocket ms = new MapiSocket();
		try {
			ms.setLanguage("sql");
			ms.setDatabase(_dbName);
			ms.connect(_host, _port, _user, _password);

			final BufferedMCLWriter os = ms.getWriter();
			final BufferedMCLReader reader = ms.getReader();

			final ServerResponseReader srr = new ServerResponseReader(reader);

			final Thread responseReaderThread = new Thread(srr);
			responseReaderThread.start();
			try {
				// FIXME: we assume here that the dump is in system's default encoding
				final BufferedReader sourceData = new BufferedReader(new java.io.FileReader(source));
				try {
					os.write('s'); // signal that a new statement (or series of) is coming
					while(!srr.inErrorState()) {
						final char[] buf = new char[4096];
						final int result = sourceData.read(buf);
						if (result < 0)
							break;
						os.write(buf, 0, result);
					}

					os.flush(); // mark the end of the statement (or series of)
					os.close();
				} finally {
					sourceData.close();
				}
			} finally {
				try {
					responseReaderThread.join();
				} catch (InterruptedException e) {
					throw new IOException(e.getMessage());
				}

				// if the server signalled an error, we should respect it...
				if (srr.inErrorState()) {
					throw new IOException(srr.getErrorMessage());
				}
			}
		} catch (nl.cwi.monetdb.mcl.MCLException e) {
			throw new IOException(e.getMessage());
		} catch (nl.cwi.monetdb.mcl.parser.MCLParseException e) {
			throw new IOException(e.getMessage());
		} finally {
			ms.close();
		}
	}

	public void close() {
		// do nothing at the moment...
	}


	public static void main(String[] args) throws IOException {
		if (args.length != 6) {
			System.err.println("USAGE: java " + SQLRestore.class.getName() +
					" <host> <port> <user> <password> <dbname> <dumpfile>");
			System.exit(1);
		}

		// parse arguments
		final String host = args[0];
		final int port = Integer.parseInt(args[1]); // FIXME: catch NumberFormatException
		final String user = args[2];
		final String password = args[3];
		final String dbName = args[4];
		final File dumpFile = new File(args[5]);

		// check arguments
		if (!dumpFile.isFile() || !dumpFile.canRead()) {
			System.err.println("Cannot read: " + dumpFile);
			System.exit(1);
		}

		final SQLRestore md = new SQLRestore(host, port, user, password, dbName);
		try {
			System.out.println("Start restoring " + dumpFile);
			long duration = -System.currentTimeMillis();
			md.restore(dumpFile);
			duration += System.currentTimeMillis();
			System.out.println("Restoring took: " + duration + "ms");
		} finally {
			md.close();
		}
	}
}