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.persist;
032:
033: import java.util.Enumeration;
034:
035: import org.hsqldb.Database;
036: import org.hsqldb.DatabaseURL;
037: import org.hsqldb.HsqlException;
038: import org.hsqldb.Trace;
039: import org.hsqldb.lib.HashMap;
040: import org.hsqldb.lib.HashSet;
041: import org.hsqldb.lib.Iterator;
042: import org.hsqldb.lib.Set;
043: import org.hsqldb.lib.SimpleLog;
044: import org.hsqldb.lib.java.JavaSystem;
045: import org.hsqldb.store.ValuePool;
046:
047: /**
048: * Manages a .properties file for a database.
049: *
050: * @author fredt@users
051: * @version 1.8.0
052: * @since 1.7.0
053: */
054: public class HsqlDatabaseProperties extends HsqlProperties {
055:
056: // column number mappings
057: public static final int indexName = 0;
058: public static final int indexAccess = 1;
059: public static final int indexClass = 2;
060: public static final int indexIsRange = 3;
061: public static final int indexDefaultValue = 4;
062: public static final int indexRangeLow = 5;
063: public static final int indexRangeHigh = 6;
064: public static final int indexValues = 7;
065: public static final int indexLimit = 8;
066:
067: // accessibility
068: private static final int SET_PROPERTY = 0;
069: private static final int SQL_PROPERTY = 1;
070: private static final int FILE_PROPERTY = 2;
071:
072: // db files modified
073: public static final int FILES_NOT_MODIFIED = 0;
074: public static final int FILES_MODIFIED = 1;
075: public static final int FILES_NEW = 2;
076: private static final String MODIFIED_NO = "no";
077: private static final String MODIFIED_YES = "yes";
078: private static final String MODIFIED_NEW = "no-new-files";
079:
080: // allowed property metadata
081: private static HashMap meta = new HashMap();
082:
083: // versions
084: public static final String VERSION_STRING_1_7_0 = "1.7.0";
085: public static final String VERSION_STRING_1_8_0 = "1.8.0";
086: public static final String FIRST_COMPATIBLE_VERSION = "1.8.0";
087: public static final String THIS_VERSION = "1.8.0";
088: public static final String THIS_FULL_VERSION = "1.8.0.8";
089: public static final String THIS_CACHE_VERSION = "1.7.0";
090: public static final String PRODUCT_NAME = "HSQL Database Engine";
091: public static final int MAJOR = 1, MINOR = 8, REVISION = 0;
092:
093: //
094: public static final String db_version = "version";
095: private static final String db_readonly = "readonly";
096: private static final String db_modified = "modified";
097:
098: //
099: private static final String runtime_gc_interval = "runtime.gc_interval";
100: public static final String hsqldb_applog = "hsqldb.applog";
101: public static final String hsqldb_cache_file_scale = "hsqldb.cache_file_scale";
102: public static final String hsqldb_cache_free_count_scale = "hsqldb.cache_free_count_scale";
103: public static final String hsqldb_cache_scale = "hsqldb.cache_scale";
104: public static final String hsqldb_cache_size_scale = "hsqldb.cache_size_scale";
105: public static final String hsqldb_cache_version = "hsqldb.cache_version";
106: private static final String hsqldb_catalogs = "hsqldb.catalogs";
107: public static final String hsqldb_compatible_version = "hsqldb.compatible_version";
108: public static final String hsqldb_default_table_type = "hsqldb.default_table_type";
109: public static final String hsqldb_defrag_limit = "hsqldb.defrag_limit";
110: private static final String hsqldb_files_readonly = "hsqldb.files_readonly";
111: public static final String hsqldb_log_size = "hsqldb.log_size";
112: public static final String hsqldb_nio_data_file = "hsqldb.nio_data_file";
113: public static final String hsqldb_max_nio_scale = "hsqldb.max_nio_scale";
114: public static final String hsqldb_raf_buffer_scale = "hsqldb.raf_buffer_scale";
115: private static final String hsqldb_original_version = "hsqldb.original_version";
116: public static final String hsqldb_script_format = "hsqldb.script_format";
117:
118: //
119: private static final String sql_compare_in_locale = "sql.compare_in_locale";
120: private static final String sql_enforce_strict_size = "sql.enforce_strict_size";
121: public static final String sql_tx_no_multi_write = "sql.tx_no_multi_rewrite";
122:
123: //
124: public static final String textdb_cache_scale = "textdb.cache_scale";
125: public static final String textdb_cache_size_scale = "textdb.cache_size_scale";
126: public static final String textdb_all_quoted = "textdb.all_quoted";
127: public static final String textdb_allow_full_path = "textdb.allow_full_path";
128: public static final String textdb_encoding = "textdb.encoding";
129: public static final String textdb_ignore_first = "textdb.ignore_first";
130: public static final String textdb_quoted = "textdb.quoted";
131: public static final String textdb_fs = "textdb.fs";
132: public static final String textdb_vs = "textdb.vs";
133: public static final String textdb_lvs = "textdb.lvs";
134:
135: static {
136:
137: // string defaults for protected props
138: meta.put(db_version, getMeta(db_version, FILE_PROPERTY, null));
139: meta.put(hsqldb_compatible_version, getMeta(
140: hsqldb_compatible_version, FILE_PROPERTY, null));
141: meta.put(hsqldb_cache_version, getMeta(hsqldb_cache_version,
142: FILE_PROPERTY, null));
143: meta.put(hsqldb_original_version, getMeta(
144: hsqldb_original_version, FILE_PROPERTY, null));
145: meta
146: .put(db_modified, getMeta(db_modified, FILE_PROPERTY,
147: null));
148:
149: // string defaults for user defined props
150: meta.put(hsqldb_default_table_type, getMeta(
151: hsqldb_default_table_type, SET_PROPERTY, "memory"));
152: meta.put(textdb_fs, getMeta(textdb_fs, SET_PROPERTY, ","));
153: meta.put(textdb_vs, getMeta(textdb_vs, SET_PROPERTY, null));
154: meta.put(textdb_lvs, getMeta(textdb_lvs, SET_PROPERTY, null));
155: meta.put(textdb_encoding, getMeta(textdb_encoding,
156: SET_PROPERTY, null));
157:
158: // boolean defaults for protected props
159: meta.put(db_readonly,
160: getMeta(db_readonly, FILE_PROPERTY, false));
161: meta.put(hsqldb_files_readonly, getMeta(hsqldb_files_readonly,
162: FILE_PROPERTY, false));
163: meta.put(textdb_allow_full_path, getMeta(
164: textdb_allow_full_path, FILE_PROPERTY, false));
165:
166: // boolean defaults for user defined props
167: meta.put(hsqldb_nio_data_file, getMeta(hsqldb_nio_data_file,
168: SET_PROPERTY, false));
169: meta.put(hsqldb_catalogs, getMeta(hsqldb_catalogs,
170: SET_PROPERTY, false));
171: meta.put(sql_enforce_strict_size, getMeta(
172: sql_enforce_strict_size, SET_PROPERTY, false));
173: meta.put(sql_tx_no_multi_write, getMeta(sql_tx_no_multi_write,
174: SET_PROPERTY, false));
175: meta.put(textdb_quoted, getMeta(textdb_quoted, SET_PROPERTY,
176: false));
177: meta.put(textdb_all_quoted, getMeta(textdb_all_quoted,
178: SET_PROPERTY, false));
179: meta.put(textdb_ignore_first, getMeta(textdb_ignore_first,
180: SET_PROPERTY, false));
181:
182: // integral defaults for user-defined set props
183: meta.put(hsqldb_applog, getMeta(hsqldb_applog, SET_PROPERTY, 0,
184: new byte[] { 0, 1, 2 }));
185: meta.put(hsqldb_cache_file_scale, getMeta(
186: hsqldb_cache_file_scale, SET_PROPERTY, 1, new byte[] {
187: 1, 8 }));
188: meta.put(hsqldb_script_format, getMeta(hsqldb_script_format,
189: SET_PROPERTY, 0, new byte[] { 0, 1, 3 }));
190:
191: // integral defaults for proteced range props
192: meta.put(hsqldb_log_size, getMeta(hsqldb_log_size,
193: SQL_PROPERTY, 0, 0, 16000));
194: meta.put(hsqldb_defrag_limit, getMeta(hsqldb_defrag_limit,
195: SQL_PROPERTY, 200, 0, 16000));
196:
197: // integral defaults for user defined range props
198: meta.put(runtime_gc_interval, getMeta(runtime_gc_interval,
199: SET_PROPERTY, 0, 0, 1000000));
200: meta.put(hsqldb_cache_free_count_scale, getMeta(
201: hsqldb_cache_free_count_scale, SET_PROPERTY, 9, 6, 12));
202: meta.put(hsqldb_cache_scale, getMeta(hsqldb_cache_scale,
203: SET_PROPERTY, 14, 8, 18));
204: meta.put(hsqldb_cache_size_scale, getMeta(
205: hsqldb_cache_size_scale, SET_PROPERTY, 10, 6, 20));
206: meta.put(hsqldb_max_nio_scale, getMeta(hsqldb_max_nio_scale,
207: SET_PROPERTY, 28, 24, 31));
208: meta.put(hsqldb_raf_buffer_scale, getMeta(
209: hsqldb_raf_buffer_scale, SET_PROPERTY, 12, 8, 13));
210: meta.put(textdb_cache_scale, getMeta(textdb_cache_scale,
211: SET_PROPERTY, 10, 8, 16));
212: meta.put(textdb_cache_size_scale, getMeta(
213: textdb_cache_size_scale, SET_PROPERTY, 10, 6, 20));
214: }
215:
216: private Database database;
217:
218: public HsqlDatabaseProperties(Database db) {
219:
220: super (db.getPath(), db.getFileAccess(), db.isFilesInJar());
221:
222: database = db;
223:
224: // char padding to size and exception if data is too long
225: setProperty(sql_enforce_strict_size, false);
226:
227: // removed from 1.7.2 - sql.month is always true (1-12)
228: // removed from 1.7.2 - sql.strict_fk is always enforced
229: // if true, requires a pre-existing unique index for foreign key
230: // referenced column and returns an error if index does not exist
231: // 1.61 creates a non-unique index if no index exists
232: // setProperty("sql.strict_fk", false);
233: // removed from 1.7.2
234: // has no effect if sql_strict_fk is true, otherwise if true,
235: // creates a unique index for foreign keys instead of non-unique
236: // setProperty("sql.strong_fk", true);
237: // the two properties below are meant for attempting to open an
238: // existing database with all its files *.properties, *script and
239: // *.data.
240: // version of a new database
241: setProperty(db_version, THIS_VERSION);
242:
243: // the earliest version that can open this database
244: // this is set to 1.7.2 when the db is written to
245: setProperty(hsqldb_compatible_version, FIRST_COMPATIBLE_VERSION);
246:
247: // data format of the cache file
248: // this is set to 1.7.0 when a new *.data file is created
249: setProperty(hsqldb_cache_version, THIS_CACHE_VERSION);
250:
251: // the version that created this database
252: // once created, this won't change if db is used with a future version
253: setProperty(hsqldb_original_version, THIS_VERSION);
254: /*
255: garbage collection with gc_interval
256: Setting this value can be useful when HSQLDB is used as an
257: in-process part of an application. The minimum practical
258: amount is probably "10000" and the maximum "1000000"
259:
260: In some versions of Java, such as 1.3.1_02 on windows,
261: when the application runs out of memory it runs the gc AND
262: requests more memory from the OS. Setting this property
263: forces the DB to live inside its memory budget but the
264: maximum amount of memory can still be set with the
265: java -Xmx argument to provide the memory needed by other
266: parts of the app to do graphics and networking.
267:
268: Of course there is a speed penalty for setting the value
269: too low and doing garbage collection too often.
270:
271: This was introduced as a result of tests by Karl Meissner
272: (meissnersd@users)
273: */
274:
275: // garbage collect per Record or Cache Row objects created
276: // the default, "0" means no garbage collection is forced by
277: // hsqldb (the Java Runtime will do it's own garbage collection
278: // in any case).
279: setProperty(runtime_gc_interval, 0);
280:
281: // this property is either 1 or 8
282: setProperty(hsqldb_cache_file_scale, 1);
283:
284: // this property is between 6 - 20, default 8
285: setProperty(hsqldb_cache_size_scale, 8);
286:
287: // number of rows from CACHED tables kept constantly in memory
288: // the number of rows in up to 3 * (2 to the power of
289: // cache_scale value).
290: // reduce the default 14 (3*16K rows) if memory is limited and rows
291: // are large.
292: // values between 8-16 are allowed
293: setProperty(hsqldb_cache_scale, 14);
294:
295: // maximum size of .log file in megabytes
296: setProperty(hsqldb_log_size, 200);
297:
298: // type of logging (0 : text , 1 : binary, 3 : compressed)
299: setProperty(hsqldb_script_format, 0);
300: setProperty(db_readonly, false);
301: setProperty(db_modified, "no-new-files");
302:
303: // initial method of data file access
304: setProperty(hsqldb_nio_data_file, true);
305:
306: // set default table type to MEMORY
307: setProperty(hsqldb_default_table_type, "memory");
308:
309: // the property "version" is also set to the current version
310: //
311: // the following properties can be set by the user as defaults for
312: // text tables. the default values are shown.
313: // "textdb.fs", ","
314: // "textdb.vs", ",";
315: // "textdb.lvs", ","
316: // "textdb.ignore_first", false
317: // "textdb.quoted", true
318: // "textdb.all_quoted", false
319: // "textdb.encoding", "ASCII"
320: // "textdb.cache_scale", 10 -- allowed range 8-16
321: // "textdb.cache_size_scale", 10 -- allowed range 8-20
322: //
323: // settings for OOo integration
324: if (db.isStoredFileAccess()) {
325: setProperty(hsqldb_default_table_type, "cached");
326: setProperty(hsqldb_cache_scale, 13);
327: setProperty(hsqldb_log_size, 10);
328: setProperty(sql_enforce_strict_size, true);
329: setProperty(hsqldb_nio_data_file, false);
330: }
331: }
332:
333: /**
334: * Creates file with defaults if it didn't exist.
335: * Returns false if file already existed.
336: */
337: public boolean load() throws HsqlException {
338:
339: boolean exists;
340:
341: if (!DatabaseURL.isFileBasedDatabaseType(database.getType())) {
342: return true;
343: }
344:
345: try {
346: exists = super .load();
347: } catch (Exception e) {
348: throw Trace.error(Trace.FILE_IO_ERROR,
349: Trace.LOAD_SAVE_PROPERTIES, new Object[] {
350: fileName, e });
351: }
352:
353: if (!exists) {
354: return false;
355: }
356:
357: filterLoadedProperties();
358:
359: String version = getProperty(hsqldb_compatible_version);
360:
361: // do not open if the database belongs to a later (future) version
362: int check = version.substring(0, 5).compareTo(THIS_VERSION);
363:
364: Trace.check(check <= 0, Trace.WRONG_DATABASE_FILE_VERSION);
365:
366: version = getProperty(db_version);
367:
368: if (version.charAt(2) == '6') {
369: setProperty(hsqldb_cache_version, "1.6.0");
370: }
371:
372: JavaSystem.gcFrequency = getIntegerProperty(
373: runtime_gc_interval, 0);
374:
375: return true;
376: }
377:
378: /**
379: * Sets the database member variables after creating the properties object,
380: * openning a properties file, or changing a property with a command
381: */
382: public void setDatabaseVariables() {
383:
384: if (isPropertyTrue(db_readonly)) {
385: database.setReadOnly();
386: }
387:
388: if (isPropertyTrue(hsqldb_files_readonly)) {
389: database.setFilesReadOnly();
390: }
391:
392: database.sqlEnforceStrictSize = isPropertyTrue(sql_enforce_strict_size);
393:
394: if (isPropertyTrue(sql_compare_in_locale)) {
395: stringProps.remove(sql_compare_in_locale);
396: database.collation.setCollationAsLocale();
397: }
398:
399: database.txManager
400: .setReWriteProtection(isPropertyTrue(sql_tx_no_multi_write));
401: database.setMetaDirty(false);
402: }
403:
404: public void save() throws HsqlException {
405:
406: if (!DatabaseURL.isFileBasedDatabaseType(database.getType())
407: || database.isFilesReadOnly()
408: || database.isFilesInJar()) {
409: return;
410: }
411:
412: try {
413: super .save(fileName + ".properties" + ".new");
414: fa.renameElement(fileName + ".properties" + ".new",
415: fileName + ".properties");
416: } catch (Exception e) {
417: database.logger.appLog.logContext(SimpleLog.LOG_ERROR,
418: "failed");
419:
420: throw Trace.error(Trace.FILE_IO_ERROR,
421: Trace.LOAD_SAVE_PROPERTIES, new Object[] {
422: fileName, e });
423: }
424: }
425:
426: void filterLoadedProperties() {
427:
428: Enumeration en = stringProps.propertyNames();
429:
430: while (en.hasMoreElements()) {
431: String key = (String) en.nextElement();
432: boolean accept = meta.containsKey(key);
433:
434: if (!accept) {
435: stringProps.remove(key);
436: }
437: }
438: }
439:
440: /**
441: * overload file database properties with any passed on URL line
442: * do not store password etc
443: */
444: public void setURLProperties(HsqlProperties p) {
445:
446: if (p != null) {
447: for (Enumeration e = p.propertyNames(); e.hasMoreElements();) {
448: String propertyName = (String) e.nextElement();
449: Object[] row = (Object[]) meta.get(propertyName);
450:
451: if (row != null
452: && (db_readonly.equals(propertyName) || ((Integer) row[indexAccess])
453: .intValue() == SET_PROPERTY)) {
454:
455: // can add error checking with defaults
456: setProperty(propertyName, p
457: .getProperty(propertyName));
458: }
459: }
460: }
461: }
462:
463: public Set getUserDefinedPropertyData() {
464:
465: Set set = new HashSet();
466: Iterator it = meta.values().iterator();
467:
468: while (it.hasNext()) {
469: Object[] row = (Object[]) it.next();
470:
471: if (((Integer) row[indexAccess]).intValue() == SET_PROPERTY) {
472: set.add(row);
473: }
474: }
475:
476: return set;
477: }
478:
479: public boolean isUserDefinedProperty(String key) {
480:
481: Object[] row = (Object[]) meta.get(key);
482:
483: return row != null
484: && ((Integer) row[indexAccess]).intValue() == SET_PROPERTY;
485: }
486:
487: public boolean isBoolean(String key) {
488:
489: Object[] row = (Object[]) meta.get(key);
490:
491: return row != null
492: && row[indexClass].equals("boolean")
493: && ((Integer) row[indexAccess]).intValue() == SET_PROPERTY;
494: }
495:
496: public boolean isIntegral(String key) {
497:
498: Object[] row = (Object[]) meta.get(key);
499:
500: return row != null
501: && row[indexClass].equals("int")
502: && ((Integer) row[indexAccess]).intValue() == SET_PROPERTY;
503: }
504:
505: public boolean isString(String key) {
506:
507: Object[] row = (Object[]) meta.get(key);
508:
509: return row != null
510: && row[indexClass].equals("java.lang.String")
511: && ((Integer) row[indexAccess]).intValue() == SET_PROPERTY;
512: }
513:
514: public String setDatabaseProperty(String key, String value)
515: throws HsqlException {
516:
517: Object[] row = (Object[]) meta.get(key);
518:
519: // can check bounds here
520: value = super .setProperty(key, value);
521:
522: return value;
523: }
524:
525: public int getDefaultWriteDelay() {
526: return database.isStoredFileAccess() ? 2000 : 10000;
527: }
528:
529: public void setDBModified(int mode) throws HsqlException {
530:
531: String value = MODIFIED_NO;
532:
533: if (mode == FILES_MODIFIED) {
534: value = MODIFIED_YES;
535: } else if (mode == FILES_NEW) {
536: value = MODIFIED_NEW;
537: }
538:
539: setProperty(db_modified, value);
540: save();
541: }
542:
543: public int getDBModified() throws HsqlException {
544:
545: String value = getProperty("modified");
546:
547: if (MODIFIED_YES.equals(value)) {
548: return FILES_MODIFIED;
549: } else if (MODIFIED_NEW.equals(value)) {
550: return FILES_NEW;
551: }
552:
553: return FILES_NOT_MODIFIED;
554: }
555:
556: private static Object[] getMeta(String name, int accessLevel,
557: String defaultValue) {
558:
559: Object[] row = new Object[indexLimit];
560:
561: row[indexName] = name;
562: row[indexAccess] = ValuePool.getInt(accessLevel);
563: row[indexClass] = "java.lang.String";
564: row[indexDefaultValue] = defaultValue;
565:
566: return row;
567: }
568:
569: private static Object[] getMeta(String name, int accessLevel,
570: boolean defaultValue) {
571:
572: Object[] row = new Object[indexLimit];
573:
574: row[indexName] = name;
575: row[indexAccess] = ValuePool.getInt(accessLevel);
576: row[indexClass] = "boolean";
577: row[indexDefaultValue] = defaultValue ? Boolean.TRUE
578: : Boolean.FALSE;
579:
580: return row;
581: }
582:
583: private static Object[] getMeta(String name, int accessLevel,
584: int defaultValue, byte[] values) {
585:
586: Object[] row = new Object[indexLimit];
587:
588: row[indexName] = name;
589: row[indexAccess] = ValuePool.getInt(accessLevel);
590: row[indexClass] = "int";
591: row[indexDefaultValue] = ValuePool.getInt(defaultValue);
592: row[indexValues] = values;
593:
594: return row;
595: }
596:
597: private static Object[] getMeta(String name, int accessLevel,
598: int defaultValue, int rangeLow, int rangeHigh) {
599:
600: Object[] row = new Object[indexLimit];
601:
602: row[indexName] = name;
603: row[indexAccess] = ValuePool.getInt(accessLevel);
604: row[indexClass] = "int";
605: row[indexDefaultValue] = ValuePool.getInt(defaultValue);
606: row[indexIsRange] = Boolean.TRUE;
607: row[indexRangeLow] = ValuePool.getInt(rangeLow);
608: row[indexRangeHigh] = ValuePool.getInt(rangeHigh);
609:
610: return row;
611: }
612: }
|