1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package com.healthmarketscience.jackcess;
29
30 import java.io.BufferedReader;
31 import java.io.Closeable;
32 import java.io.File;
33 import java.io.FileNotFoundException;
34 import java.io.FileReader;
35 import java.io.Flushable;
36 import java.io.IOException;
37 import java.io.RandomAccessFile;
38 import java.nio.ByteBuffer;
39 import java.nio.channels.Channels;
40 import java.nio.channels.FileChannel;
41 import java.sql.ResultSet;
42 import java.sql.ResultSetMetaData;
43 import java.sql.SQLException;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Collection;
47 import java.util.ConcurrentModificationException;
48 import java.util.Date;
49 import java.util.HashMap;
50 import java.util.HashSet;
51 import java.util.Iterator;
52 import java.util.LinkedList;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.NoSuchElementException;
56 import java.util.Set;
57
58 import org.apache.commons.lang.builder.ToStringBuilder;
59 import org.apache.commons.logging.Log;
60 import org.apache.commons.logging.LogFactory;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 public class Database
81 implements Iterable<Table>, Closeable, Flushable
82 {
83
84 private static final Log LOG = LogFactory.getLog(Database.class);
85
86
87
88 private static final byte[] SYS_DEFAULT_SID = new byte[2];
89 static {
90 SYS_DEFAULT_SID[0] = (byte) 0xA6;
91 SYS_DEFAULT_SID[1] = (byte) 0x33;
92 }
93
94
95
96 public static final boolean DEFAULT_AUTO_SYNC = true;
97
98
99
100 public static final String USE_BIG_INDEX_PROPERTY =
101 "com.healthmarketscience.jackcess.bigIndex";
102
103
104 private static final int COPY_TABLE_BATCH_SIZE = 200;
105
106
107 private static final int PAGE_SYSTEM_CATALOG = 2;
108
109 private static final String TABLE_SYSTEM_CATALOG = "MSysObjects";
110
111
112
113
114 private static final Integer SYS_FULL_ACCESS_ACM = 1048575;
115
116
117 private static final String ACE_COL_ACM = "ACM";
118
119 private static final String ACE_COL_F_INHERITABLE = "FInheritable";
120
121 private static final String ACE_COL_OBJECT_ID = "ObjectId";
122
123 private static final String ACE_COL_SID = "SID";
124
125
126 private static final String REL_COL_COLUMN_COUNT = "ccolumn";
127
128 private static final String REL_COL_FLAGS = "grbit";
129
130 private static final String REL_COL_COLUMN_INDEX = "icolumn";
131
132 private static final String REL_COL_TO_COLUMN = "szColumn";
133
134 private static final String REL_COL_TO_TABLE = "szObject";
135
136 private static final String REL_COL_FROM_COLUMN = "szReferencedColumn";
137
138 private static final String REL_COL_FROM_TABLE = "szReferencedObject";
139
140 private static final String REL_COL_NAME = "szRelationship";
141
142
143
144 private static final String CAT_COL_ID = "Id";
145
146 private static final String CAT_COL_NAME = "Name";
147 private static final String CAT_COL_OWNER = "Owner";
148
149 private static final String CAT_COL_PARENT_ID = "ParentId";
150
151 private static final String CAT_COL_TYPE = "Type";
152
153 private static final String CAT_COL_DATE_CREATE = "DateCreate";
154
155 private static final String CAT_COL_DATE_UPDATE = "DateUpdate";
156
157 private static final String CAT_COL_FLAGS = "Flags";
158
159
160 private static final String EMPTY_MDB = "com/healthmarketscience/jackcess/empty.mdb";
161
162 private static final String ESCAPE_PREFIX = "x";
163
164 private static final String PREFIX_SYSTEM = "MSys";
165
166 private static final String SYSTEM_OBJECT_NAME_TABLES = "Tables";
167
168 private static final String TABLE_SYSTEM_ACES = "MSysACEs";
169
170 private static final String TABLE_SYSTEM_RELATIONSHIPS = "MSysRelationships";
171
172 private static final Short TYPE_TABLE = (short) 1;
173
174
175 private static Collection<String> SYSTEM_CATALOG_COLUMNS =
176 new HashSet<String>(Arrays.asList(CAT_COL_NAME, CAT_COL_TYPE, CAT_COL_ID));
177
178
179
180
181
182
183 private static final Set<String> RESERVED_WORDS = new HashSet<String>();
184 static {
185
186 RESERVED_WORDS.addAll(Arrays.asList(
187 "add", "all", "alphanumeric", "alter", "and", "any", "application", "as",
188 "asc", "assistant", "autoincrement", "avg", "between", "binary", "bit",
189 "boolean", "by", "byte", "char", "character", "column", "compactdatabase",
190 "constraint", "container", "count", "counter", "create", "createdatabase",
191 "createfield", "creategroup", "createindex", "createobject", "createproperty",
192 "createrelation", "createtabledef", "createuser", "createworkspace",
193 "currency", "currentuser", "database", "date", "datetime", "delete",
194 "desc", "description", "disallow", "distinct", "distinctrow", "document",
195 "double", "drop", "echo", "else", "end", "eqv", "error", "exists", "exit",
196 "false", "field", "fields", "fillcache", "float", "float4", "float8",
197 "foreign", "form", "forms", "from", "full", "function", "general",
198 "getobject", "getoption", "gotopage", "group", "group by", "guid", "having",
199 "idle", "ieeedouble", "ieeesingle", "if", "ignore", "imp", "in", "index",
200 "indexes", "inner", "insert", "inserttext", "int", "integer", "integer1",
201 "integer2", "integer4", "into", "is", "join", "key", "lastmodified", "left",
202 "level", "like", "logical", "logical1", "long", "longbinary", "longtext",
203 "macro", "match", "max", "min", "mod", "memo", "module", "money", "move",
204 "name", "newpassword", "no", "not", "null", "number", "numeric", "object",
205 "oleobject", "off", "on", "openrecordset", "option", "or", "order", "outer",
206 "owneraccess", "parameter", "parameters", "partial", "percent", "pivot",
207 "primary", "procedure", "property", "queries", "query", "quit", "real",
208 "recalc", "recordset", "references", "refresh", "refreshlink",
209 "registerdatabase", "relation", "repaint", "repairdatabase", "report",
210 "reports", "requery", "right", "screen", "section", "select", "set",
211 "setfocus", "setoption", "short", "single", "smallint", "some", "sql",
212 "stdev", "stdevp", "string", "sum", "table", "tabledef", "tabledefs",
213 "tableid", "text", "time", "timestamp", "top", "transform", "true", "type",
214 "union", "unique", "update", "user", "value", "values", "var", "varp",
215 "varbinary", "varchar", "where", "with", "workspace", "xor", "year", "yes",
216 "yesno"
217 ));
218 }
219
220
221 private ByteBuffer _buffer;
222
223 private Integer _tableParentId;
224
225 private final JetFormat _format;
226
227
228
229
230 private Map<String, TableInfo> _tableLookup =
231 new HashMap<String, TableInfo>();
232
233 private Set<String> _tableNames;
234
235 private final PageChannel _pageChannel;
236
237 private Table _systemCatalog;
238
239 private Table _accessControlEntries;
240
241 private Integer _relationshipsPageNumber;
242
243 private Table _relationships;
244
245 private final List<byte[]> _newTableSIDs = new ArrayList<byte[]>();
246
247 private boolean _useBigIndex;
248
249
250
251
252
253
254
255 public static Database open(File mdbFile) throws IOException {
256 return open(mdbFile, false);
257 }
258
259
260
261
262
263
264
265
266
267 public static Database open(File mdbFile, boolean readOnly)
268 throws IOException
269 {
270 return open(mdbFile, readOnly, DEFAULT_AUTO_SYNC);
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288 public static Database open(File mdbFile, boolean readOnly, boolean autoSync)
289 throws IOException
290 {
291 if(!mdbFile.exists() || !mdbFile.canRead()) {
292 throw new FileNotFoundException("given file does not exist: " + mdbFile);
293 }
294 return new Database(openChannel(mdbFile,
295 (!mdbFile.canWrite() || readOnly)),
296 autoSync);
297 }
298
299
300
301
302
303
304 public static Database create(File mdbFile) throws IOException {
305 return create(mdbFile, DEFAULT_AUTO_SYNC);
306 }
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321 public static Database create(File mdbFile, boolean autoSync)
322 throws IOException
323 {
324 FileChannel channel = openChannel(mdbFile, false);
325 channel.truncate(0);
326 channel.transferFrom(Channels.newChannel(
327 Thread.currentThread().getContextClassLoader().getResourceAsStream(
328 EMPTY_MDB)), 0, Integer.MAX_VALUE);
329 return new Database(channel, autoSync);
330 }
331
332 private static FileChannel openChannel(File mdbFile, boolean readOnly)
333 throws FileNotFoundException
334 {
335 String mode = (readOnly ? "r" : "rw");
336 return new RandomAccessFile(mdbFile, mode).getChannel();
337 }
338
339
340
341
342
343
344
345 protected Database(FileChannel channel, boolean autoSync) throws IOException
346 {
347 _format = JetFormat.getFormat(channel);
348 _pageChannel = new PageChannel(channel, _format, autoSync);
349
350
351
352 _pageChannel.initialize(this);
353 _buffer = _pageChannel.createPageBuffer();
354 readSystemCatalog();
355 }
356
357 public PageChannel getPageChannel() {
358 return _pageChannel;
359 }
360
361 public JetFormat getFormat() {
362 return _format;
363 }
364
365
366
367
368 public Table getSystemCatalog() {
369 return _systemCatalog;
370 }
371
372
373
374
375 public Table getAccessControlEntries() {
376 return _accessControlEntries;
377 }
378
379
380
381
382 public boolean doUseBigIndex() {
383 return _useBigIndex;
384 }
385
386
387
388
389 public void setUseBigIndex(boolean useBigIndex) {
390 _useBigIndex = useBigIndex;
391 }
392
393
394
395
396 private void readSystemCatalog() throws IOException {
397 _systemCatalog = readTable(TABLE_SYSTEM_CATALOG, PAGE_SYSTEM_CATALOG,
398 defaultUseBigIndex());
399 for(Map<String,Object> row :
400 Cursor.createCursor(_systemCatalog).iterable(
401 SYSTEM_CATALOG_COLUMNS))
402 {
403 String name = (String) row.get(CAT_COL_NAME);
404 if (name != null && TYPE_TABLE.equals(row.get(CAT_COL_TYPE))) {
405 if (!name.startsWith(PREFIX_SYSTEM)) {
406 addTable((String) row.get(CAT_COL_NAME), (Integer) row.get(CAT_COL_ID));
407 } else if(TABLE_SYSTEM_ACES.equals(name)) {
408 int pageNumber = (Integer)row.get(CAT_COL_ID);
409 _accessControlEntries = readTable(TABLE_SYSTEM_ACES, pageNumber,
410 defaultUseBigIndex());
411 } else if(TABLE_SYSTEM_RELATIONSHIPS.equals(name)) {
412 _relationshipsPageNumber = (Integer)row.get(CAT_COL_ID);
413 }
414 } else if (SYSTEM_OBJECT_NAME_TABLES.equals(name)) {
415 _tableParentId = (Integer) row.get(CAT_COL_ID);
416 }
417 }
418
419
420 if(_accessControlEntries == null) {
421 throw new IOException("Did not find required " + TABLE_SYSTEM_ACES +
422 " table");
423 }
424 if(_tableParentId == null) {
425 throw new IOException("Did not find required parent table id");
426 }
427
428 if (LOG.isDebugEnabled()) {
429 LOG.debug("Finished reading system catalog. Tables: " +
430 getTableNames());
431 }
432 }
433
434
435
436
437 public Set<String> getTableNames() {
438 if(_tableNames == null) {
439 _tableNames = new HashSet<String>();
440 for(TableInfo tableInfo : _tableLookup.values()) {
441 _tableNames.add(tableInfo.tableName);
442 }
443 }
444 return _tableNames;
445 }
446
447
448
449
450
451
452
453
454 public Iterator<Table> iterator() {
455 return new TableIterator();
456 }
457
458
459
460
461
462 public Table getTable(String name) throws IOException {
463 return getTable(name, defaultUseBigIndex());
464 }
465
466
467
468
469
470
471
472
473 public Table getTable(String name, boolean useBigIndex) throws IOException {
474
475 TableInfo tableInfo = lookupTable(name);
476
477 if ((tableInfo == null) || (tableInfo.pageNumber == null)) {
478 return null;
479 }
480
481 return readTable(tableInfo.tableName, tableInfo.pageNumber, useBigIndex);
482 }
483
484
485
486
487
488
489 public void createTable(String name, List<Column> columns)
490 throws IOException
491 {
492 if(getTable(name) != null) {
493 throw new IllegalArgumentException(
494 "Cannot create table with name of existing table");
495 }
496 if(columns.isEmpty()) {
497 throw new IllegalArgumentException(
498 "Cannot create table with no columns");
499 }
500
501 Set<String> colNames = new HashSet<String>();
502
503 for(Column column : columns) {
504 column.validate(_format);
505 if(!colNames.add(column.getName().toUpperCase())) {
506 throw new IllegalArgumentException("duplicate column name: " +
507 column.getName());
508 }
509 }
510
511 if(Table.countAutoNumberColumns(columns) > 1) {
512 throw new IllegalArgumentException(
513 "Can have at most one AutoNumber column per table");
514 }
515
516
517 int tdefPageNumber = Table.writeTableDefinition(columns, _pageChannel,
518 _format);
519
520
521 addTable(name, Integer.valueOf(tdefPageNumber));
522
523
524 addToSystemCatalog(name, tdefPageNumber);
525 addToAccessControlEntries(tdefPageNumber);
526 }
527
528
529
530
531 public List<Relationship> getRelationships(Table table1, Table table2)
532 throws IOException
533 {
534
535 if(_relationships == null) {
536 if(_relationshipsPageNumber == null) {
537 throw new IOException("Could not find system relationships table");
538 }
539 _relationships = readTable(TABLE_SYSTEM_RELATIONSHIPS,
540 _relationshipsPageNumber,
541 defaultUseBigIndex());
542 }
543
544 int nameCmp = table1.getName().compareTo(table2.getName());
545 if(nameCmp == 0) {
546 throw new IllegalArgumentException("Must provide two different tables");
547 }
548 if(nameCmp > 0) {
549
550
551
552 Table tmp = table1;
553 table1 = table2;
554 table2 = tmp;
555 }
556
557
558 List<Relationship> relationships = new ArrayList<Relationship>();
559 Cursor cursor = createCursorWithOptionalIndex(
560 _relationships, REL_COL_FROM_TABLE, table1.getName());
561 collectRelationships(cursor, table1, table2, relationships);
562 cursor = createCursorWithOptionalIndex(
563 _relationships, REL_COL_TO_TABLE, table1.getName());
564 collectRelationships(cursor, table2, table1, relationships);
565
566 return relationships;
567 }
568
569
570
571
572
573 private void collectRelationships(
574 Cursor cursor, Table fromTable, Table toTable,
575 List<Relationship> relationships)
576 {
577 for(Map<String,Object> row : cursor) {
578 String fromName = (String)row.get(REL_COL_FROM_TABLE);
579 String toName = (String)row.get(REL_COL_TO_TABLE);
580
581 if(fromTable.getName().equals(fromName) &&
582 toTable.getName().equals(toName))
583 {
584
585 String relName = (String)row.get(REL_COL_NAME);
586
587
588
589 Relationship rel = null;
590 for(Relationship tmp : relationships) {
591 if(tmp.getName().equals(relName)) {
592 rel = tmp;
593 break;
594 }
595 }
596
597 if(rel == null) {
598
599 int numCols = (Integer)row.get(REL_COL_COLUMN_COUNT);
600 int flags = (Integer)row.get(REL_COL_FLAGS);
601 rel = new Relationship(relName, fromTable, toTable,
602 flags, numCols);
603 relationships.add(rel);
604 }
605
606
607 int colIdx = (Integer)row.get(REL_COL_COLUMN_INDEX);
608 Column fromCol = fromTable.getColumn(
609 (String)row.get(REL_COL_FROM_COLUMN));
610 Column toCol = toTable.getColumn(
611 (String)row.get(REL_COL_TO_COLUMN));
612
613 rel.getFromColumns().set(colIdx, fromCol);
614 rel.getToColumns().set(colIdx, toCol);
615 }
616 }
617 }
618
619
620
621
622
623
624 private void addToSystemCatalog(String name, int pageNumber)
625 throws IOException
626 {
627 Object[] catalogRow = new Object[_systemCatalog.getColumnCount()];
628 int idx = 0;
629 Date creationTime = new Date();
630 for (Iterator<Column> iter = _systemCatalog.getColumns().iterator();
631 iter.hasNext(); idx++)
632 {
633 Column col = iter.next();
634 if (CAT_COL_ID.equals(col.getName())) {
635 catalogRow[idx] = Integer.valueOf(pageNumber);
636 } else if (CAT_COL_NAME.equals(col.getName())) {
637 catalogRow[idx] = name;
638 } else if (CAT_COL_TYPE.equals(col.getName())) {
639 catalogRow[idx] = TYPE_TABLE;
640 } else if (CAT_COL_DATE_CREATE.equals(col.getName()) ||
641 CAT_COL_DATE_UPDATE.equals(col.getName())) {
642 catalogRow[idx] = creationTime;
643 } else if (CAT_COL_PARENT_ID.equals(col.getName())) {
644 catalogRow[idx] = _tableParentId;
645 } else if (CAT_COL_FLAGS.equals(col.getName())) {
646 catalogRow[idx] = Integer.valueOf(0);
647 } else if (CAT_COL_OWNER.equals(col.getName())) {
648 byte[] owner = new byte[2];
649 catalogRow[idx] = owner;
650 owner[0] = (byte) 0xcf;
651 owner[1] = (byte) 0x5f;
652 }
653 }
654 _systemCatalog.addRow(catalogRow);
655 }
656
657
658
659
660
661 private void addToAccessControlEntries(int pageNumber) throws IOException {
662
663 if(_newTableSIDs.isEmpty()) {
664 initNewTableSIDs();
665 }
666
667 Column acmCol = _accessControlEntries.getColumn(ACE_COL_ACM);
668 Column inheritCol = _accessControlEntries.getColumn(ACE_COL_F_INHERITABLE);
669 Column objIdCol = _accessControlEntries.getColumn(ACE_COL_OBJECT_ID);
670 Column sidCol = _accessControlEntries.getColumn(ACE_COL_SID);
671
672
673
674 List<Object[]> aceRows = new ArrayList<Object[]>(_newTableSIDs.size());
675 for(byte[] sid : _newTableSIDs) {
676 Object[] aceRow = new Object[_accessControlEntries.getColumnCount()];
677 aceRow[acmCol.getColumnIndex()] = SYS_FULL_ACCESS_ACM;
678 aceRow[inheritCol.getColumnIndex()] = Boolean.FALSE;
679 aceRow[objIdCol.getColumnIndex()] = Integer.valueOf(pageNumber);
680 aceRow[sidCol.getColumnIndex()] = sid;
681 aceRows.add(aceRow);
682 }
683 _accessControlEntries.addRows(aceRows);
684 }
685
686
687
688
689 private void initNewTableSIDs() throws IOException
690 {
691
692
693 Cursor cursor = createCursorWithOptionalIndex(
694 _accessControlEntries, ACE_COL_OBJECT_ID, _tableParentId);
695
696 for(Map<String, Object> row : cursor) {
697 Integer objId = (Integer)row.get(ACE_COL_OBJECT_ID);
698 if(_tableParentId.equals(objId)) {
699 _newTableSIDs.add((byte[])row.get(ACE_COL_SID));
700 }
701 }
702
703 if(_newTableSIDs.isEmpty()) {
704
705 _newTableSIDs.add(SYS_DEFAULT_SID);
706 }
707 }
708
709
710
711
712 private Table readTable(String name, int pageNumber, boolean useBigIndex)
713 throws IOException
714 {
715 _pageChannel.readPage(_buffer, pageNumber);
716 byte pageType = _buffer.get(0);
717 if (pageType != PageTypes.TABLE_DEF) {
718 throw new IOException("Looking for " + name + " at page " + pageNumber +
719 ", but page type is " + pageType);
720 }
721 return new Table(this, _buffer, pageNumber, name, useBigIndex);
722 }
723
724
725
726
727
728 private static Cursor createCursorWithOptionalIndex(
729 Table table, String colName, Object colValue)
730 throws IOException
731 {
732 try {
733 return new CursorBuilder(table)
734 .setIndexByColumns(table.getColumn(colName))
735 .setSpecificEntry(colValue)
736 .toCursor();
737 } catch(IllegalArgumentException e) {
738 LOG.info("Could not find expected index on table " + table.getName());
739 }
740
741 return Cursor.createCursor(table);
742 }
743
744
745
746
747
748
749 public void copyTable(String name, ResultSet source)
750 throws SQLException, IOException
751 {
752 copyTable(name, source, SimpleImportFilter.INSTANCE);
753 }
754
755
756
757
758
759
760
761 public void copyTable(String name, ResultSet source, ImportFilter filter)
762 throws SQLException, IOException
763 {
764 ResultSetMetaData md = source.getMetaData();
765 List<Column> columns = new LinkedList<Column>();
766 for (int i = 1; i <= md.getColumnCount(); i++) {
767 Column column = new Column();
768 column.setName(escape(md.getColumnName(i)));
769 int lengthInUnits = md.getColumnDisplaySize(i);
770 column.setSQLType(md.getColumnType(i), lengthInUnits);
771 DataType type = column.getType();
772
773
774
775 if(type.isTrueVariableLength() && !type.isLongValue()) {
776 column.setLengthInUnits((short)lengthInUnits);
777 }
778 if(type.getHasScalePrecision()) {
779 int scale = md.getScale(i);
780 int precision = md.getPrecision(i);
781 if(type.isValidScale(scale)) {
782 column.setScale((byte)scale);
783 }
784 if(type.isValidPrecision(precision)) {
785 column.setPrecision((byte)precision);
786 }
787 }
788 columns.add(column);
789 }
790 createTable(escape(name), filter.filterColumns(columns, md));
791 Table table = getTable(escape(name));
792 List<Object[]> rows = new ArrayList<Object[]>(COPY_TABLE_BATCH_SIZE);
793 while (source.next()) {
794 Object[] row = new Object[md.getColumnCount()];
795 for (int i = 0; i < row.length; i++) {
796 row[i] = source.getObject(i + 1);
797 }
798 rows.add(filter.filterRow(row));
799 if (rows.size() == COPY_TABLE_BATCH_SIZE) {
800 table.addRows(rows);
801 rows.clear();
802 }
803 }
804 if (rows.size() > 0) {
805 table.addRows(rows);
806 }
807 }
808
809
810
811
812
813
814
815 public void importFile(String name, File f, String delim)
816 throws IOException
817 {
818 importFile(name, f, delim, SimpleImportFilter.INSTANCE);
819 }
820
821
822
823
824
825
826
827
828 public void importFile(String name, File f, String delim,
829 ImportFilter filter)
830 throws IOException
831 {
832 BufferedReader in = null;
833 try {
834 in = new BufferedReader(new FileReader(f));
835 importReader(name, in, delim, filter);
836 } finally {
837 if (in != null) {
838 try {
839 in.close();
840 } catch (IOException ex) {
841 LOG.warn("Could not close file " + f.getAbsolutePath(), ex);
842 }
843 }
844 }
845 }
846
847
848
849
850
851
852
853 public void importReader(String name, BufferedReader in, String delim)
854 throws IOException
855 {
856 importReader(name, in, delim, SimpleImportFilter.INSTANCE);
857 }
858
859
860
861
862
863
864
865
866 public void importReader(String name, BufferedReader in, String delim,
867 ImportFilter filter)
868 throws IOException
869 {
870 String line = in.readLine();
871 if (line == null || line.trim().length() == 0) {
872 return;
873 }
874
875 String tableName = escape(name);
876 int counter = 0;
877 while(getTable(tableName) != null) {
878 tableName = escape(name + (counter++));
879 }
880
881 List<Column> columns = new LinkedList<Column>();
882 String[] columnNames = line.split(delim);
883
884 for (int i = 0; i < columnNames.length; i++) {
885 columns.add(new ColumnBuilder(escape(columnNames[i]), DataType.TEXT)
886 .setLength((short)DataType.TEXT.getMaxSize())
887 .toColumn());
888 }
889
890 try {
891 createTable(tableName, filter.filterColumns(columns, null));
892 Table table = getTable(tableName);
893 List<Object[]> rows = new ArrayList<Object[]>(COPY_TABLE_BATCH_SIZE);
894
895 while ((line = in.readLine()) != null)
896 {
897
898
899
900
901
902 Object[] data = Table.dupeRow(line.split(delim), columnNames.length);
903 rows.add(filter.filterRow(data));
904 if (rows.size() == COPY_TABLE_BATCH_SIZE) {
905 table.addRows(rows);
906 rows.clear();
907 }
908 }
909 if (rows.size() > 0) {
910 table.addRows(rows);
911 }
912 } catch(SQLException e) {
913 throw (IOException)new IOException(e.getMessage()).initCause(e);
914 }
915 }
916
917
918
919
920 public void flush() throws IOException {
921 _pageChannel.flush();
922 }
923
924
925
926
927 public void close() throws IOException {
928 _pageChannel.close();
929 }
930
931
932
933
934 private String escape(String s) {
935 if (isReservedWord(s)) {
936 return ESCAPE_PREFIX + s;
937 }
938 return s;
939 }
940
941
942
943
944
945 public static boolean isReservedWord(String s) {
946 return RESERVED_WORDS.contains(s.toLowerCase());
947 }
948
949 @Override
950 public String toString() {
951 return ToStringBuilder.reflectionToString(this);
952 }
953
954
955
956
957 private void addTable(String tableName, Integer pageNumber)
958 {
959 _tableLookup.put(toLookupTableName(tableName),
960 new TableInfo(pageNumber, tableName));
961
962 _tableNames = null;
963 }
964
965
966
967
968 private TableInfo lookupTable(String tableName) {
969 return _tableLookup.get(toLookupTableName(tableName));
970 }
971
972
973
974
975 private String toLookupTableName(String tableName) {
976 return ((tableName != null) ? tableName.toUpperCase() : null);
977 }
978
979
980
981
982
983 public boolean defaultUseBigIndex() {
984 return doUseBigIndex() || Boolean.getBoolean(USE_BIG_INDEX_PROPERTY);
985 }
986
987
988
989
990 private static class TableInfo
991 {
992 public final Integer pageNumber;
993 public final String tableName;
994
995 private TableInfo(Integer newPageNumber,
996 String newTableName) {
997 pageNumber = newPageNumber;
998 tableName = newTableName;
999 }
1000 }
1001
1002
1003
1004
1005 private class TableIterator implements Iterator<Table>
1006 {
1007 private Iterator<String> _tableNameIter;
1008
1009 private TableIterator() {
1010 _tableNameIter = getTableNames().iterator();
1011 }
1012
1013 public boolean hasNext() {
1014 return _tableNameIter.hasNext();
1015 }
1016
1017 public void remove() {
1018 throw new UnsupportedOperationException();
1019 }
1020
1021 public Table next() {
1022 if(!hasNext()) {
1023 throw new NoSuchElementException();
1024 }
1025 try {
1026 return getTable(_tableNameIter.next());
1027 } catch(IOException e) {
1028 throw new IllegalStateException(e);
1029 }
1030 }
1031 }
1032
1033 }