001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.command.dml;
007:
008: import java.io.BufferedInputStream;
009: import java.io.BufferedReader;
010: import java.io.IOException;
011: import java.io.InputStream;
012: import java.io.OutputStream;
013: import java.io.Reader;
014: import java.io.Writer;
015: import java.sql.Connection;
016: import java.sql.PreparedStatement;
017: import java.sql.ResultSet;
018: import java.sql.SQLException;
019: import java.util.Comparator;
020:
021: import org.h2.command.Parser;
022: import org.h2.constant.SysProperties;
023: import org.h2.constraint.Constraint;
024: import org.h2.engine.Comment;
025: import org.h2.engine.Constants;
026: import org.h2.engine.Database;
027: import org.h2.engine.DbObject;
028: import org.h2.engine.FunctionAlias;
029: import org.h2.engine.Right;
030: import org.h2.engine.Role;
031: import org.h2.engine.Session;
032: import org.h2.engine.Setting;
033: import org.h2.engine.User;
034: import org.h2.engine.UserAggregate;
035: import org.h2.engine.UserDataType;
036: import org.h2.expression.ExpressionColumn;
037: import org.h2.index.Cursor;
038: import org.h2.index.Index;
039: import org.h2.message.Message;
040: import org.h2.result.LocalResult;
041: import org.h2.result.Row;
042: import org.h2.schema.Constant;
043: import org.h2.schema.Schema;
044: import org.h2.schema.Sequence;
045: import org.h2.schema.TriggerObject;
046: import org.h2.table.Column;
047: import org.h2.table.PlanItem;
048: import org.h2.table.Table;
049: import org.h2.util.AutoCloseInputStream;
050: import org.h2.util.ByteUtils;
051: import org.h2.util.FileUtils;
052: import org.h2.util.IOUtils;
053: import org.h2.util.MathUtils;
054: import org.h2.util.ObjectArray;
055: import org.h2.util.StringUtils;
056: import org.h2.value.Value;
057: import org.h2.value.ValueLob;
058: import org.h2.value.ValueString;
059:
060: /**
061: * This class represents the statement
062: * SCRIPT
063: */
064: public class ScriptCommand extends ScriptBase {
065:
066: private boolean passwords;
067: private boolean data;
068: private boolean settings;
069: private boolean drop;
070: private boolean simple;
071: private LocalResult result;
072: private byte[] lineSeparator;
073: private byte[] buffer;
074: private boolean tempLobTableCreated;
075: private int nextLobId;
076: private int lobBlockSize = Constants.IO_BUFFER_SIZE;
077: private static final String TEMP_LOB_FILENAME = "system_temp_lob.db";
078:
079: public ScriptCommand(Session session) {
080: super (session);
081: }
082:
083: public boolean isQuery() {
084: return true;
085: }
086:
087: // TODO lock all tables for 'script' command
088:
089: public void setData(boolean data) {
090: this .data = data;
091: }
092:
093: public void setPasswords(boolean passwords) {
094: this .passwords = passwords;
095: }
096:
097: public void setSettings(boolean settings) {
098: this .settings = settings;
099: }
100:
101: public void setLobBlockSize(long blockSize) {
102: this .lobBlockSize = MathUtils.convertLongToInt(blockSize);
103: }
104:
105: public void setDrop(boolean drop) {
106: this .drop = drop;
107: }
108:
109: public LocalResult queryMeta() throws SQLException {
110: LocalResult result = createResult();
111: result.done();
112: return result;
113: }
114:
115: private LocalResult createResult() {
116: ObjectArray cols = new ObjectArray();
117: cols.add(new ExpressionColumn(session.getDatabase(),
118: new Column("SCRIPT", Value.STRING)));
119: return new LocalResult(session, cols, 1);
120: }
121:
122: public LocalResult query(int maxrows) throws SQLException {
123: session.getUser().checkAdmin();
124: reset();
125: try {
126: result = createResult();
127: deleteStore();
128: openOutput();
129: if (out != null) {
130: buffer = new byte[Constants.IO_BUFFER_SIZE];
131: }
132: Database db = session.getDatabase();
133: if (settings) {
134: ObjectArray settings = db.getAllSettings();
135: for (int i = 0; i < settings.size(); i++) {
136: Setting setting = (Setting) settings.get(i);
137: if (setting
138: .getName()
139: .equals(
140: SetTypes
141: .getTypeName(SetTypes.CREATE_BUILD))) {
142: // don't add CREATE_BUILD to the script
143: // (it is only set when creating the database)
144: continue;
145: }
146: add(setting.getCreateSQL(), false);
147: }
148: }
149: if (out != null) {
150: add("", true);
151: }
152: ObjectArray users = db.getAllUsers();
153: for (int i = 0; i < users.size(); i++) {
154: User user = (User) users.get(i);
155: add(user.getCreateSQL(passwords, true), false);
156: }
157: ObjectArray roles = db.getAllRoles();
158: for (int i = 0; i < roles.size(); i++) {
159: Role role = (Role) roles.get(i);
160: add(role.getCreateSQL(), false);
161: }
162: ObjectArray schemas = db.getAllSchemas();
163: for (int i = 0; i < schemas.size(); i++) {
164: Schema schema = (Schema) schemas.get(i);
165: add(schema.getCreateSQL(), false);
166: }
167: ObjectArray datatypes = db.getAllUserDataTypes();
168: for (int i = 0; i < datatypes.size(); i++) {
169: UserDataType datatype = (UserDataType) datatypes.get(i);
170: if (drop) {
171: add(datatype.getDropSQL(), false);
172: }
173: add(datatype.getCreateSQL(), false);
174: }
175: ObjectArray constants = db
176: .getAllSchemaObjects(DbObject.CONSTANT);
177: for (int i = 0; i < constants.size(); i++) {
178: Constant constant = (Constant) constants.get(i);
179: add(constant.getCreateSQL(), false);
180: }
181: ObjectArray functionAliases = db.getAllFunctionAliases();
182: for (int i = 0; i < functionAliases.size(); i++) {
183: FunctionAlias alias = (FunctionAlias) functionAliases
184: .get(i);
185: if (drop) {
186: add(alias.getDropSQL(), false);
187: }
188: add(alias.getCreateSQL(), false);
189: }
190: ObjectArray aggregates = db.getAllAggregates();
191: for (int i = 0; i < aggregates.size(); i++) {
192: UserAggregate agg = (UserAggregate) aggregates.get(i);
193: if (drop) {
194: add(agg.getDropSQL(), false);
195: }
196: add(agg.getCreateSQL(), false);
197: }
198: ObjectArray tables = db
199: .getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
200: // sort by id, so that views are after tables and views on views
201: // after the base views
202: tables.sort(new Comparator() {
203: public int compare(Object o1, Object o2) {
204: Table t1 = (Table) o1;
205: Table t2 = (Table) o2;
206: return t1.getId() - t2.getId();
207: }
208: });
209: for (int i = 0; i < tables.size(); i++) {
210: Table table = (Table) tables.get(i);
211: table.lock(session, false, false);
212: String sql = table.getCreateSQL();
213: if (sql == null) {
214: // null for metadata tables
215: continue;
216: }
217: if (drop) {
218: add(table.getDropSQL(), false);
219: }
220: }
221: ObjectArray sequences = db
222: .getAllSchemaObjects(DbObject.SEQUENCE);
223: for (int i = 0; i < sequences.size(); i++) {
224: Sequence sequence = (Sequence) sequences.get(i);
225: if (drop) {
226: add(sequence.getDropSQL(), false);
227: }
228: add(sequence.getCreateSQL(), false);
229: }
230: for (int i = 0; i < tables.size(); i++) {
231: Table table = (Table) tables.get(i);
232: table.lock(session, false, false);
233: String sql = table.getCreateSQL();
234: if (sql == null) {
235: // null for metadata tables
236: continue;
237: }
238: String tableType = table.getTableType();
239: add(sql, false);
240: if (Table.TABLE.equals(tableType)) {
241: if (table.canGetRowCount()) {
242: String rowcount = "-- "
243: + table.getRowCount(session)
244: + " = SELECT COUNT(*) FROM "
245: + table.getSQL();
246: add(rowcount, false);
247: }
248: if (data) {
249: PlanItem plan = table.getBestPlanItem(session,
250: null);
251: Index index = plan.getIndex();
252: Cursor cursor = index.find(session, null, null);
253: Column[] columns = table.getColumns();
254: StringBuffer buff = new StringBuffer();
255: buff.append("INSERT INTO ");
256: buff.append(table.getSQL());
257: buff.append('(');
258: for (int j = 0; j < columns.length; j++) {
259: if (j > 0) {
260: buff.append(", ");
261: }
262: buff.append(Parser
263: .quoteIdentifier(columns[j]
264: .getName()));
265: }
266: buff.append(") VALUES");
267: if (!simple) {
268: buff.append('\n');
269: }
270: buff.append('(');
271: String ins = buff.toString();
272: buff = null;
273: while (cursor.next()) {
274: Row row = cursor.get();
275: if (buff == null) {
276: buff = new StringBuffer(ins);
277: } else {
278: buff.append(",\n(");
279: }
280: for (int j = 0; j < row.getColumnCount(); j++) {
281: if (j > 0) {
282: buff.append(", ");
283: }
284: Value v = row.getValue(j);
285: if (v.getPrecision() > lobBlockSize) {
286: int id;
287: if (v.getType() == Value.CLOB) {
288: id = writeLobStream((ValueLob) v);
289: buff
290: .append("SYSTEM_COMBINE_CLOB("
291: + id + ")");
292: } else if (v.getType() == Value.BLOB) {
293: id = writeLobStream((ValueLob) v);
294: buff
295: .append("SYSTEM_COMBINE_BLOB("
296: + id + ")");
297: } else {
298: buff.append(v.getSQL());
299: }
300: } else {
301: buff.append(v.getSQL());
302: }
303: }
304: buff.append(")");
305: if (simple
306: || buff.length() > Constants.IO_BUFFER_SIZE) {
307: add(buff.toString(), true);
308: buff = null;
309: }
310: }
311: if (buff != null) {
312: add(buff.toString(), true);
313: }
314: }
315: }
316: ObjectArray indexes = table.getIndexes();
317: for (int j = 0; indexes != null && j < indexes.size(); j++) {
318: Index index = (Index) indexes.get(j);
319: if (!index.getIndexType().belongsToConstraint()) {
320: add(index.getCreateSQL(), false);
321: }
322: }
323: }
324: if (tempLobTableCreated) {
325: add("DROP TABLE IF EXISTS SYSTEM_LOB_STREAM", true);
326: add("CALL SYSTEM_COMBINE_BLOB(-1)", true);
327: add("DROP ALIAS IF EXISTS SYSTEM_COMBINE_CLOB", true);
328: add("DROP ALIAS IF EXISTS SYSTEM_COMBINE_BLOB", true);
329: tempLobTableCreated = false;
330: }
331: ObjectArray constraints = db
332: .getAllSchemaObjects(DbObject.CONSTRAINT);
333: constraints.sort(new Comparator() {
334: public int compare(Object o1, Object o2) {
335: Constraint c1 = (Constraint) o1;
336: Constraint c2 = (Constraint) o2;
337: return c1.compareTo(c2);
338: }
339: });
340: for (int i = 0; i < constraints.size(); i++) {
341: Constraint constraint = (Constraint) constraints.get(i);
342: add(constraint.getCreateSQLWithoutIndexes(), false);
343: }
344: ObjectArray triggers = db
345: .getAllSchemaObjects(DbObject.TRIGGER);
346: for (int i = 0; i < triggers.size(); i++) {
347: TriggerObject trigger = (TriggerObject) triggers.get(i);
348: add(trigger.getCreateSQL(), false);
349: }
350: ObjectArray rights = db.getAllRights();
351: for (int i = 0; i < rights.size(); i++) {
352: Right right = (Right) rights.get(i);
353: add(right.getCreateSQL(), false);
354: }
355: ObjectArray comments = db.getAllComments();
356: for (int i = 0; i < comments.size(); i++) {
357: Comment comment = (Comment) comments.get(i);
358: add(comment.getCreateSQL(), false);
359: }
360: if (out != null) {
361: out.close();
362: }
363: } catch (IOException e) {
364: throw Message.convertIOException(e, fileName);
365: } finally {
366: closeIO();
367: }
368: result.done();
369: LocalResult r = result;
370: reset();
371: return r;
372: }
373:
374: private int writeLobStream(ValueLob v) throws IOException,
375: SQLException {
376: if (!tempLobTableCreated) {
377: add(
378: "CREATE TABLE IF NOT EXISTS SYSTEM_LOB_STREAM(ID INT, PART INT, CDATA VARCHAR, BDATA BINARY, PRIMARY KEY(ID, PART))",
379: true);
380: add("CREATE ALIAS IF NOT EXISTS SYSTEM_COMBINE_CLOB FOR \""
381: + this .getClass().getName() + ".combineClob\"",
382: true);
383: add("CREATE ALIAS IF NOT EXISTS SYSTEM_COMBINE_BLOB FOR \""
384: + this .getClass().getName() + ".combineBlob\"",
385: true);
386: tempLobTableCreated = true;
387: }
388: int id = nextLobId++;
389: switch (v.getType()) {
390: case Value.BLOB: {
391: byte[] bytes = new byte[lobBlockSize];
392: InputStream in = v.getInputStream();
393: try {
394: for (int i = 0;; i++) {
395: StringBuffer buff = new StringBuffer(
396: lobBlockSize * 2);
397: buff.append("INSERT INTO SYSTEM_LOB_STREAM VALUES("
398: + id + ", " + i + ", NULL, '");
399: int len = IOUtils.readFully(in, bytes, 0,
400: lobBlockSize);
401: if (len <= 0) {
402: break;
403: }
404: buff.append(ByteUtils.convertBytesToString(bytes,
405: len));
406: buff.append("')");
407: String sql = buff.toString();
408: add(sql, true);
409: }
410: } finally {
411: IOUtils.closeSilently(in);
412: }
413: break;
414: }
415: case Value.CLOB: {
416: char[] chars = new char[lobBlockSize];
417: Reader in = v.getReader();
418: try {
419: for (int i = 0;; i++) {
420: StringBuffer buff = new StringBuffer(
421: lobBlockSize * 2);
422: buff.append("INSERT INTO SYSTEM_LOB_STREAM VALUES("
423: + id + ", " + i + ", ");
424: int len = IOUtils
425: .readFully(in, chars, lobBlockSize);
426: if (len < 0) {
427: break;
428: }
429: buff.append(StringUtils.quoteStringSQL(new String(
430: chars, 0, len)));
431: buff.append(", NULL)");
432: String sql = buff.toString();
433: add(sql, true);
434: }
435: } finally {
436: IOUtils.closeSilently(in);
437: }
438: break;
439: }
440: default:
441: throw Message.getInternalError("type:" + v.getType());
442: }
443: return id;
444: }
445:
446: /**
447: * Combine a BLOB.
448: * This method is called from the script.
449: * When calling with id -1, the file is deleted.
450: *
451: * @param conn a connection
452: * @param id the lob id
453: * @return a stream for the combined data
454: */
455: public static InputStream combineBlob(Connection conn, int id)
456: throws SQLException, IOException {
457: if (id < 0) {
458: FileUtils.delete(TEMP_LOB_FILENAME);
459: return null;
460: }
461: ResultSet rs = getLobStream(conn, "BDATA", id);
462: OutputStream out = FileUtils.openFileOutputStream(
463: TEMP_LOB_FILENAME, false);
464: while (rs.next()) {
465: InputStream in = rs.getBinaryStream(1);
466: IOUtils.copyAndCloseInput(in, out);
467: }
468: out.close();
469: deleteLobStream(conn, id);
470: return openAutoCloseInput();
471: }
472:
473: /**
474: * Combine a CLOB.
475: * This method is called from the script.
476: *
477: * @param conn a connection
478: * @param id the lob id
479: * @return a reader for the combined data
480: */
481: public static Reader combineClob(Connection conn, int id)
482: throws SQLException, IOException {
483: ResultSet rs = getLobStream(conn, "CDATA", id);
484: Writer out = FileUtils.openFileWriter(TEMP_LOB_FILENAME, false);
485: while (rs.next()) {
486: Reader in = new BufferedReader(rs.getCharacterStream(1));
487: IOUtils.copyAndCloseInput(in, out);
488: }
489: out.close();
490: deleteLobStream(conn, id);
491: return IOUtils.getReader(openAutoCloseInput());
492: }
493:
494: private static ResultSet getLobStream(Connection conn,
495: String column, int id) throws SQLException {
496: PreparedStatement prep = conn.prepareStatement("SELECT "
497: + column
498: + " FROM SYSTEM_LOB_STREAM WHERE ID=? ORDER BY PART");
499: prep.setInt(1, id);
500: return prep.executeQuery();
501: }
502:
503: private static void deleteLobStream(Connection conn, int id)
504: throws SQLException {
505: PreparedStatement prep = conn
506: .prepareStatement("DELETE FROM SYSTEM_LOB_STREAM WHERE ID=?");
507: prep.setInt(1, id);
508: prep.execute();
509: }
510:
511: private static InputStream openAutoCloseInput() throws IOException {
512: InputStream in = FileUtils
513: .openFileInputStream(TEMP_LOB_FILENAME);
514: in = new BufferedInputStream(in);
515: return new AutoCloseInputStream(in);
516: }
517:
518: private void reset() throws SQLException {
519: result = null;
520: buffer = null;
521: lineSeparator = StringUtils
522: .utf8Encode(SysProperties.LINE_SEPARATOR);
523: }
524:
525: private void add(String s, boolean insert) throws SQLException,
526: IOException {
527: if (s == null) {
528: return;
529: }
530: s += ";";
531: if (out != null) {
532: byte[] buff = StringUtils.utf8Encode(s);
533: int len = MathUtils.roundUp(buff.length
534: + lineSeparator.length, Constants.FILE_BLOCK_SIZE);
535: buffer = ByteUtils.copy(buff, buffer);
536:
537: if (len > buffer.length) {
538: buffer = new byte[len];
539: }
540: System.arraycopy(buff, 0, buffer, 0, buff.length);
541: for (int i = buff.length; i < len - lineSeparator.length; i++) {
542: buffer[i] = ' ';
543: }
544: for (int j = 0, i = len - lineSeparator.length; i < len; i++, j++) {
545: buffer[i] = lineSeparator[j];
546: }
547: out.write(buffer, 0, len);
548: if (!insert) {
549: Value[] row = new Value[1];
550: row[0] = ValueString.get(s);
551: result.addRow(row);
552: }
553: } else {
554: Value[] row = new Value[1];
555: row[0] = ValueString.get(s);
556: result.addRow(row);
557: }
558: }
559:
560: public void setSimple(boolean simple) {
561: this.simple = simple;
562: }
563:
564: }
|