1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.command.dml; |
7 | |
8 | import java.io.BufferedInputStream; |
9 | import java.io.BufferedReader; |
10 | import java.io.IOException; |
11 | import java.io.InputStream; |
12 | import java.io.Reader; |
13 | import java.nio.charset.Charset; |
14 | import java.sql.Connection; |
15 | import java.sql.PreparedStatement; |
16 | import java.sql.ResultSet; |
17 | import java.sql.SQLException; |
18 | import java.util.ArrayList; |
19 | import java.util.Collection; |
20 | import java.util.Collections; |
21 | import java.util.Comparator; |
22 | import java.util.Set; |
23 | |
24 | import org.h2.api.ErrorCode; |
25 | import org.h2.command.CommandInterface; |
26 | import org.h2.command.Parser; |
27 | import org.h2.constraint.Constraint; |
28 | import org.h2.engine.Comment; |
29 | import org.h2.engine.Constants; |
30 | import org.h2.engine.Database; |
31 | import org.h2.engine.DbObject; |
32 | import org.h2.engine.Right; |
33 | import org.h2.engine.Role; |
34 | import org.h2.engine.Session; |
35 | import org.h2.engine.Setting; |
36 | import org.h2.engine.SysProperties; |
37 | import org.h2.engine.User; |
38 | import org.h2.engine.UserAggregate; |
39 | import org.h2.engine.UserDataType; |
40 | import org.h2.expression.Expression; |
41 | import org.h2.expression.ExpressionColumn; |
42 | import org.h2.index.Cursor; |
43 | import org.h2.index.Index; |
44 | import org.h2.message.DbException; |
45 | import org.h2.result.LocalResult; |
46 | import org.h2.result.ResultInterface; |
47 | import org.h2.result.Row; |
48 | import org.h2.schema.Constant; |
49 | import org.h2.schema.Schema; |
50 | import org.h2.schema.SchemaObject; |
51 | import org.h2.schema.Sequence; |
52 | import org.h2.schema.TriggerObject; |
53 | import org.h2.table.Column; |
54 | import org.h2.table.PlanItem; |
55 | import org.h2.table.Table; |
56 | import org.h2.util.IOUtils; |
57 | import org.h2.util.MathUtils; |
58 | import org.h2.util.StatementBuilder; |
59 | import org.h2.util.StringUtils; |
60 | import org.h2.util.Utils; |
61 | import org.h2.value.Value; |
62 | import org.h2.value.ValueString; |
63 | |
64 | /** |
65 | * This class represents the statement |
66 | * SCRIPT |
67 | */ |
68 | public class ScriptCommand extends ScriptBase { |
69 | |
70 | private Charset charset = Constants.UTF8; |
71 | private Set<String> schemaNames; |
72 | private Collection<Table> tables; |
73 | private boolean passwords; |
74 | |
75 | // true if we're generating the INSERT..VALUES statements for row values |
76 | private boolean data; |
77 | private boolean settings; |
78 | |
79 | // true if we're generating the DROP statements |
80 | private boolean drop; |
81 | private boolean simple; |
82 | private LocalResult result; |
83 | private String lineSeparatorString; |
84 | private byte[] lineSeparator; |
85 | private byte[] buffer; |
86 | private boolean tempLobTableCreated; |
87 | private int nextLobId; |
88 | private int lobBlockSize = Constants.IO_BUFFER_SIZE; |
89 | |
90 | public ScriptCommand(Session session) { |
91 | super(session); |
92 | } |
93 | |
94 | @Override |
95 | public boolean isQuery() { |
96 | return true; |
97 | } |
98 | |
99 | // TODO lock all tables for 'script' command |
100 | |
101 | public void setSchemaNames(Set<String> schemaNames) { |
102 | this.schemaNames = schemaNames; |
103 | } |
104 | |
105 | public void setTables(Collection<Table> tables) { |
106 | this.tables = tables; |
107 | } |
108 | |
109 | public void setData(boolean data) { |
110 | this.data = data; |
111 | } |
112 | |
113 | public void setPasswords(boolean passwords) { |
114 | this.passwords = passwords; |
115 | } |
116 | |
117 | public void setSettings(boolean settings) { |
118 | this.settings = settings; |
119 | } |
120 | |
121 | public void setLobBlockSize(long blockSize) { |
122 | this.lobBlockSize = MathUtils.convertLongToInt(blockSize); |
123 | } |
124 | |
125 | public void setDrop(boolean drop) { |
126 | this.drop = drop; |
127 | } |
128 | |
129 | @Override |
130 | public ResultInterface queryMeta() { |
131 | LocalResult r = createResult(); |
132 | r.done(); |
133 | return r; |
134 | } |
135 | |
136 | private LocalResult createResult() { |
137 | Expression[] expressions = { new ExpressionColumn( |
138 | session.getDatabase(), new Column("SCRIPT", Value.STRING)) }; |
139 | return new LocalResult(session, expressions, 1); |
140 | } |
141 | |
142 | @Override |
143 | public ResultInterface query(int maxrows) { |
144 | session.getUser().checkAdmin(); |
145 | reset(); |
146 | Database db = session.getDatabase(); |
147 | if (schemaNames != null) { |
148 | for (String schemaName : schemaNames) { |
149 | Schema schema = db.findSchema(schemaName); |
150 | if (schema == null) { |
151 | throw DbException.get(ErrorCode.SCHEMA_NOT_FOUND_1, |
152 | schemaName); |
153 | } |
154 | } |
155 | } |
156 | try { |
157 | result = createResult(); |
158 | deleteStore(); |
159 | openOutput(); |
160 | if (out != null) { |
161 | buffer = new byte[Constants.IO_BUFFER_SIZE]; |
162 | } |
163 | if (settings) { |
164 | for (Setting setting : db.getAllSettings()) { |
165 | if (setting.getName().equals(SetTypes.getTypeName( |
166 | SetTypes.CREATE_BUILD))) { |
167 | // don't add CREATE_BUILD to the script |
168 | // (it is only set when creating the database) |
169 | continue; |
170 | } |
171 | add(setting.getCreateSQL(), false); |
172 | } |
173 | } |
174 | if (out != null) { |
175 | add("", true); |
176 | } |
177 | for (User user : db.getAllUsers()) { |
178 | add(user.getCreateSQL(passwords), false); |
179 | } |
180 | for (Role role : db.getAllRoles()) { |
181 | add(role.getCreateSQL(true), false); |
182 | } |
183 | for (Schema schema : db.getAllSchemas()) { |
184 | if (excludeSchema(schema)) { |
185 | continue; |
186 | } |
187 | add(schema.getCreateSQL(), false); |
188 | } |
189 | for (UserDataType datatype : db.getAllUserDataTypes()) { |
190 | if (drop) { |
191 | add(datatype.getDropSQL(), false); |
192 | } |
193 | add(datatype.getCreateSQL(), false); |
194 | } |
195 | for (SchemaObject obj : db.getAllSchemaObjects( |
196 | DbObject.CONSTANT)) { |
197 | if (excludeSchema(obj.getSchema())) { |
198 | continue; |
199 | } |
200 | Constant constant = (Constant) obj; |
201 | add(constant.getCreateSQL(), false); |
202 | } |
203 | |
204 | final ArrayList<Table> tables = db.getAllTablesAndViews(false); |
205 | // sort by id, so that views are after tables and views on views |
206 | // after the base views |
207 | Collections.sort(tables, new Comparator<Table>() { |
208 | @Override |
209 | public int compare(Table t1, Table t2) { |
210 | return t1.getId() - t2.getId(); |
211 | } |
212 | }); |
213 | |
214 | // Generate the DROP XXX ... IF EXISTS |
215 | for (Table table : tables) { |
216 | if (excludeSchema(table.getSchema())) { |
217 | continue; |
218 | } |
219 | if (excludeTable(table)) { |
220 | continue; |
221 | } |
222 | if (table.isHidden()) { |
223 | continue; |
224 | } |
225 | table.lock(session, false, false); |
226 | String sql = table.getCreateSQL(); |
227 | if (sql == null) { |
228 | // null for metadata tables |
229 | continue; |
230 | } |
231 | if (drop) { |
232 | add(table.getDropSQL(), false); |
233 | } |
234 | } |
235 | for (SchemaObject obj : db.getAllSchemaObjects( |
236 | DbObject.FUNCTION_ALIAS)) { |
237 | if (excludeSchema(obj.getSchema())) { |
238 | continue; |
239 | } |
240 | if (drop) { |
241 | add(obj.getDropSQL(), false); |
242 | } |
243 | add(obj.getCreateSQL(), false); |
244 | } |
245 | for (UserAggregate agg : db.getAllAggregates()) { |
246 | if (drop) { |
247 | add(agg.getDropSQL(), false); |
248 | } |
249 | add(agg.getCreateSQL(), false); |
250 | } |
251 | for (SchemaObject obj : db.getAllSchemaObjects( |
252 | DbObject.SEQUENCE)) { |
253 | if (excludeSchema(obj.getSchema())) { |
254 | continue; |
255 | } |
256 | Sequence sequence = (Sequence) obj; |
257 | if (drop) { |
258 | add(sequence.getDropSQL(), false); |
259 | } |
260 | add(sequence.getCreateSQL(), false); |
261 | } |
262 | |
263 | // Generate CREATE TABLE and INSERT...VALUES |
264 | int count = 0; |
265 | for (Table table : tables) { |
266 | if (excludeSchema(table.getSchema())) { |
267 | continue; |
268 | } |
269 | if (excludeTable(table)) { |
270 | continue; |
271 | } |
272 | if (table.isHidden()) { |
273 | continue; |
274 | } |
275 | table.lock(session, false, false); |
276 | String createTableSql = table.getCreateSQL(); |
277 | if (createTableSql == null) { |
278 | // null for metadata tables |
279 | continue; |
280 | } |
281 | final String tableType = table.getTableType(); |
282 | add(createTableSql, false); |
283 | final ArrayList<Constraint> constraints = table.getConstraints(); |
284 | if (constraints != null) { |
285 | for (Constraint constraint : constraints) { |
286 | if (Constraint.PRIMARY_KEY.equals( |
287 | constraint.getConstraintType())) { |
288 | add(constraint.getCreateSQLWithoutIndexes(), false); |
289 | } |
290 | } |
291 | } |
292 | if (Table.TABLE.equals(tableType)) { |
293 | if (table.canGetRowCount()) { |
294 | String rowcount = "-- " + |
295 | table.getRowCountApproximation() + |
296 | " +/- SELECT COUNT(*) FROM " + table.getSQL(); |
297 | add(rowcount, false); |
298 | } |
299 | if (data) { |
300 | count = generateInsertValues(count, table); |
301 | } |
302 | } |
303 | final ArrayList<Index> indexes = table.getIndexes(); |
304 | for (int j = 0; indexes != null && j < indexes.size(); j++) { |
305 | Index index = indexes.get(j); |
306 | if (!index.getIndexType().getBelongsToConstraint()) { |
307 | add(index.getCreateSQL(), false); |
308 | } |
309 | } |
310 | } |
311 | if (tempLobTableCreated) { |
312 | add("DROP TABLE IF EXISTS SYSTEM_LOB_STREAM", true); |
313 | add("CALL SYSTEM_COMBINE_BLOB(-1)", true); |
314 | add("DROP ALIAS IF EXISTS SYSTEM_COMBINE_CLOB", true); |
315 | add("DROP ALIAS IF EXISTS SYSTEM_COMBINE_BLOB", true); |
316 | tempLobTableCreated = false; |
317 | } |
318 | // Generate CREATE CONSTRAINT ... |
319 | final ArrayList<SchemaObject> constraints = db.getAllSchemaObjects( |
320 | DbObject.CONSTRAINT); |
321 | Collections.sort(constraints, new Comparator<SchemaObject>() { |
322 | @Override |
323 | public int compare(SchemaObject c1, SchemaObject c2) { |
324 | return ((Constraint) c1).compareTo((Constraint) c2); |
325 | } |
326 | }); |
327 | for (SchemaObject obj : constraints) { |
328 | if (excludeSchema(obj.getSchema())) { |
329 | continue; |
330 | } |
331 | Constraint constraint = (Constraint) obj; |
332 | if (excludeTable(constraint.getTable())) { |
333 | continue; |
334 | } |
335 | if (constraint.getTable().isHidden()) { |
336 | continue; |
337 | } |
338 | if (!Constraint.PRIMARY_KEY.equals(constraint.getConstraintType())) { |
339 | add(constraint.getCreateSQLWithoutIndexes(), false); |
340 | } |
341 | } |
342 | // Generate CREATE TRIGGER ... |
343 | for (SchemaObject obj : db.getAllSchemaObjects(DbObject.TRIGGER)) { |
344 | if (excludeSchema(obj.getSchema())) { |
345 | continue; |
346 | } |
347 | TriggerObject trigger = (TriggerObject) obj; |
348 | if (excludeTable(trigger.getTable())) { |
349 | continue; |
350 | } |
351 | add(trigger.getCreateSQL(), false); |
352 | } |
353 | // Generate GRANT ... |
354 | for (Right right : db.getAllRights()) { |
355 | Table table = right.getGrantedTable(); |
356 | if (table != null) { |
357 | if (excludeSchema(table.getSchema())) { |
358 | continue; |
359 | } |
360 | if (excludeTable(table)) { |
361 | continue; |
362 | } |
363 | } |
364 | add(right.getCreateSQL(), false); |
365 | } |
366 | // Generate COMMENT ON ... |
367 | for (Comment comment : db.getAllComments()) { |
368 | add(comment.getCreateSQL(), false); |
369 | } |
370 | if (out != null) { |
371 | out.close(); |
372 | } |
373 | } catch (IOException e) { |
374 | throw DbException.convertIOException(e, getFileName()); |
375 | } finally { |
376 | closeIO(); |
377 | } |
378 | result.done(); |
379 | LocalResult r = result; |
380 | reset(); |
381 | return r; |
382 | } |
383 | |
384 | private int generateInsertValues(int count, Table table) throws IOException { |
385 | PlanItem plan = table.getBestPlanItem(session, null, null, null); |
386 | Index index = plan.getIndex(); |
387 | Cursor cursor = index.find(session, null, null); |
388 | Column[] columns = table.getColumns(); |
389 | StatementBuilder buff = new StatementBuilder("INSERT INTO "); |
390 | buff.append(table.getSQL()).append('('); |
391 | for (Column col : columns) { |
392 | buff.appendExceptFirst(", "); |
393 | buff.append(Parser.quoteIdentifier(col.getName())); |
394 | } |
395 | buff.append(") VALUES"); |
396 | if (!simple) { |
397 | buff.append('\n'); |
398 | } |
399 | buff.append('('); |
400 | String ins = buff.toString(); |
401 | buff = null; |
402 | while (cursor.next()) { |
403 | Row row = cursor.get(); |
404 | if (buff == null) { |
405 | buff = new StatementBuilder(ins); |
406 | } else { |
407 | buff.append(",\n("); |
408 | } |
409 | for (int j = 0; j < row.getColumnCount(); j++) { |
410 | if (j > 0) { |
411 | buff.append(", "); |
412 | } |
413 | Value v = row.getValue(j); |
414 | if (v.getPrecision() > lobBlockSize) { |
415 | int id; |
416 | if (v.getType() == Value.CLOB) { |
417 | id = writeLobStream(v); |
418 | buff.append("SYSTEM_COMBINE_CLOB(" + id + ")"); |
419 | } else if (v.getType() == Value.BLOB) { |
420 | id = writeLobStream(v); |
421 | buff.append("SYSTEM_COMBINE_BLOB(" + id + ")"); |
422 | } else { |
423 | buff.append(v.getSQL()); |
424 | } |
425 | } else { |
426 | buff.append(v.getSQL()); |
427 | } |
428 | } |
429 | buff.append(')'); |
430 | count++; |
431 | if ((count & 127) == 0) { |
432 | checkCanceled(); |
433 | } |
434 | if (simple || buff.length() > Constants.IO_BUFFER_SIZE) { |
435 | add(buff.toString(), true); |
436 | buff = null; |
437 | } |
438 | } |
439 | if (buff != null) { |
440 | add(buff.toString(), true); |
441 | } |
442 | return count; |
443 | } |
444 | |
445 | private int writeLobStream(Value v) throws IOException { |
446 | if (!tempLobTableCreated) { |
447 | add("CREATE TABLE IF NOT EXISTS SYSTEM_LOB_STREAM" + |
448 | "(ID INT NOT NULL, PART INT NOT NULL, " + |
449 | "CDATA VARCHAR, BDATA BINARY)", |
450 | true); |
451 | add("CREATE PRIMARY KEY SYSTEM_LOB_STREAM_PRIMARY_KEY " + |
452 | "ON SYSTEM_LOB_STREAM(ID, PART)", true); |
453 | add("CREATE ALIAS IF NOT EXISTS " + "SYSTEM_COMBINE_CLOB FOR \"" + |
454 | this.getClass().getName() + ".combineClob\"", true); |
455 | add("CREATE ALIAS IF NOT EXISTS " + "SYSTEM_COMBINE_BLOB FOR \"" + |
456 | this.getClass().getName() + ".combineBlob\"", true); |
457 | tempLobTableCreated = true; |
458 | } |
459 | int id = nextLobId++; |
460 | switch (v.getType()) { |
461 | case Value.BLOB: { |
462 | byte[] bytes = new byte[lobBlockSize]; |
463 | InputStream input = v.getInputStream(); |
464 | try { |
465 | for (int i = 0;; i++) { |
466 | StringBuilder buff = new StringBuilder(lobBlockSize * 2); |
467 | buff.append("INSERT INTO SYSTEM_LOB_STREAM VALUES(" + id + |
468 | ", " + i + ", NULL, '"); |
469 | int len = IOUtils.readFully(input, bytes, lobBlockSize); |
470 | if (len <= 0) { |
471 | break; |
472 | } |
473 | buff.append(StringUtils.convertBytesToHex(bytes, len)).append("')"); |
474 | String sql = buff.toString(); |
475 | add(sql, true); |
476 | } |
477 | } finally { |
478 | IOUtils.closeSilently(input); |
479 | } |
480 | break; |
481 | } |
482 | case Value.CLOB: { |
483 | char[] chars = new char[lobBlockSize]; |
484 | Reader reader = v.getReader(); |
485 | try { |
486 | for (int i = 0;; i++) { |
487 | StringBuilder buff = new StringBuilder(lobBlockSize * 2); |
488 | buff.append("INSERT INTO SYSTEM_LOB_STREAM VALUES(" + id + ", " + i + ", "); |
489 | int len = IOUtils.readFully(reader, chars, lobBlockSize); |
490 | if (len == 0) { |
491 | break; |
492 | } |
493 | buff.append(StringUtils.quoteStringSQL(new String(chars, 0, len))). |
494 | append(", NULL)"); |
495 | String sql = buff.toString(); |
496 | add(sql, true); |
497 | } |
498 | } finally { |
499 | IOUtils.closeSilently(reader); |
500 | } |
501 | break; |
502 | } |
503 | default: |
504 | DbException.throwInternalError("type:" + v.getType()); |
505 | } |
506 | return id; |
507 | } |
508 | |
509 | /** |
510 | * Combine a BLOB. |
511 | * This method is called from the script. |
512 | * When calling with id -1, the file is deleted. |
513 | * |
514 | * @param conn a connection |
515 | * @param id the lob id |
516 | * @return a stream for the combined data |
517 | */ |
518 | public static InputStream combineBlob(Connection conn, int id) |
519 | throws SQLException { |
520 | if (id < 0) { |
521 | return null; |
522 | } |
523 | final ResultSet rs = getLobStream(conn, "BDATA", id); |
524 | return new InputStream() { |
525 | private InputStream current; |
526 | private boolean closed; |
527 | @Override |
528 | public int read() throws IOException { |
529 | while (true) { |
530 | try { |
531 | if (current == null) { |
532 | if (closed) { |
533 | return -1; |
534 | } |
535 | if (!rs.next()) { |
536 | close(); |
537 | return -1; |
538 | } |
539 | current = rs.getBinaryStream(1); |
540 | current = new BufferedInputStream(current); |
541 | } |
542 | int x = current.read(); |
543 | if (x >= 0) { |
544 | return x; |
545 | } |
546 | current = null; |
547 | } catch (SQLException e) { |
548 | throw DbException.convertToIOException(e); |
549 | } |
550 | } |
551 | } |
552 | @Override |
553 | public void close() throws IOException { |
554 | if (closed) { |
555 | return; |
556 | } |
557 | closed = true; |
558 | try { |
559 | rs.close(); |
560 | } catch (SQLException e) { |
561 | throw DbException.convertToIOException(e); |
562 | } |
563 | } |
564 | }; |
565 | } |
566 | |
567 | /** |
568 | * Combine a CLOB. |
569 | * This method is called from the script. |
570 | * |
571 | * @param conn a connection |
572 | * @param id the lob id |
573 | * @return a reader for the combined data |
574 | */ |
575 | public static Reader combineClob(Connection conn, int id) throws SQLException { |
576 | if (id < 0) { |
577 | return null; |
578 | } |
579 | final ResultSet rs = getLobStream(conn, "CDATA", id); |
580 | return new Reader() { |
581 | private Reader current; |
582 | private boolean closed; |
583 | @Override |
584 | public int read() throws IOException { |
585 | while (true) { |
586 | try { |
587 | if (current == null) { |
588 | if (closed) { |
589 | return -1; |
590 | } |
591 | if (!rs.next()) { |
592 | close(); |
593 | return -1; |
594 | } |
595 | current = rs.getCharacterStream(1); |
596 | current = new BufferedReader(current); |
597 | } |
598 | int x = current.read(); |
599 | if (x >= 0) { |
600 | return x; |
601 | } |
602 | current = null; |
603 | } catch (SQLException e) { |
604 | throw DbException.convertToIOException(e); |
605 | } |
606 | } |
607 | } |
608 | @Override |
609 | public void close() throws IOException { |
610 | if (closed) { |
611 | return; |
612 | } |
613 | closed = true; |
614 | try { |
615 | rs.close(); |
616 | } catch (SQLException e) { |
617 | throw DbException.convertToIOException(e); |
618 | } |
619 | } |
620 | @Override |
621 | public int read(char[] buffer, int off, int len) throws IOException { |
622 | if (len == 0) { |
623 | return 0; |
624 | } |
625 | int c = read(); |
626 | if (c == -1) { |
627 | return -1; |
628 | } |
629 | buffer[off] = (char) c; |
630 | int i = 1; |
631 | for (; i < len; i++) { |
632 | c = read(); |
633 | if (c == -1) { |
634 | break; |
635 | } |
636 | buffer[off + i] = (char) c; |
637 | } |
638 | return i; |
639 | } |
640 | }; |
641 | } |
642 | |
643 | private static ResultSet getLobStream(Connection conn, String column, int id) |
644 | throws SQLException { |
645 | PreparedStatement prep = conn.prepareStatement("SELECT " + column + |
646 | " FROM SYSTEM_LOB_STREAM WHERE ID=? ORDER BY PART"); |
647 | prep.setInt(1, id); |
648 | return prep.executeQuery(); |
649 | } |
650 | |
651 | private void reset() { |
652 | result = null; |
653 | buffer = null; |
654 | lineSeparatorString = SysProperties.LINE_SEPARATOR; |
655 | lineSeparator = lineSeparatorString.getBytes(charset); |
656 | } |
657 | |
658 | private boolean excludeSchema(Schema schema) { |
659 | if (schemaNames != null && !schemaNames.contains(schema.getName())) { |
660 | return true; |
661 | } |
662 | if (tables != null) { |
663 | // if filtering on specific tables, only include those schemas |
664 | for (Table table : schema.getAllTablesAndViews()) { |
665 | if (tables.contains(table)) { |
666 | return false; |
667 | } |
668 | } |
669 | return true; |
670 | } |
671 | return false; |
672 | } |
673 | |
674 | private boolean excludeTable(Table table) { |
675 | return tables != null && !tables.contains(table); |
676 | } |
677 | |
678 | private void add(String s, boolean insert) throws IOException { |
679 | if (s == null) { |
680 | return; |
681 | } |
682 | if (lineSeparator.length > 1 || lineSeparator[0] != '\n') { |
683 | s = StringUtils.replaceAll(s, "\n", lineSeparatorString); |
684 | } |
685 | s += ";"; |
686 | if (out != null) { |
687 | byte[] buff = s.getBytes(charset); |
688 | int len = MathUtils.roundUpInt(buff.length + |
689 | lineSeparator.length, Constants.FILE_BLOCK_SIZE); |
690 | buffer = Utils.copy(buff, buffer); |
691 | |
692 | if (len > buffer.length) { |
693 | buffer = new byte[len]; |
694 | } |
695 | System.arraycopy(buff, 0, buffer, 0, buff.length); |
696 | for (int i = buff.length; i < len - lineSeparator.length; i++) { |
697 | buffer[i] = ' '; |
698 | } |
699 | for (int j = 0, i = len - lineSeparator.length; i < len; i++, j++) { |
700 | buffer[i] = lineSeparator[j]; |
701 | } |
702 | out.write(buffer, 0, len); |
703 | if (!insert) { |
704 | Value[] row = { ValueString.get(s) }; |
705 | result.addRow(row); |
706 | } |
707 | } else { |
708 | Value[] row = { ValueString.get(s) }; |
709 | result.addRow(row); |
710 | } |
711 | } |
712 | |
713 | public void setSimple(boolean simple) { |
714 | this.simple = simple; |
715 | } |
716 | |
717 | public void setCharset(Charset charset) { |
718 | this.charset = charset; |
719 | } |
720 | |
721 | @Override |
722 | public int getType() { |
723 | return CommandInterface.SCRIPT; |
724 | } |
725 | |
726 | } |