1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.jdbc.schema;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.PrintWriter;
24 import java.io.Writer;
25 import java.sql.Connection;
26 import java.sql.DatabaseMetaData;
27 import java.sql.SQLException;
28 import java.sql.Statement;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.LinkedHashSet;
34 import java.util.LinkedList;
35 import java.util.Set;
36 import javax.sql.DataSource;
37
38 import org.apache.commons.lang.StringUtils;
39 import org.apache.openjpa.conf.OpenJPAConfiguration;
40 import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
41 import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
42 import org.apache.openjpa.jdbc.sql.DBDictionary;
43 import org.apache.openjpa.jdbc.sql.SQLExceptions;
44 import org.apache.openjpa.lib.conf.Configurations;
45 import org.apache.openjpa.lib.log.Log;
46 import org.apache.openjpa.lib.util.Files;
47 import org.apache.openjpa.lib.util.Localizer;
48 import org.apache.openjpa.lib.util.Options;
49 import org.apache.openjpa.util.InvalidStateException;
50
51 /**
52 * The SchemaTool is used to manage the database schema. Note that the
53 * tool never adds or drops unique constraints from existing tables, because
54 * JDBC {@link DatabaseMetaData} does not include information on these
55 * constraints.
56 *
57 * @author Abe White
58 * @author Patrick Linskey
59 */
60 public class SchemaTool {
61
62 public static final String ACTION_ADD = "add";
63 public static final String ACTION_DROP = "drop";
64 public static final String ACTION_RETAIN = "retain";
65 public static final String ACTION_REFRESH = "refresh";
66 public static final String ACTION_BUILD = "build";
67 public static final String ACTION_REFLECT = "reflect";
68 public static final String ACTION_CREATEDB = "createDB";
69 public static final String ACTION_DROPDB = "dropDB";
70 public static final String ACTION_IMPORT = "import";
71 public static final String ACTION_EXPORT = "export";
72 public static final String ACTION_DELETE_TABLE_CONTENTS =
73 "deleteTableContents";
74
75 public static final String[] ACTIONS = new String[]{
76 ACTION_ADD,
77 ACTION_DROP,
78 ACTION_RETAIN,
79 ACTION_REFRESH,
80 ACTION_BUILD,
81 ACTION_REFLECT,
82 ACTION_CREATEDB,
83 ACTION_DROPDB,
84 ACTION_IMPORT,
85 ACTION_EXPORT,
86 ACTION_DELETE_TABLE_CONTENTS,
87 };
88
89 private static final Localizer _loc = Localizer.forPackage
90 (SchemaTool.class);
91
92 private final JDBCConfiguration _conf;
93 private final DataSource _ds;
94 private final Log _log;
95 private final DBDictionary _dict;
96 private final String _action;
97 private boolean _ignoreErrs = false;
98 private boolean _openjpaTables = false;
99 private boolean _dropTables = true;
100 private boolean _dropSeqs = true;
101 private boolean _pks = true;
102 private boolean _fks = true;
103 private boolean _indexes = true;
104 private boolean _seqs = true;
105 private PrintWriter _writer = null;
106 private SchemaGroup _group = null;
107 private SchemaGroup _db = null;
108 private boolean _fullDB = false;
109
110 /**
111 * Default constructor. Tools constructed this way will not have an
112 * action, so the {@link #run()} method will be a no-op.
113 */
114 public SchemaTool(JDBCConfiguration conf) {
115 this(conf, null);
116 }
117
118 /**
119 * Construct a tool to perform the given action.
120 */
121 public SchemaTool(JDBCConfiguration conf, String action) {
122 if (action != null && !Arrays.asList(ACTIONS).contains(action))
123 throw new IllegalArgumentException("action == " + action);
124
125 _conf = conf;
126 _action = action;
127 _ds = conf.getDataSource2(null);
128 _log = conf.getLog(JDBCConfiguration.LOG_SCHEMA);
129
130 // initialize this up-front; otherwise the dbdictionaryfactory might
131 // try to take a connection to initialize when we've already got one:
132 // bad news if the max pool is 1
133 _dict = _conf.getDBDictionaryInstance();
134 }
135
136 /**
137 * The action supplied on construction.
138 */
139 public String getAction() {
140 return _action;
141 }
142
143 /**
144 * If true, SQLExceptions thrown during schema manipulation will be
145 * printed but ignored.
146 */
147 public boolean getIgnoreErrors() {
148 return _ignoreErrs;
149 }
150
151 /**
152 * If true, SQLExceptions thrown during schema manipulation will be
153 * printed but ignored.
154 */
155 public void setIgnoreErrors(boolean ignoreErrs) {
156 _ignoreErrs = ignoreErrs;
157 }
158
159 /**
160 * Whether to act on special tables used by OpenJPA components
161 * for bookkeeping.
162 */
163 public boolean getOpenJPATables() {
164 return _openjpaTables;
165 }
166
167 /**
168 * Whether to act on special tables used by OpenJPA components
169 * for bookkeeping.
170 */
171 public void setOpenJPATables(boolean openjpaTables) {
172 _openjpaTables = openjpaTables;
173 }
174
175 /**
176 * If true, tables that appear to be unused will be dropped. Defaults to
177 * true.
178 */
179 public boolean getDropTables() {
180 return _dropTables;
181 }
182
183 /**
184 * If true, tables that appear to be unused will be dropped. Defaults to
185 * true.
186 */
187 public void setDropTables(boolean dropTables) {
188 _dropTables = dropTables;
189 }
190
191 /**
192 * If true, sequences that appear to be unused will be dropped. Defaults
193 * to true.
194 */
195 public boolean getDropSequences() {
196 return _dropSeqs;
197 }
198
199 /**
200 * If true, sequences that appear to be unused will be dropped. Defaults
201 * to true.
202 */
203 public void setDropSequences(boolean dropSeqs) {
204 _dropSeqs = dropSeqs;
205 if (dropSeqs)
206 setSequences(true);
207 }
208
209 /**
210 * Whether sequences should be manipulated. Defaults to true.
211 */
212 public boolean getSequences() {
213 return _seqs;
214 }
215
216 /**
217 * Whether sequences should be manipulated. Defaults to true.
218 */
219 public void setSequences(boolean seqs) {
220 _seqs = seqs;
221 }
222
223 /**
224 * Whether indexes on existing tables should be manipulated.
225 * Defaults to true.
226 */
227 public boolean getIndexes() {
228 return _indexes;
229 }
230
231 /**
232 * Whether indexes on existing tables should be manipulated.
233 * Defaults to true.
234 */
235 public void setIndexes(boolean indexes) {
236 _indexes = indexes;
237 }
238
239 /**
240 * Whether foreign keys on existing tables should be manipulated.
241 * Defaults to true.
242 */
243 public boolean getForeignKeys() {
244 return _fks;
245 }
246
247 /**
248 * Whether foreign keys on existing tables should be manipulated.
249 * Defaults to true.
250 */
251 public void setForeignKeys(boolean fks) {
252 _fks = fks;
253 }
254
255 /**
256 * Whether primary keys on existing tables should be manipulated.
257 * Defaults to true.
258 */
259 public boolean getPrimaryKeys() {
260 return _pks;
261 }
262
263 /**
264 * Whether primary keys on existing tables should be manipulated.
265 * Defaults to true.
266 */
267 public void setPrimaryKeys(boolean pks) {
268 _pks = pks;
269 }
270
271 /**
272 * The stream to write to for the creation of SQL scripts. If the
273 * stream is non-null, all SQL will be written to this stream rather than
274 * executed against the database.
275 */
276 public Writer getWriter() {
277 return _writer;
278 }
279
280 /**
281 * The stream to write to for the creation of SQL scripts. If the
282 * stream is non-null, all SQL will be written to this stream rather than
283 * executed against the database.
284 */
285 public void setWriter(Writer writer) {
286 if (writer == null)
287 _writer = null;
288 else if (writer instanceof PrintWriter)
289 _writer = (PrintWriter) writer;
290 else
291 _writer = new PrintWriter(writer);
292 }
293
294 /**
295 * Return the schema group the tool will act on.
296 */
297 public SchemaGroup getSchemaGroup() {
298 return _group;
299 }
300
301 /**
302 * Set the schema group the tool will act on.
303 */
304 public void setSchemaGroup(SchemaGroup group) {
305 _group = group;
306 }
307
308 ///////////
309 // Actions
310 ///////////
311
312 /**
313 * Run the tool action.
314 */
315 public void run()
316 throws SQLException {
317 if (_action == null)
318 return;
319
320 if (ACTION_ADD.equals(_action))
321 add();
322 else if (ACTION_DROP.equals(_action))
323 drop();
324 else if (ACTION_RETAIN.equals(_action))
325 retain();
326 else if (ACTION_REFRESH.equals(_action))
327 refresh();
328 else if (ACTION_BUILD.equals(_action))
329 build();
330 else if (ACTION_CREATEDB.equals(_action))
331 createDB();
332 else if (ACTION_DROPDB.equals(_action))
333 dropDB();
334 else if (ACTION_DELETE_TABLE_CONTENTS.equals(_action))
335 deleteTableContents();
336 }
337
338 /**
339 * Adds any components present in the schema repository but absent from
340 * the database. Package-private for testing.
341 */
342 void add()
343 throws SQLException {
344 add(getDBSchemaGroup(false), assertSchemaGroup());
345 }
346
347 /**
348 * Drops all schema components in the schema repository that also exist
349 * in the database. Package-private for testing.
350 */
351 void drop()
352 throws SQLException {
353 drop(getDBSchemaGroup(false), assertSchemaGroup());
354 }
355
356 /**
357 * Drops database components that are not mentioned in the schema
358 * repository. Package-private for testing.
359 */
360 void retain()
361 throws SQLException {
362 retain(getDBSchemaGroup(true), assertSchemaGroup(),
363 getDropTables(), getDropSequences());
364 }
365
366 /**
367 * Adds any components present in the schema repository but absent from
368 * the database, and drops unused database components.
369 * Package-private for testing.
370 */
371 void refresh()
372 throws SQLException {
373 SchemaGroup local = assertSchemaGroup();
374 SchemaGroup db = getDBSchemaGroup(true);
375 retain(db, local, getDropTables(), getDropSequences());
376 add(db, local);
377 }
378
379 /**
380 * Re-execute all SQL used for the creation of the current database;
381 * this action is usually used when creating SQL scripts.
382 * Package-private for testing.
383 */
384 void createDB()
385 throws SQLException {
386 SchemaGroup group = new SchemaGroup();
387 group.addSchema();
388 add(group, getDBSchemaGroup(true));
389 }
390
391 /**
392 * Re-execute all SQL used for the creation of the current database;
393 * this action is usually used when creating SQL scripts.
394 * Package-private for testing.
395 */
396 void build()
397 throws SQLException {
398 SchemaGroup group = new SchemaGroup();
399 group.addSchema();
400 add(group, assertSchemaGroup());
401 }
402
403 /**
404 * Drop the current database. Package-private for testing.
405 */
406 void dropDB()
407 throws SQLException {
408 retain(getDBSchemaGroup(true), new SchemaGroup(), true, true);
409 }
410
411 /**
412 * Issue DELETE statement against all known tables.
413 */
414 private void deleteTableContents()
415 throws SQLException {
416 SchemaGroup group = getSchemaGroup();
417 Schema[] schemas = group.getSchemas();
418 Collection tables = new LinkedHashSet();
419 for (int i = 0; i < schemas.length; i++) {
420 Table[] ts = schemas[i].getTables();
421 for (int j = 0; j < ts.length; j++)
422 tables.add(ts[j]);
423 }
424 Table[] tableArray = (Table[]) tables.toArray(new Table[tables.size()]);
425 String[] sql = _conf.getDBDictionaryInstance()
426 .getDeleteTableContentsSQL(tableArray);
427 if (!executeSQL(sql))
428 _log.warn(_loc.get("delete-table-contents"));
429 }
430
431 /**
432 * Record the changes made to the DB in the current {@link SchemaFactory}.
433 */
434 public void record() {
435 if (_db != null && _writer == null)
436 _conf.getSchemaFactoryInstance().storeSchema(_db);
437 }
438
439 /**
440 * Adds all database components in the repository schema that are not
441 * present in the given database schema to the database.
442 */
443 private void add(SchemaGroup db, SchemaGroup repos)
444 throws SQLException {
445 // add sequences
446 Schema[] schemas = repos.getSchemas();
447 Schema schema;
448 if (_seqs) {
449 Sequence[] seqs;
450 for (int i = 0; i < schemas.length; i++) {
451 seqs = schemas[i].getSequences();
452 for (int j = 0; j < seqs.length; j++) {
453 if (db.findSequence(schemas[i], seqs[j].getFullName()) != null)
454 continue;
455
456 if (createSequence(seqs[j])) {
457 schema = db.getSchema(seqs[j].getSchemaName());
458 if (schema == null)
459 schema = db.addSchema(seqs[j].getSchemaName());
460 schema.importSequence(seqs[j]);
461 } else
462 _log.warn(_loc.get("add-seq", seqs[j]));
463 }
464 }
465 }
466
467 // order is important in this method; start with columns
468 Table[] tabs;
469 Table dbTable;
470 Column[] cols;
471 Column col;
472 for (int i = 0; i < schemas.length; i++) {
473 tabs = schemas[i].getTables();
474 for (int j = 0; j < tabs.length; j++) {
475 cols = tabs[j].getColumns();
476 dbTable = db.findTable(schemas[i], tabs[j].getFullName());
477 for (int k = 0; k < cols.length; k++) {
478 if (dbTable != null) {
479 col = dbTable.getColumn(cols[k].getName());
480 if (col == null) {
481 if (addColumn(cols[k]))
482 dbTable.importColumn(cols[k]);
483 else
484 _log.warn(_loc.get("add-col", cols[k],
485 tabs[j]));
486 } else if (!cols[k].equalsColumn(col)) {
487 _log.warn(_loc.get("bad-col", new Object[]{
488 col, dbTable, col.getDescription(),
489 cols[k].getDescription() }));
490 }
491 }
492 }
493 }
494 }
495
496 // primary keys
497 if (_pks) {
498 PrimaryKey pk;
499 for (int i = 0; i < schemas.length; i++) {
500 tabs = schemas[i].getTables();
501 for (int j = 0; j < tabs.length; j++) {
502 pk = tabs[j].getPrimaryKey();
503 dbTable = db.findTable(schemas[i], tabs[j].getFullName());
504 if (pk != null && !pk.isLogical() && dbTable != null) {
505 if (dbTable.getPrimaryKey() == null
506 && addPrimaryKey(pk))
507 dbTable.importPrimaryKey(pk);
508 else if (dbTable.getPrimaryKey() == null)
509 _log.warn(_loc.get("add-pk", pk, tabs[j]));
510 else if (!pk.equalsPrimaryKey(dbTable.getPrimaryKey()))
511 _log.warn(_loc.get("bad-pk",
512 dbTable.getPrimaryKey(), dbTable));
513 }
514 }
515 }
516 }
517
518 // tables
519 Set newTables = new HashSet();
520 for (int i = 0; i < schemas.length; i++) {
521 tabs = schemas[i].getTables();
522 for (int j = 0; j < tabs.length; j++) {
523 if (db.findTable(schemas[i], tabs[j].getFullName()) != null)
524 continue;
525
526 if (createTable(tabs[j])) {
527 newTables.add(tabs[j]);
528 schema = db.getSchema(tabs[j].getSchemaName());
529 if (schema == null)
530 schema = db.addSchema(tabs[j].getSchemaName());
531 schema.importTable(tabs[j]);
532 } else
533 _log.warn(_loc.get("add-table", tabs[j]));
534 }
535 }
536
537 // indexes
538 Index[] idxs;
539 Index idx;
540 for (int i = 0; i < schemas.length; i++) {
541 tabs = schemas[i].getTables();
542 for (int j = 0; j < tabs.length; j++) {
543 // create indexes on new tables even if indexes
544 // have been turned off
545 if (!_indexes && !newTables.contains(tabs[j]))
546 continue;
547
548 idxs = tabs[j].getIndexes();
549 dbTable = db.findTable(schemas[i], tabs[j].getFullName());
550 for (int k = 0; k < idxs.length; k++) {
551 if (dbTable != null) {
552 idx = findIndex(dbTable, idxs[k]);
553 if (idx == null) {
554 if (createIndex(idxs[k], dbTable))
555 dbTable.importIndex(idxs[k]);
556 else
557 _log.warn(_loc.get("add-index", idxs[k],
558 tabs[j]));
559 } else if (!idxs[k].equalsIndex(idx))
560 _log.warn(_loc.get("bad-index", idx, dbTable));
561 }
562 }
563 }
564 }
565
566 // Unique Constraints on group of columns
567 Unique[] uniques;
568 for (int i = 0; i < schemas.length; i++) {
569 tabs = schemas[i].getTables();
570 for (int j = 0; j < tabs.length; j++) {
571 // create unique constraints only on new tables
572 if (!newTables.contains(tabs[j]))
573 continue;
574
575 uniques = tabs[j].getUniques();
576 if (uniques == null || uniques.length == 0)
577 continue;
578 dbTable = db.findTable(tabs[j]);
579 if (dbTable == null)
580 continue;
581 for (int k = 0; k < uniques.length; k++) {
582 dbTable.importUnique(uniques[k]);
583 }
584 }
585 }
586
587 // foreign keys
588 ForeignKey[] fks;
589 ForeignKey fk;
590 for (int i = 0; i < schemas.length; i++) {
591 tabs = schemas[i].getTables();
592 for (int j = 0; j < tabs.length; j++) {
593 // create foreign keys on new tables even if fks
594 // have been turned off
595 if (!_fks && !newTables.contains(tabs[j]))
596 continue;
597
598 fks = tabs[j].getForeignKeys();
599 dbTable = db.findTable(schemas[i],tabs[j].getFullName());
600 for (int k = 0; k < fks.length; k++) {
601 if (!fks[k].isLogical() && dbTable != null) {
602 fk = findForeignKey(dbTable, fks[k]);
603 if (fk == null) {
604 if (addForeignKey(fks[k]))
605 dbTable.importForeignKey(fks[k]);
606 else
607 _log.warn(_loc.get("add-fk",
608 fks[k], tabs[j]));
609 } else if (!fks[k].equalsForeignKey(fk))
610 _log.warn(_loc.get("bad-fk", fk, dbTable));
611 }
612 }
613 }
614 }
615 }
616
617 /**
618 * Drops all database components that are in the given database schema
619 * but not in the repository schema.
620 */
621 private void retain(SchemaGroup db, SchemaGroup repos, boolean tables,
622 boolean sequences)
623 throws SQLException {
624 Schema[] schemas = db.getSchemas();
625 if (_seqs && sequences) {
626 Sequence[] seqs;
627 for (int i = 0; i < schemas.length; i++) {
628 seqs = schemas[i].getSequences();
629 for (int j = 0; j < seqs.length; j++) {
630 if (!isDroppable(seqs[j]))
631 continue;
632 if (repos.findSequence(seqs[j]) == null) {
633 if (dropSequence(seqs[j]))
634 schemas[i].removeSequence(seqs[j]);
635 else
636 _log.warn(_loc.get("drop-seq", seqs[j]));
637 }
638 }
639 }
640 }
641
642 // order is important in this method; start with foreign keys
643 Table[] tabs;
644 Table reposTable;
645 if (_fks) {
646 ForeignKey[] fks;
647 ForeignKey fk;
648 for (int i = 0; i < schemas.length; i++) {
649 tabs = schemas[i].getTables();
650 for (int j = 0; j < tabs.length; j++) {
651 if (!isDroppable(tabs[j]))
652 continue;
653 fks = tabs[j].getForeignKeys();
654 reposTable = repos.findTable(tabs[j]);
655 if (!tables && reposTable == null)
656 continue;
657
658 for (int k = 0; k < fks.length; k++) {
659 if (fks[k].isLogical())
660 continue;
661
662 fk = null;
663 if (reposTable != null)
664 fk = findForeignKey(reposTable, fks[k]);
665 if (reposTable == null || fk == null
666 || !fks[k].equalsForeignKey(fk)) {
667 if (dropForeignKey(fks[k]))
668 tabs[j].removeForeignKey(fks[k]);
669 else
670 _log.warn(_loc.get("drop-fk", fks[k],
671 tabs[j]));
672 }
673 }
674 }
675 }
676 }
677
678 // primary keys
679 if (_pks) {
680 PrimaryKey pk;
681 for (int i = 0; i < schemas.length; i++) {
682 tabs = schemas[i].getTables();
683 for (int j = 0; j < tabs.length; j++) {
684 if (!isDroppable(tabs[j]))
685 continue;
686 pk = tabs[j].getPrimaryKey();
687 if (pk != null && pk.isLogical())
688 continue;
689
690 reposTable = repos.findTable(tabs[j]);
691 if (pk != null && reposTable != null
692 && (reposTable.getPrimaryKey() == null
693 || !pk.equalsPrimaryKey(reposTable.getPrimaryKey()))) {
694 if (dropPrimaryKey(pk))
695 tabs[j].removePrimaryKey();
696 else
697 _log.warn(_loc.get("drop-pk", pk, tabs[j]));
698 }
699 }
700 }
701 }
702
703 // columns
704 Column[] cols;
705 Column col;
706 Collection drops = new LinkedList();
707 for (int i = 0; i < schemas.length; i++) {
708 tabs = schemas[i].getTables();
709 for (int j = 0; j < tabs.length; j++) {
710 if (!isDroppable(tabs[j]))
711 continue;
712 cols = tabs[j].getColumns();
713 reposTable = repos.findTable(tabs[j]);
714 if (reposTable != null) {
715 for (int k = 0; k < cols.length; k++) {
716 col = reposTable.getColumn(cols[k].getName());
717 if (col == null || !cols[k].equalsColumn(col)) {
718 if (tabs[j].getColumns().length == 1)
719 drops.add(tabs[j]);
720 else if (dropColumn(cols[k]))
721 tabs[j].removeColumn(cols[k]);
722 else
723 _log.warn(_loc.get("drop-col", cols[k],
724 tabs[j]));
725 }
726 }
727 }
728 }
729 }
730
731 // now tables
732 if (tables) {
733 for (int i = 0; i < schemas.length; i++) {
734 tabs = schemas[i].getTables();
735 for (int j = 0; j < tabs.length; j++)
736 if (!!isDroppable(tabs[j])
737 && repos.findTable(tabs[j]) == null)
738 drops.add(tabs[j]);
739 }
740 }
741 dropTables(drops, db);
742 }
743
744 /**
745 * Drops all database components in the given repository schema.
746 */
747 private void drop(SchemaGroup db, SchemaGroup repos)
748 throws SQLException {
749 // drop sequences
750 Schema[] schemas = repos.getSchemas();
751 if (_seqs) {
752 Sequence[] seqs;
753 Sequence dbSeq;
754 for (int i = 0; i < schemas.length; i++) {
755 seqs = schemas[i].getSequences();
756 for (int j = 0; j < seqs.length; j++) {
757 if (!isDroppable(seqs[j]))
758 continue;
759 dbSeq = db.findSequence(seqs[j]);
760 if (dbSeq != null) {
761 if (dropSequence(seqs[j]))
762 dbSeq.getSchema().removeSequence(dbSeq);
763 else
764 _log.warn(_loc.get("drop-seq", seqs[j]));
765 }
766 }
767 }
768 }
769
770 // calculate tables to drop; we can only drop tables if we're sure
771 // the user listed the entire table definition in the stuff they want
772 // dropped; else they might just want to drop a few columns
773 Collection drops = new LinkedList();
774 Table[] tabs;
775 Table dbTable;
776 Column[] dbCols;
777 for (int i = 0; i < schemas.length; i++) {
778 tabs = schemas[i].getTables();
779 tables:
780 for (int j = 0; j < tabs.length; j++) {
781 if (!isDroppable(tabs[j]))
782 continue;
783 dbTable = db.findTable(tabs[j]);
784 if (dbTable == null)
785 continue;
786
787 dbCols = dbTable.getColumns();
788 for (int k = 0; k < dbCols.length; k++)
789 if (tabs[j].getColumn(dbCols[k].getName()) == null)
790 continue tables;
791
792 drops.add(tabs[j]);
793 }
794 }
795
796 // order is important in this method; start with foreign keys mentioned
797 // in the drop schema
798 if (_fks) {
799 ForeignKey[] fks;
800 ForeignKey fk;
801 for (int i = 0; i < schemas.length; i++) {
802 tabs = schemas[i].getTables();
803 for (int j = 0; j < tabs.length; j++) {
804 if (!isDroppable(tabs[j]))
805 continue;
806 fks = tabs[j].getForeignKeys();
807 dbTable = db.findTable(tabs[j]);
808 for (int k = 0; k < fks.length; k++) {
809 if (fks[k].isLogical())
810 continue;
811
812 fk = null;
813 if (dbTable != null)
814 fk = findForeignKey(dbTable, fks[k]);
815 if (dbTable == null || fk == null)
816 continue;
817
818 if (dropForeignKey(fks[k]))
819 if (dbTable != null)
820 dbTable.removeForeignKey(fk);
821 else
822 _log.warn(_loc.get("drop-fk", fks[k], tabs[j]));
823 }
824 }
825 }
826
827 // also drop imported foreign keys for tables that will be dropped
828 Table tab;
829 for (Iterator itr = drops.iterator(); itr.hasNext();) {
830 tab = (Table) itr.next();
831 dbTable = db.findTable(tab);
832 if (dbTable == null)
833 continue;
834
835 fks = db.findExportedForeignKeys(dbTable.getPrimaryKey());
836 for (int i = 0; i < fks.length; i++) {
837 if (dropForeignKey(fks[i]))
838 dbTable.removeForeignKey(fks[i]);
839 else
840 _log.warn(_loc.get("drop-fk", fks[i], dbTable));
841 }
842 }
843 }
844
845 // drop the tables we calculated above
846 dropTables(drops, db);
847
848 // columns
849 Column[] cols;
850 Column col;
851 for (int i = 0; i < schemas.length; i++) {
852 tabs = schemas[i].getTables();
853 for (int j = 0; j < tabs.length; j++) {
854 if (!isDroppable(tabs[j]))
855 continue;
856 cols = tabs[j].getColumns();
857 dbTable = db.findTable(tabs[j]);
858 for (int k = 0; k < cols.length; k++) {
859 col = null;
860 if (dbTable != null)
861 col = dbTable.getColumn(cols[k].getName());
862 if (dbTable == null || col == null)
863 continue;
864
865 if (dropColumn(cols[k])) {
866 if (dbTable != null)
867 dbTable.removeColumn(col);
868 else
869 _log.warn(_loc.get("drop-col", cols[k], tabs[j]));
870 }
871 }
872 }
873 }
874 }
875
876 /**
877 * Return true if the table is droppable.
878 */
879 private boolean isDroppable(Table table) {
880 return _openjpaTables
881 || (!table.getName().toUpperCase().startsWith("OPENJPA_")
882 && !table.getName().toUpperCase().startsWith("JDO_")); // legacy
883 }
884
885 /**
886 * Return true if the sequence is droppable.
887 */
888 private boolean isDroppable(Sequence seq) {
889 return _openjpaTables
890 || (!seq.getName().toUpperCase().startsWith("OPENJPA_")
891 && !seq.getName().toUpperCase().startsWith("JDO_")); // legacy
892 }
893
894 /**
895 * Find an index in the given table that matches the given one.
896 */
897 private Index findIndex(Table dbTable, Index idx) {
898 Index[] idxs = dbTable.getIndexes();
899 for (int i = 0; i < idxs.length; i++)
900 if (idx.columnsMatch(idxs[i].getColumns()))
901 return idxs[i];
902 return null;
903 }
904
905 /**
906 * Find a foreign key in the given table that matches the given one.
907 */
908 private ForeignKey findForeignKey(Table dbTable, ForeignKey fk) {
909 if (fk.getConstantColumns().length > 0
910 || fk.getConstantPrimaryKeyColumns().length > 0)
911 return null;
912 ForeignKey[] fks = dbTable.getForeignKeys();
913 for (int i = 0; i < fks.length; i++)
914 if (fk.columnsMatch(fks[i].getColumns(),
915 fks[i].getPrimaryKeyColumns()))
916 return fks[i];
917 return null;
918 }
919
920 /**
921 * Remove the given collection of tables from the database schema. Orders
922 * the removals according to foreign key constraints on the tables.
923 */
924 private void dropTables(Collection tables, SchemaGroup change)
925 throws SQLException {
926 if (tables.isEmpty())
927 return;
928
929 Table table;
930 Table changeTable;
931 for (Iterator itr = tables.iterator(); itr.hasNext();) {
932 table = (Table) itr.next();
933 if (dropTable(table)) {
934 changeTable = change.findTable(table);
935 if (changeTable != null)
936 changeTable.getSchema().removeTable(changeTable);
937 } else
938 _log.warn(_loc.get("drop-table", table));
939 }
940 }
941
942 /**
943 * Add the given table to the database schema.
944 *
945 * @return true if the operation was successful, false otherwise
946 */
947 public boolean createTable(Table table)
948 throws SQLException {
949 return executeSQL(_dict.getCreateTableSQL(table));
950 }
951
952 /**
953 * Drop the given table from the database schema.
954 *
955 * @return true if the operation was successful, false otherwise
956 */
957 public boolean dropTable(Table table)
958 throws SQLException {
959 return executeSQL(_dict.getDropTableSQL(table));
960 }
961
962 /**
963 * Add the given sequence to the database schema.
964 *
965 * @return true if the operation was successful, false otherwise
966 */
967 public boolean createSequence(Sequence seq)
968 throws SQLException {
969 return executeSQL(_dict.getCreateSequenceSQL(seq));
970 }
971
972 /**
973 * Drop the given sequence from the database schema.
974 *
975 * @return true if the operation was successful, false otherwise
976 */
977 public boolean dropSequence(Sequence seq)
978 throws SQLException {
979 return executeSQL(_dict.getDropSequenceSQL(seq));
980 }
981
982 /**
983 * Add the given index to the database schema.
984 *
985 * @return true if the operation was successful, false otherwise
986 */
987 public boolean createIndex(Index idx, Table table)
988 throws SQLException {
989 int max = _dict.maxIndexesPerTable;
990
991 int len = table.getIndexes().length;
992 if (table.getPrimaryKey() != null)
993 len += table.getPrimaryKey().getColumns().length;
994
995 if (len >= max) {
996 _log.warn(_loc.get("too-many-indexes", idx, table, max + ""));
997 return false;
998 }
999
1000 return executeSQL(_dict.getCreateIndexSQL(idx));
1001 }
1002
1003 /**
1004 * Drop the given index from the database schema.
1005 *
1006 * @return true if the operation was successful, false otherwise
1007 */
1008 public boolean dropIndex(Index idx)
1009 throws SQLException {
1010 return executeSQL(_dict.getDropIndexSQL(idx));
1011 }
1012
1013 /**
1014 * Add the given column to the database schema.
1015 *
1016 * @return true if the operation was successful, false otherwise
1017 */
1018 public boolean addColumn(Column col)
1019 throws SQLException {
1020 return executeSQL(_dict.getAddColumnSQL(col));
1021 }
1022
1023 /**
1024 * Drop the given column from the database schema.
1025 *
1026 * @return true if the operation was successful, false otherwise
1027 */
1028 public boolean dropColumn(Column col)
1029 throws SQLException {
1030 return executeSQL(_dict.getDropColumnSQL(col));
1031 }
1032
1033 /**
1034 * Add the given primary key to the database schema.
1035 *
1036 * @return true if the operation was successful, false otherwise
1037 */
1038 public boolean addPrimaryKey(PrimaryKey pk)
1039 throws SQLException {
1040 return executeSQL(_dict.getAddPrimaryKeySQL(pk));
1041 }
1042
1043 /**
1044 * Drop the given primary key from the database schema.
1045 *
1046 * @return true if the operation was successful, false otherwise
1047 */
1048 public boolean dropPrimaryKey(PrimaryKey pk)
1049 throws SQLException {
1050 return executeSQL(_dict.getDropPrimaryKeySQL(pk));
1051 }
1052
1053 /**
1054 * Add the given foreign key to the database schema.
1055 *
1056 * @return true if the operation was successful, false otherwise
1057 */
1058 public boolean addForeignKey(ForeignKey fk)
1059 throws SQLException {
1060 return executeSQL(_dict.getAddForeignKeySQL(fk));
1061 }
1062
1063 /**
1064 * Drop the given foreign key from the database schema.
1065 *
1066 * @return true if the operation was successful, false otherwise
1067 */
1068 public boolean dropForeignKey(ForeignKey fk)
1069 throws SQLException {
1070 return executeSQL(_dict.getDropForeignKeySQL(fk));
1071 }
1072
1073 /**
1074 * Return the database schema.
1075 */
1076 public SchemaGroup getDBSchemaGroup() {
1077 try {
1078 return getDBSchemaGroup(true);
1079 } catch (SQLException se) {
1080 throw SQLExceptions.getStore(se, _dict);
1081 }
1082 }
1083
1084 /**
1085 * Set the database schema.
1086 */
1087 public void setDBSchemaGroup(SchemaGroup db) {
1088 _db = db;
1089 if (db != null)
1090 _fullDB = true;
1091 }
1092
1093 /**
1094 * Return the database schema.
1095 *
1096 * @param full if false, only the tables named in the set schema
1097 * repository will be generated
1098 */
1099 private SchemaGroup getDBSchemaGroup(boolean full)
1100 throws SQLException {
1101 if (_db == null || (full && !_fullDB)) {
1102 SchemaGenerator gen = new SchemaGenerator(_conf);
1103 gen.setPrimaryKeys(_pks);
1104 gen.setForeignKeys(_fks);
1105 gen.setIndexes(_indexes);
1106 if (full)
1107 gen.generateSchemas();
1108 else {
1109 // generate only the tables in the given repository
1110 // group; some may not exist yet, which is OK; we just need
1111 // to make sure we can detect the changes to the ones that
1112 // do exist
1113 Collection tables = new LinkedList();
1114 SchemaGroup group = assertSchemaGroup();
1115 Schema[] schemas = group.getSchemas();
1116 Table[] tabs;
1117 for (int i = 0; i < schemas.length; i++) {
1118 tabs = schemas[i].getTables();
1119 for (int j = 0; j < tabs.length; j++) {
1120 if (tabs[j].getSchemaName() == null)
1121 tables.add("." + tabs[j].getName());
1122 else
1123 tables.add(tabs[j].getFullName());
1124 }
1125 }
1126 if (!tables.isEmpty())
1127 gen.generateSchemas((String[]) tables.toArray
1128 (new String[tables.size()]));
1129 }
1130 _db = gen.getSchemaGroup();
1131 }
1132 return _db;
1133 }
1134
1135 private SchemaGroup assertSchemaGroup() {
1136 SchemaGroup local = getSchemaGroup();
1137 if (local == null)
1138 throw new InvalidStateException(_loc.get("tool-norepos"));
1139 return local;
1140 }
1141
1142 /////////////
1143 // Utilities
1144 /////////////
1145
1146 /**
1147 * Executes the given array of non-selecting SQL statements, correctly
1148 * logging the SQL calls and optionally ignoring errors.
1149 *
1150 * @return true if there was SQL to execute and the calls were
1151 * successful, false otherwise
1152 */
1153 private boolean executeSQL(String[] sql)
1154 throws SQLException {
1155 // if no sql, probably b/c dictionary doesn't support operation
1156 if (sql.length == 0)
1157 return false;
1158
1159 boolean err = false;
1160 if (_writer == null) {
1161 // this is outside the try-catch because a failure here is
1162 // really bad, and should not be ignored.
1163 Connection conn = _ds.getConnection();
1164 Statement statement = null;
1165 boolean wasAuto = true;
1166 try {
1167 wasAuto = conn.getAutoCommit();
1168 if (!wasAuto)
1169 conn.setAutoCommit(true);
1170 for (int i = 0; i < sql.length; i++) {
1171 try {
1172 // some connections require that rollback be
1173 // called on the connection before any DDL statements
1174 // can be run on it, even when autocommit is on.
1175 // This is sometimes because the connection does not
1176 // allow DDL statements when there are multiple
1177 // commands issued on the connection, and the
1178 // connection pool may have issued some validation SQL.
1179 try {
1180 conn.rollback();
1181 } catch (Exception e) {
1182 }
1183
1184 statement = conn.createStatement();
1185 statement.executeUpdate(sql[i]);
1186
1187 // some connections seem to require an explicit
1188 // commit for DDL statements, even when autocommit
1189 // is on. The DataDirect drivers seem to suffer from
1190 // this limitation.
1191 try {
1192 conn.commit();
1193 } catch (Exception e) {
1194 }
1195 }
1196 catch (SQLException se) {
1197 err = true;
1198 handleException(se);
1199 } finally {
1200 if (statement != null)
1201 try {
1202 statement.close();
1203 } catch (SQLException se) {
1204 }
1205 }
1206 }
1207 }
1208 finally {
1209 if (!wasAuto)
1210 conn.setAutoCommit(false);
1211 try {
1212 conn.close();
1213 } catch (SQLException se) {
1214 }
1215 }
1216 } else {
1217 for (int i = 0; i < sql.length; i++)
1218 _writer.println(sql[i] + ";");
1219 _writer.flush();
1220 }
1221
1222 return !err;
1223 }
1224
1225 /**
1226 * Handle the given exception, logging it and optionally ignoring it,
1227 * depending on the flags this SchemaTool was created with.
1228 */
1229 private void handleException(SQLException sql)
1230 throws SQLException {
1231 if (!_ignoreErrs)
1232 throw sql;
1233 _log.warn(sql.getMessage(), sql);
1234 }
1235
1236 ////////
1237 // Main
1238 ////////
1239
1240 /**
1241 * Usage: java org.apache.openjpa.jdbc.schema.SchemaTool [option]*
1242 * [-action/-a <add | retain | drop | refresh | createDB | dropDB
1243 * | build | reflect | import | export>]
1244 * <.schema file or resource>*
1245 * Where the following options are recognized.
1246 * <ul>
1247 * <li><i>-properties/-p <properties file or resource></i>: The
1248 * path or resource name of a OpenJPA properties file containing
1249 * information such as the license key and connection data as
1250 * outlined in {@link JDBCConfiguration}. Optional.</li>
1251 * <li><i>-<property name> <property value></i>: All bean
1252 * properties of the OpenJPA {@link JDBCConfiguration} can be set by
1253 * using their names and supplying a value. For example:
1254 * <code>-licenseKey adslfja83r3lkadf</code></li>
1255 * <li><i>-ignoreErrors/-i <true/t | false/f></i>: If false, an
1256 * exception will will be thrown if the tool encounters any database
1257 * exceptions; defaults to <code>false</code>.</li>
1258 * <li><i>-file/-f <stdout | output file or resource></i>: Use this
1259 * option to write a SQL script for the planned schema modifications,
1260 * rather than committing them to the database. This option also
1261 * applies to the <code>export</code> and <code>reflect</code> actions.</li>
1262 * <li><i>-openjpaTables/-kt <true/t | false/f></i>: Under the
1263 * <code>reflect</code> action, whether to reflect on tables with
1264 * the name <code>OPENJPA_*</code>. Under other actions, whether to
1265 * drop such tables. Defaults to <code>false</code>.</li>
1266 * <li><i>-dropTables/-dt <true/t | false/f></i>: Set this option to
1267 * true to drop tables that appear to be unused during
1268 * <code>retain</code> and <code>refresh</code> actions. Defaults to
1269 * <code>true</code>.</li>
1270 * <li><i>-dropSequences/-dsq <true/t | false/f></i>: Set this option
1271 * to true to drop sequences that appear to be unused during
1272 * <code>retain</code> and <code>refresh</code> actions. Defaults to
1273 * <code>true</code>.</li>
1274 * <li><i>-primaryKeys/-pk <true/t | false/f></i>: Whether primary
1275 * keys on existing tables are manipulated. Defaults to true.</li>
1276 * <li><i>-foreignKeys/-fk <true/t | false/f></i>: Whether foreign
1277 * keys on existing tables are manipulated. Defaults to true.</li>
1278 * <li><i>-indexes/-ix <true/t | false/f></i>: Whether indexes
1279 * on existing tables are manipulated. Defaults to true.</li>
1280 * <li><i>-sequences/-sq <true/t | false/f></i>: Whether to
1281 * manipulate sequences. Defaults to true.</li>
1282 * <li><i>-record/-r <true/t | false/f></i>: Set this option to
1283 * <code>false</code> to prevent writing the schema changes to the
1284 * current {@link SchemaFactory}.</li>
1285 * </ul>
1286 * Actions can be composed in a comma-separated list. The various actions
1287 * are as follows.
1288 * <ul>
1289 * <li><i>add</i>: Bring the schema up-to-date with the latest
1290 * changes to the schema XML data by adding tables, columns,
1291 * indexes, etc. This action never drops any data. This is the
1292 * default action.</li>
1293 * <li><i>retain</i>: Keep all schema components in the schema XML, but
1294 * drop the rest from the database. This action never adds any data.</li>
1295 * <li><i>drop</i>: Drop all the schema components in the schema XML.</li>
1296 * <li><i>refresh</i>: Equivalent to retain, then add.</li>
1297 * <li><i>createDB</i>: Execute SQL to re-create the current database.
1298 * This action is typically used in conjuction with the
1299 * <code>file</code> option.</li>
1300 * <li><i>build</i>: Execute SQL to build the schema defined in the XML.
1301 * Because it doesn't take the current database schema into account,
1302 * this action is typically used in conjuction with the
1303 * <code>file</code> option.</li>
1304 * <li><i>reflect</i>: Reflect on the current database schema. Write the
1305 * schema's XML representation to the file specified with the
1306 * <code>file</code> option, or to stdout if no file is given.</li>
1307 * <li><i>dropDB</i>: Execute SQL to drop the current database. This
1308 * action implies <code>dropTables</code>.</li>
1309 * <li><i>deleteTableContents</i>: Execute SQL to delete all rows from
1310 * all tables that OpenJPA knows about.</li>
1311 * <li><i>import</i>: Import the given XML schema definition into the
1312 * current {@link SchemaFactory}.</li>
1313 * <li><i>export</i>: Export the current {@link SchemaFactory}'s recorded
1314 * schema to an XML schema definition file.</li>
1315 * </ul>
1316 * Examples:
1317 * <ul>
1318 * <li>Write a script to stdout to re-create the current database
1319 * schema:<br />
1320 * <code>java org.apache.openjpa.jdbc.schema.SchemaTool -f stdout
1321 * -a createDB</code></li>
1322 * <li>Drop the current database schema:<br />
1323 * <code>java org.apache.openjpa.jdbc.schema.SchemaTool
1324 * -a dropDB</code></li>
1325 * <li>Refresh the schema and delete all records in all tables:<br />
1326 * <code>java org.apache.openjpa.jdbc.schema.SchemaTool
1327 * -a refresh,deleteTableContents</code></li>
1328 * <li>Create a schema based on an XML schema definition file:<br />
1329 * <code>java org.apache.openjpa.jdbc.schema.SchemaTool
1330 * myschema.xml</code></li>
1331 * </ul>
1332 */
1333 public static void main(String[] args)
1334 throws IOException, SQLException {
1335 Options opts = new Options();
1336 final String[] arguments = opts.setFromCmdLine(args);
1337 boolean ret = Configurations.runAgainstAllAnchors(opts,
1338 new Configurations.Runnable() {
1339 public boolean run(Options opts) throws Exception {
1340 JDBCConfiguration conf = new JDBCConfigurationImpl();
1341 try {
1342 return SchemaTool.run(conf, arguments, opts);
1343 } finally {
1344 conf.close();
1345 }
1346 }
1347 });
1348 if (!ret)
1349 System.out.println(_loc.get("tool-usage"));
1350 }
1351
1352 /**
1353 * Run the tool. Returns false if any invalid options were given.
1354 *
1355 * @see #main
1356 */
1357 public static boolean run(JDBCConfiguration conf, String[] args,
1358 Options opts)
1359 throws IOException, SQLException {
1360 Flags flags = new Flags();
1361 flags.dropTables = opts.removeBooleanProperty
1362 ("dropTables", "dt", flags.dropTables);
1363 flags.dropSequences = opts.removeBooleanProperty
1364 ("dropSequences", "dsq", flags.dropSequences);
1365 flags.ignoreErrors = opts.removeBooleanProperty
1366 ("ignoreErrors", "i", flags.ignoreErrors);
1367 flags.openjpaTables = opts.removeBooleanProperty
1368 ("openjpaTables", "ot", flags.openjpaTables);
1369 flags.primaryKeys = opts.removeBooleanProperty
1370 ("primaryKeys", "pk", flags.primaryKeys);
1371 flags.foreignKeys = opts.removeBooleanProperty
1372 ("foreignKeys", "fks", flags.foreignKeys);
1373 flags.indexes = opts.removeBooleanProperty
1374 ("indexes", "ix", flags.indexes);
1375 flags.sequences = opts.removeBooleanProperty
1376 ("sequences", "sq", flags.sequences);
1377 flags.record = opts.removeBooleanProperty("record", "r", flags.record);
1378 String fileName = opts.removeProperty("file", "f", null);
1379 String schemas = opts.removeProperty("s");
1380 if (schemas != null)
1381 opts.setProperty("schemas", schemas);
1382
1383 String[] actions = opts.removeProperty("action", "a", flags.action)
1384 .split(",");
1385
1386 // setup a configuration instance with cmd-line info
1387 Configurations.populateConfiguration(conf, opts);
1388
1389 // create script writer
1390 ClassLoader loader = conf.getClassResolverInstance().
1391 getClassLoader(SchemaTool.class, null);
1392 flags.writer = Files.getWriter(fileName, loader);
1393
1394 boolean returnValue = true;
1395 for (int i = 0; i < actions.length; i++) {
1396 flags.action = actions[i];
1397 returnValue &= run(conf, args, flags, loader);
1398 }
1399
1400 return returnValue;
1401 }
1402
1403 /**
1404 * Run the tool. Return false if invalid options were given.
1405 */
1406 public static boolean run(JDBCConfiguration conf, String[] args,
1407 Flags flags, ClassLoader loader)
1408 throws IOException, SQLException {
1409 Log log = conf.getLog(OpenJPAConfiguration.LOG_TOOL);
1410 if (ACTION_REFLECT.equals(flags.action)) {
1411 if (args.length > 0)
1412 return false;
1413 if (flags.writer == null)
1414 flags.writer = new PrintWriter(System.out);
1415
1416 SchemaGenerator gen = new SchemaGenerator(conf);
1417 gen.setPrimaryKeys(flags.primaryKeys);
1418 gen.setIndexes(flags.indexes);
1419 gen.setForeignKeys(flags.foreignKeys);
1420 gen.setSequences(flags.sequences);
1421 gen.setOpenJPATables(flags.openjpaTables);
1422
1423 String schemas = conf.getSchemas();
1424 if (StringUtils.isEmpty(schemas))
1425 schemas = "all";
1426 log.info(_loc.get("sch-reflect", schemas));
1427 gen.generateSchemas();
1428
1429 // record the schema
1430 log.info(_loc.get("sch-reflect-write"));
1431 SchemaSerializer ser = new XMLSchemaSerializer(conf);
1432 ser.addAll(gen.getSchemaGroup());
1433 ser.serialize(flags.writer, ser.PRETTY);
1434 return true;
1435 }
1436
1437 if (args.length == 0
1438 && !ACTION_CREATEDB.equals(flags.action)
1439 && !ACTION_DROPDB.equals(flags.action)
1440 && !ACTION_EXPORT.equals(flags.action)
1441 && !ACTION_DELETE_TABLE_CONTENTS.equals(flags.action))
1442 return false;
1443
1444 // parse in the arguments
1445 SchemaParser parser = new XMLSchemaParser(conf);
1446 parser.setDelayConstraintResolve(true);
1447 File file;
1448 for (int i = 0; i < args.length; i++) {
1449 file = Files.getFile(args[i], loader);
1450 log.info(_loc.get("tool-running", file));
1451 parser.parse(file);
1452 }
1453 parser.resolveConstraints();
1454
1455 if (ACTION_IMPORT.equals(flags.action)) {
1456 log.info(_loc.get("tool-import-store"));
1457 SchemaGroup schema = parser.getSchemaGroup();
1458 conf.getSchemaFactoryInstance().storeSchema(schema);
1459 return true;
1460 }
1461 if (ACTION_EXPORT.equals(flags.action)) {
1462 if (flags.writer == null)
1463 flags.writer = new PrintWriter(System.out);
1464
1465 log.info(_loc.get("tool-export-gen"));
1466 SchemaGroup schema = conf.getSchemaFactoryInstance().readSchema();
1467
1468 log.info(_loc.get("tool-export-write"));
1469 SchemaSerializer ser = new XMLSchemaSerializer(conf);
1470 ser.addAll(schema);
1471 ser.serialize(flags.writer, ser.PRETTY);
1472 return true;
1473 }
1474
1475 SchemaTool tool = new SchemaTool(conf, flags.action);
1476 tool.setIgnoreErrors(flags.ignoreErrors);
1477 tool.setDropTables(flags.dropTables);
1478 tool.setSequences(flags.sequences); // set before dropseqs
1479 tool.setDropSequences(flags.dropSequences);
1480 tool.setPrimaryKeys(flags.primaryKeys);
1481 tool.setForeignKeys(flags.foreignKeys);
1482 tool.setIndexes(flags.indexes);
1483 tool.setOpenJPATables(flags.openjpaTables);
1484 if (args.length > 0)
1485 tool.setSchemaGroup(parser.getSchemaGroup());
1486 if (flags.writer != null)
1487 tool.setWriter(flags.writer);
1488
1489 log.info(_loc.get("tool-action", flags.action));
1490 try {
1491 tool.run();
1492 } finally {
1493 if (flags.record) {
1494 log.info(_loc.get("tool-record"));
1495 tool.record();
1496 }
1497 }
1498 if (flags.writer != null)
1499 flags.writer.flush();
1500
1501 return true;
1502 }
1503
1504 /**
1505 * Run flags.
1506 */
1507 public static class Flags {
1508
1509 public String action = ACTION_ADD;
1510 public Writer writer = null;
1511 public boolean dropTables = true;
1512 public boolean dropSequences = true;
1513 public boolean ignoreErrors = false;
1514 public boolean openjpaTables = false;
1515 public boolean primaryKeys = true;
1516 public boolean foreignKeys = true;
1517 public boolean indexes = true;
1518 public boolean sequences = true;
1519 public boolean record = true;
1520 }
1521 }