001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb;
032:
033: import org.hsqldb.lib.FileUtil;
034: import org.hsqldb.lib.StringConverter;
035: import org.hsqldb.persist.TextCache;
036:
037: // tony_lai@users 20020820 - patch 595099 - user define PK name
038:
039: /**
040: * Subclass of Table to handle TEXT data source. <p>
041: *
042: * Extends Table to provide the notion of an SQL base table object whose
043: * data is read from and written to a text format data file.
044: *
045: * @author sqlbob@users (RMP)
046: * @version 1.8.0
047: */
048: class TextTable extends org.hsqldb.Table {
049:
050: private String dataSource = "";
051: private boolean isReversed = false;
052: private boolean isConnected = false;
053:
054: /**
055: * Constructs a new TextTable from the given arguments.
056: *
057: * @param db the owning database
058: * @param name the table's HsqlName
059: * @param type (normal or temp text table)
060: * @param sessionid the id of the owning session (for temp table)
061: * @exception HsqlException Description of the Exception
062: */
063: TextTable(Database db, HsqlNameManager.HsqlName name, int type)
064: throws HsqlException {
065: super (db, name, type);
066: }
067:
068: /**
069: * common handling for all errors during <code>connect</code>
070: */
071: private void onConnectError(Session session) {
072:
073: if (cache != null) {
074: try {
075: cache.close(false);
076: } catch (HsqlException ex) {
077: }
078: }
079:
080: cache = null;
081:
082: clearAllRows(session);
083: }
084:
085: public boolean isConnected() {
086: return isConnected;
087: }
088:
089: /**
090: * connects to the data source
091: */
092: public void connect(Session session) throws HsqlException {
093: connect(session, isReadOnly);
094: }
095:
096: /**
097: * connects to the data source
098: */
099: private void connect(Session session, boolean withReadOnlyData)
100: throws HsqlException {
101:
102: // Open new cache:
103: if ((dataSource.length() == 0) || isConnected) {
104:
105: // nothing to do
106: return;
107: }
108:
109: try {
110: cache = database.logger.openTextCache(this , dataSource,
111: withReadOnlyData, isReversed);
112:
113: // read and insert all the rows from the source file
114: CachedRow row = null;
115: int nextpos = 0;
116:
117: if (((TextCache) cache).ignoreFirst) {
118: nextpos += ((TextCache) cache).readHeaderLine();
119: }
120:
121: while (true) {
122: row = (CachedRow) rowStore.get(nextpos);
123:
124: if (row == null) {
125: break;
126: }
127:
128: nextpos = row.getPos() + row.getStorageSize();
129:
130: row.setNewNodes();
131: insertFromTextSource(row);
132: }
133: } catch (HsqlException e) {
134: int linenumber = cache == null ? 0 : ((TextCache) cache)
135: .getLineNumber();
136:
137: onConnectError(session);
138:
139: // everything is in order here.
140: // At this point table should either have a valid (old) data
141: // source and cache or have an empty source and null cache.
142: throw Trace.error(Trace.TEXT_FILE, new Object[] {
143: new Integer(linenumber), e.getMessage() });
144: } catch (java.lang.RuntimeException t) {
145: int linenumber = cache == null ? 0 : ((TextCache) cache)
146: .getLineNumber();
147:
148: onConnectError(session);
149:
150: throw Trace.error(Trace.TEXT_FILE, new Object[] {
151: new Integer(linenumber),
152: t.getClass().getName() + ": " + t.getMessage() });
153: }
154:
155: isConnected = true;
156:
157: setIsReadOnly(withReadOnlyData);
158: }
159:
160: /**
161: * disconnects from the data source
162: */
163: public void disconnect(Session session) throws HsqlException {
164:
165: // Close old cache:
166: database.logger.closeTextCache(this );
167:
168: cache = null;
169:
170: clearAllRows(session);
171:
172: isConnected = false;
173: }
174:
175: /**
176: * This method does some of the work involved with managing the creation
177: * and openning of the cache, the rest is done in Log.java and
178: * TextCache.java.
179: *
180: * Better clarification of the role of the methods is needed.
181: */
182: private void openCache(Session session, String dataSourceNew,
183: boolean isReversedNew, boolean isReadOnlyNew)
184: throws HsqlException {
185:
186: if (dataSourceNew == null) {
187: dataSourceNew = "";
188: }
189:
190: disconnect(session);
191:
192: dataSource = dataSourceNew;
193: isReversed = (isReversedNew && dataSource.length() > 0);
194:
195: connect(session, isReadOnlyNew);
196: }
197:
198: /**
199: * High level command to assign a data source to the table definition.
200: * Reassigns only if the data source or direction has changed.
201: */
202: protected void setDataSource(Session session, String dataSourceNew,
203: boolean isReversedNew, boolean newFile)
204: throws HsqlException {
205:
206: if (getTableType() == Table.TEMP_TEXT_TABLE) {
207: ;
208: } else {
209: session.checkAdmin();
210: }
211:
212: dataSourceNew = dataSourceNew.trim();
213:
214: if (newFile && FileUtil.exists(dataSourceNew)) {
215: throw Trace.error(Trace.TEXT_SOURCE_EXISTS, dataSourceNew);
216: }
217:
218: //-- Open if descending, direction changed, file changed, or not connected currently
219: if (isReversedNew || (isReversedNew != isReversed)
220: || !dataSource.equals(dataSourceNew) || !isConnected) {
221: openCache(session, dataSourceNew, isReversedNew, isReadOnly);
222: }
223:
224: if (isReversed) {
225: setIsReadOnly(true);
226: }
227: }
228:
229: protected String getDataSource() {
230: return dataSource;
231: }
232:
233: protected boolean isDescDataSource() {
234: return isReversed;
235: }
236:
237: public void setHeader(String header) throws HsqlException {
238:
239: if (cache != null && ((TextCache) cache).ignoreFirst) {
240: ((TextCache) cache).setHeader(header);
241:
242: return;
243: }
244:
245: throw Trace.error(Trace.TEXT_TABLE_HEADER);
246: }
247:
248: public String getHeader() {
249:
250: String header = cache == null ? null : ((TextCache) cache)
251: .getHeader();
252:
253: return header == null ? null : StringConverter.toQuotedString(
254: header, '\"', true);
255: }
256:
257: /**
258: * Used by INSERT, DELETE, UPDATE operations. This class will return
259: * a more appropriate message when there is no data source.
260: */
261: void checkDataReadOnly() throws HsqlException {
262:
263: if (dataSource.length() == 0) {
264: throw Trace.error(Trace.UNKNOWN_DATA_SOURCE);
265: }
266:
267: if (isDataReadOnly()) {
268: throw Trace.error(Trace.DATA_IS_READONLY);
269: }
270: }
271:
272: public boolean isDataReadOnly() {
273: return !isConnected() || super .isDataReadOnly();
274: }
275:
276: void setDataReadOnly(boolean value) throws HsqlException {
277:
278: if (isReversed && value == false) {
279: throw Trace.error(Trace.DATA_IS_READONLY);
280: }
281:
282: openCache(null, dataSource, isReversed, value);
283: setIsReadOnly(value);
284: }
285:
286: boolean isIndexCached() {
287: return false;
288: }
289:
290: protected Table duplicate() throws HsqlException {
291: return new TextTable(database, tableName, getTableType());
292: }
293:
294: void drop() throws HsqlException {
295: openCache(null, "", false, false);
296: }
297:
298: void setIndexRoots(String s) throws HsqlException {
299:
300: // do nothing
301: }
302: }
|