0001: /*
0002: (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
0003: [See end of file]
0004: */
0005:
0006: package com.hp.hpl.jena.db.impl;
0007:
0008: import java.sql.*;
0009: import java.util.*;
0010: import java.util.zip.CRC32;
0011: import java.io.UnsupportedEncodingException;
0012: import java.lang.Thread;
0013:
0014: import com.hp.hpl.jena.datatypes.RDFDatatype;
0015: import com.hp.hpl.jena.datatypes.TypeMapper;
0016: import com.hp.hpl.jena.db.GraphRDB;
0017: import com.hp.hpl.jena.db.IDBConnection;
0018: import com.hp.hpl.jena.db.RDFRDBException;
0019: import com.hp.hpl.jena.graph.*;
0020: import com.hp.hpl.jena.graph.query.ExpressionFunctionURIs;
0021:
0022: import com.hp.hpl.jena.rdf.model.AnonId;
0023: import com.hp.hpl.jena.shared.*;
0024:
0025: import com.hp.hpl.jena.vocabulary.RDF;
0026: import com.hp.hpl.jena.vocabulary.DB;
0027:
0028: import org.apache.commons.logging.Log;
0029: import org.apache.commons.logging.LogFactory;
0030: import org.apache.xerces.util.XMLChar;
0031:
0032: //=======================================================================
0033: /**
0034: * Base database driver for implementing SpecializedGraphs.
0035: * Different drivers are needed for different databases and different
0036: * layout schemes.
0037: * <p>
0038: * This driver is a base implemention from which database-specific
0039: * drivers can inherit. It is not generic in the sense that it will work
0040: * on any minimal SQL store and so should be treated as if it were
0041: * an abstract class.
0042: * <p>The SQL statements which implement each of the functions are
0043: * loaded in a separate file etc/[layout]_[database].sql from the classpath.
0044: *
0045: * @author hkuno modification of Jena1 code by Dave Reynolds (der)
0046: * @version $Revision: 1.69 $ on $Date: 2008/01/02 12:08:22 $
0047: */
0048:
0049: public abstract class DriverRDB implements IRDBDriver {
0050:
0051: //=======================================================================
0052: // Cutomization variables
0053: // =======================================================================
0054: /**
0055: * This Graph's db properties
0056: */
0057: protected DBPropDatabase m_dbProps;
0058:
0059: /**
0060: * Name of this class's PSet_TripleStore_XXX class
0061: */
0062: protected String m_psetClassName;
0063:
0064: /**
0065: * Name of this class's PSet_TripleStore_XXX class
0066: */
0067: protected String m_psetReifierClassName;
0068:
0069: /**
0070: * Cached name of this class's SpecializedGraph_XXX class
0071: */
0072: protected String m_lsetClassName;
0073:
0074: /**
0075: * Cached name of this class's SpecializedGraphReifer_XXX class
0076: */
0077: protected String m_lsetReifierClassName;
0078:
0079: /** The class name of the database driver (e.g. jdbc.sql.class)*/
0080: protected String DRIVER_NAME;
0081: // Dummy - needs replacing when instantiated?
0082:
0083: /** The name of the database type this driver supports */
0084: protected String DATABASE_TYPE;
0085:
0086: /** The maximum size of index key (or a component of a key) */
0087: protected int INDEX_KEY_LENGTH;
0088:
0089: /** The maximum possible value for INDEX_KEY_LENGTH (db-dependent) */
0090: protected int INDEX_KEY_LENGTH_MAX;
0091:
0092: /** true if graphs using this database instance supports transactions.
0093: * this is a user settable parameter. the underlying db engine may support
0094: * transactions but an application may prefer to run without transactions
0095: * for better performance. this can only be set before the db is formatted.
0096: */
0097: protected boolean IS_XACT_DB;
0098:
0099: protected boolean STRINGS_TRIMMED;
0100: /** true if the database engine will trim trailing spaces in strings. to
0101: * prevent this, append EOS to strings that should not be trimmed.
0102: */
0103:
0104: protected String EOS = "";
0105: protected char EOS_CHAR = ':';
0106: protected int EOS_LEN = 0;
0107: /** EOS is appended to most RDB strings to deal with string trimming. if
0108: * STRINGS_TRIMMED is false, EOS is null. otherwise, EOS is EOS_CHAR.
0109: * EOS_LEN is the length of EOS (0 or 1).
0110: */
0111:
0112: protected char QUOTE_CHAR = '\"';
0113: /** the quote character used to delimit characters and strings.
0114: */
0115:
0116: /**
0117: * Indicates whether search pattern used to select system objects by name should be upper-case.
0118: */
0119: protected boolean DB_NAMES_TO_UPPER = false;
0120:
0121: /** true if URI's are to be compressed by storing prefixes (an approximation
0122: * of a namespace) in the JENA_PREFIX table. note that "short" prefixes are
0123: * not stored, i.e., the prefix length not more than URI_COMPRESS_LENGTH.
0124: */
0125: protected boolean URI_COMPRESS;
0126:
0127: protected int URI_COMPRESS_LENGTH = 100;
0128: /** if URI_COMPRESS is true, compress prefixes that are longer than this.
0129:
0130: /** The maximum size of an object that can be stored in a Statement table */
0131: protected int LONG_OBJECT_LENGTH;
0132:
0133: /** The maximum possible value for LONG_OBJECT_LENGTH (db-dependent) */
0134: protected int LONG_OBJECT_LENGTH_MAX;
0135:
0136: /** The SQL type to use for storing ids (compatible with wrapDBID) */
0137: protected String ID_SQL_TYPE;
0138:
0139: /** Set to true if the insert operations already check for duplications */
0140: protected boolean SKIP_DUPLICATE_CHECK;
0141:
0142: /** Set to true if IDs are allocated prior to insert */
0143: protected boolean PRE_ALLOCATE_ID;
0144:
0145: /** The name of the sql definition file for this database/layout combo */
0146: protected String SQL_FILE;
0147:
0148: /** The name of the sql definition file for this database/layout combo */
0149: protected String DEFAULT_SQL_FILE = "etc/generic_generic.sql";
0150:
0151: // =======================================================================
0152: // Common variables
0153: // =======================================================================
0154: /**
0155: * Holds prefix for names of Jena database tables.
0156: */
0157: protected String TABLE_NAME_PREFIX = "jena_";
0158:
0159: /**
0160: * Holds maximum length of table and index names in database.
0161: */
0162: protected int TABLE_NAME_LENGTH_MAX;
0163:
0164: /** Suffixes for asserted and reified table names. */
0165: protected String STMT_TABLE_NAME_SUFFIX = "_stmt";
0166: protected String REIF_TABLE_NAME_SUFFIX = "_reif";
0167:
0168: /** Maximum number of index columns. can be changed. */
0169: protected int MAXIMUM_INDEX_COLUMNS = 3;
0170:
0171: /** Number of required system tables. */
0172: protected int SYSTEM_TABLE_CNT = 0;
0173:
0174: /** Names of jena system tables. */
0175: public String[] SYSTEM_TABLE_NAME;
0176:
0177: /** Set to true to enable cache of pre-prepared statements */
0178: protected boolean CACHE_PREPARED_STATEMENTS = true;
0179:
0180: /** The name of the layout type this driver supports */
0181: protected String LAYOUT_TYPE = "TripleStore";
0182:
0183: /** Default name of the table that holds system property graph asserted statements **/
0184: protected String SYSTEM_STMT_TABLE;
0185:
0186: /** Name of the long literal table **/
0187: protected String LONG_LIT_TABLE;
0188:
0189: /** Name of the long URI table **/
0190: protected String LONG_URI_TABLE;
0191:
0192: /** Name of the prefix table **/
0193: protected String PREFIX_TABLE;
0194:
0195: /** Name of the graph table **/
0196: protected String GRAPH_TABLE;
0197:
0198: /** Name of the mutex table **/
0199: protected String MUTEX_TABLE;
0200:
0201: /** If not null, newly-created graphs share tables with the identified graph **/
0202: protected String STORE_WITH_MODEL = null;
0203:
0204: /** Name of the graph holding default properties (the one's that a newly-created
0205: * graph will have by default **/
0206: protected final String DEFAULT_PROPS = "JENA_DEFAULT_GRAPH_PROPERTIES";
0207:
0208: /** Unique numeric identifier of the graph holding default properties **/
0209: protected final int DEFAULT_ID = 0;
0210:
0211: /** Driver version number */
0212: protected final String VERSION = "2.0alpha";
0213:
0214: /** Database layout version */
0215: protected String LAYOUT_VERSION = "2.0";
0216:
0217: protected static Log logger = LogFactory.getLog(DriverRDB.class);
0218:
0219: // =======================================================================
0220: // Instance variables
0221: // =======================================================================
0222:
0223: /**
0224: * Instance of SQLCache used by Driver for hard-coded db commands
0225: */
0226: protected SQLCache m_sql = null;
0227:
0228: /** Cache a reference to the system property graph (java) **/
0229: protected SpecializedGraph m_sysProperties = null;
0230:
0231: protected IDBConnection m_dbcon = null;
0232:
0233: protected LRUCache prefixCache = null;
0234:
0235: public static final int PREFIX_CACHE_SIZE = 50;
0236:
0237: //===================================
0238: // for transaction support
0239: //===================================
0240:
0241: // caches whether or not underlying connection supports transactions
0242: private Boolean m_transactionsSupported;
0243:
0244: /** flag to indicate that there is a transaction active on the associated connection */
0245: private boolean inTransaction = false;
0246:
0247: // =======================================================================
0248: // Constructor
0249: // =======================================================================
0250:
0251: /**
0252: * Create a bare instance of the driver. It is not functional until a
0253: * database connection has been supplied via setConnection.
0254: */
0255: public DriverRDB() {
0256: }
0257:
0258: // =======================================================================
0259: // Methods
0260: // =======================================================================
0261:
0262: /**
0263: * Return the connection
0264: */
0265: public IDBConnection getConnection() {
0266: return m_dbcon;
0267: }
0268:
0269: /**
0270: * Return the specialized graph used to store system properties.
0271: * (Constuct a new one if necessary). if the database is not
0272: * properly formatted, then if doInit is true, the database will
0273: * be formatted, else null is returned and the (unformatted
0274: * database is unchanged).
0275: */
0276: public SpecializedGraph getSystemSpecializedGraph(boolean doInit) {
0277:
0278: SpecializedGraph res = null;
0279:
0280: if (m_sysProperties != null) {
0281: return m_sysProperties;
0282: }
0283:
0284: if (!isDBFormatOK()) {
0285: // another thread could be formatting database
0286: // so get the mutex and try again
0287: lockDB();
0288: if (!isDBFormatOK()) {
0289: if (doInit) {
0290: try {
0291: // Format the DB
0292: // throw new JenaException("The database is not
0293: // formatted.\n");
0294: doCleanDB(false);
0295: prefixCache = new LRUCache(PREFIX_CACHE_SIZE);
0296: res = formatAndConstructSystemSpecializedGraph();
0297: } catch (Exception e) {
0298: unlockDB();
0299: // We see an error during format testing, might be
0300: // a dead connection rather than an unformated
0301: // database so abort
0302: throw new JenaException(
0303: "The database appears to be unformatted or corrupted and\n"
0304: + "an attempt to automatically format the database has failed\n",
0305: e);
0306: }
0307: }
0308: unlockDB();
0309: return res;
0310: }
0311: // after second try, DB is found to be correctly formatted.
0312: unlockDB();
0313: }
0314:
0315: prefixCache = new LRUCache(PREFIX_CACHE_SIZE);
0316: getDbInitTablesParams(); //this call is a hack. it's needed because
0317: // it has the side effect of initializing some vars (e.g., EOS).
0318: IPSet pSet = createIPSetInstanceFromName(m_psetClassName,
0319: SYSTEM_STMT_TABLE);
0320: m_sysProperties = createLSetInstanceFromName(m_lsetClassName,
0321: pSet, DEFAULT_ID);
0322: m_dbProps = new DBPropDatabase(m_sysProperties);
0323:
0324: // need to get initial values for encoding parameters
0325: String longObjLen = m_dbProps.getInitLongObjectLength();
0326: String indexKeyLen = m_dbProps.getInitIndexKeyLength();
0327: String compURI = m_dbProps.getInitDoCompressURI();
0328: String compURILen = m_dbProps.getInitCompressURILength();
0329:
0330: if (longObjLen == null)
0331: throwBadFormat("long object length");
0332: else
0333: LONG_OBJECT_LENGTH = Integer.parseInt(longObjLen);
0334: if (indexKeyLen == null)
0335: throwBadFormat("index key length");
0336: else
0337: INDEX_KEY_LENGTH = Integer.parseInt(indexKeyLen);
0338: if (compURI == null)
0339: throwBadFormat("compress URIs");
0340: else
0341: URI_COMPRESS = Boolean.valueOf(compURI).booleanValue();
0342: if (compURILen == null)
0343: throwBadFormat("URI compress length");
0344: else
0345: URI_COMPRESS_LENGTH = Integer.parseInt(compURILen);
0346:
0347: // now reset the configuration parameters
0348: checkEngine(m_dbProps);
0349: checkDriverVersion(m_dbProps);
0350: checkLayoutVersion(m_dbProps);
0351: String val = null;
0352: val = m_dbProps.getIsTransactionDb();
0353: if (val == null)
0354: throwBadFormat("database supports transactions");
0355: else
0356: IS_XACT_DB = Boolean.valueOf(val).booleanValue();
0357: val = m_dbProps.getTableNamePrefix();
0358: if (val == null)
0359: throwBadFormat("table name prefix");
0360: else
0361: TABLE_NAME_PREFIX = val;
0362:
0363: return m_sysProperties;
0364: }
0365:
0366: private void checkEngine(DBProp dbProps) {
0367: String dbtype = m_dbProps.getEngineType();
0368: if (dbtype == null)
0369: throwBadFormat("database type");
0370: if (!dbtype.equals(DATABASE_TYPE)) {
0371: throw new JenaException(
0372: "Database created with incompatible database type for this version of Jena: "
0373: + dbtype);
0374: }
0375: }
0376:
0377: private void checkDriverVersion(DBProp dbProps) {
0378: String vers = m_dbProps.getDriverVersion();
0379: if (vers == null)
0380: throwBadFormat("database version");
0381: if (!vers.equals(VERSION)) {
0382: throw new JenaException(
0383: "Models in the database were created with an incompatible version of Jena: "
0384: + vers);
0385: }
0386: }
0387:
0388: private void checkLayoutVersion(DBProp dbProps) {
0389: String layout = m_dbProps.getLayoutVersion();
0390: if (layout == null)
0391: throwBadFormat("database layout");
0392: if (!layout.equals(LAYOUT_VERSION)) {
0393: throw new JenaException(
0394: "The database layout cannot be processed by this version of Jena: "
0395: + layout);
0396: }
0397:
0398: }
0399:
0400: private void throwBadFormat(String prop) {
0401: throw new JenaException(
0402: "The database appears to be unformatted or corrupted - could not find value\n"
0403: + " for \""
0404: + prop
0405: + "\" in Jena system properties table.\n"
0406: + "If possible, call IDBConnection.cleanDB(). \n"
0407: + "Warning: cleanDB will remove all Jena models from the databases.");
0408: }
0409:
0410: /**
0411: * Format the database and construct a brand new system specialized graph.
0412: */
0413: protected SpecializedGraph formatAndConstructSystemSpecializedGraph() {
0414: String errMsg = null;
0415: if (xactOp(xactIsActive))
0416: throw new RDFRDBException(
0417: "Cannot intialize database while transaction is active.\n"
0418: + "Commit or abort transaction before intializing database.");
0419:
0420: boolean autoIsOn = xactOp(xactAutoOff);
0421: try {
0422: String[] params = getDbInitTablesParams();
0423: m_sql.runSQLGroup("initDBtables", params);
0424: m_sql.runSQLGroup("initDBgenerators");// m_sql.runSQLGroup("initDBprocedures");
0425: } catch (SQLException e) {
0426: logger.warn("Problem formatting database", e);
0427: errMsg = e.toString();
0428: }
0429:
0430: if (errMsg == null)
0431: try {
0432: xactOp(xactCommit);
0433: xactOp(xactBegin);
0434:
0435: // Construct the system properties
0436: IPSet pSet = createIPSetInstanceFromName(
0437: m_psetClassName, SYSTEM_STMT_TABLE);
0438: m_sysProperties = createLSetInstanceFromName(
0439: m_lsetClassName, pSet, DEFAULT_ID);
0440:
0441: // The following call constructs a new set of database
0442: // properties and
0443: // adds them to the m_sysProperties specialized graph.
0444:
0445: // Ugh: m_dbcon.getDatabaseType(), not this.getDatabaseType()
0446: m_dbProps = new DBPropDatabase(m_sysProperties, m_dbcon
0447: .getDatabaseType(), VERSION, LAYOUT_VERSION,
0448: String.valueOf(LONG_OBJECT_LENGTH), String
0449: .valueOf(INDEX_KEY_LENGTH), String
0450: .valueOf(IS_XACT_DB), String
0451: .valueOf(URI_COMPRESS), String
0452: .valueOf(URI_COMPRESS_LENGTH),
0453: TABLE_NAME_PREFIX);
0454:
0455: // Now we also need to construct the parameters that will be the
0456: // default settings for any graph added to this database
0457: DBPropGraph def_prop = new DBPropGraph(m_sysProperties,
0458: DEFAULT_PROPS, "generic");
0459:
0460: def_prop.addGraphId(DEFAULT_ID);
0461:
0462: xactOp(xactCommit);
0463: if (autoIsOn)
0464: xactOp(xactAutoOn);
0465: } catch (Exception e) {
0466: errMsg = e.toString();
0467: }
0468:
0469: if (errMsg != null) {
0470: doCleanDB(false);
0471: m_sysProperties = null;
0472: throw new RDFRDBException(errMsg);
0473: }
0474:
0475: return m_sysProperties;
0476: }
0477:
0478: abstract String[] getDbInitTablesParams();
0479:
0480: abstract String[] getCreateTableParams(int graphId, boolean isReif);
0481:
0482: abstract public int graphIdAlloc(String graphName);
0483:
0484: /**
0485: * Construct and return a new specialized graph.
0486: */
0487: public List createSpecializedGraphs(String graphName,
0488: Graph requestedProperties) {
0489:
0490: /*
0491: * create the specialized graphs for the new graph. this includes
0492: * updating the database for the new graph (allocating a new graph
0493: * identifier, updating the jena system tables and creating tables, if
0494: * necessary. this should be done atomically to avoid corrupting the
0495: * database but a single transaction is not sufficient because some
0496: * database engines (e.g., oracle) require create table statements to
0497: * run as a separate transaction, i.e., a create table statement in the
0498: * middle of a group of updates will cause an automatic commit of the
0499: * updates prior to the create table statement.
0500: *
0501: * fortunately, we can run most of the updates in a single transaction.
0502: * however, allocation of the graph indentifier must be done prior to
0503: * creating the statement tables. so, if any subsequent operation fails,
0504: * we must run a compensating transaction to deallocate the graph
0505: * identifier.
0506: *
0507: * because of the above, we assume that there is no active transaction
0508: * when this routine is called.
0509: */
0510:
0511: // String graphName = graphProperties.getName();
0512: String stmtTbl = null;
0513: String reifTbl = null;
0514: String dbSchema = STORE_WITH_MODEL;
0515: boolean didGraphIdAlloc = false;
0516: boolean didTableCreate = false;
0517: String errMsg = null;
0518: DBPropGraph graphProperties = null;
0519:
0520: SpecializedGraph sysGraph = getSystemSpecializedGraph(false);
0521: // should have already create sys graph.
0522:
0523: if (xactOp(xactIsActive))
0524: throw new RDFRDBException(
0525: "Cannot create graph while transaction is active.\n"
0526: + "Commit or abort transaction before creating graph");
0527:
0528: boolean autoOn = xactOp(xactAutoOff);
0529: int graphId = -1; // bogus initialization to make java happy
0530:
0531: try {
0532: xactOp(xactBegin);
0533: graphId = graphIdAlloc(graphName);
0534: didGraphIdAlloc = true;
0535: xactOp(xactCommit);
0536: xactOp(xactBegin);
0537: boolean useDefault = false;
0538:
0539: // dbSchema = graphProperties.getDBSchema();
0540: // use the default schema if:
0541: // 1) no schema is specified and we are creating the default
0542: // (unnamed) graph
0543: // 2) a schema is specified and it is the default (unnamed) graph
0544: if (((dbSchema == null) && graphName
0545: .equals(GraphRDB.DEFAULT))) {
0546: useDefault = true;
0547: dbSchema = DEFAULT_PROPS; // default graph should use default
0548: // tables
0549: }
0550: // else if ( ((dbSchema != null) &&
0551: // dbSchema.equals(GraphRDB.DEFAULT)) ) {
0552: // useDefault = true;
0553: // dbSchema = DEFAULT_PROPS; // default graph should use default
0554: // tables
0555: // }
0556: if (dbSchema != null) {
0557: DBPropGraph schProp = DBPropGraph.findPropGraphByName(
0558: sysGraph, dbSchema);
0559: if (schProp != null) {
0560: reifTbl = schProp.getReifTable();
0561: stmtTbl = schProp.getStmtTable();
0562: }
0563: if (((reifTbl == null) || (stmtTbl == null))
0564: && (useDefault == false))
0565: // schema not found. this is ok ONLY IF it's the DEFAULT
0566: // schema
0567: throw new RDFRDBException("Creating graph "
0568: + graphName
0569: + ": referenced schema not found: "
0570: + dbSchema);
0571: }
0572: if ((reifTbl == null) || (stmtTbl == null)) {
0573: didTableCreate = true;
0574: reifTbl = createTable(graphId, true);
0575: stmtTbl = createTable(graphId, false);
0576: if ((reifTbl == null) || (stmtTbl == null))
0577: throw new RDFRDBException("Creating graph "
0578: + graphName + ": cannot create tables");
0579: }
0580: xactOp(xactCommit); // may not be needed but it doesn't hurt
0581: } catch (Exception e) {
0582: errMsg = e.toString();
0583: }
0584:
0585: // we can now start a new transaction and update the metadata.
0586: // we should already be committed but we commit again just in case
0587:
0588: if (errMsg == null)
0589: try {
0590: xactOp(xactBegin);
0591:
0592: graphProperties = new DBPropGraph(sysGraph, graphName,
0593: requestedProperties);
0594: graphProperties.addGraphId(graphId);
0595: graphProperties.addStmtTable(stmtTbl);
0596: graphProperties.addReifTable(reifTbl);
0597:
0598: DBPropDatabase dbprop = new DBPropDatabase(
0599: getSystemSpecializedGraph(true));
0600: dbprop.addGraph(graphProperties);
0601:
0602: // Add the reifier first
0603: DBPropPSet pSetReifier = new DBPropPSet(
0604: m_sysProperties, m_psetReifierClassName,
0605: reifTbl);
0606: DBPropLSet lSetReifier = new DBPropLSet(
0607: m_sysProperties, "LSET_"
0608: + graphProperties.getName()
0609: + "_REIFIER", m_lsetReifierClassName);
0610: lSetReifier.setPSet(pSetReifier);
0611: graphProperties.addLSet(lSetReifier);
0612:
0613: // Now add support for all non-reified triples
0614: DBPropPSet pSet = new DBPropPSet(m_sysProperties,
0615: m_psetClassName, stmtTbl);
0616: DBPropLSet lSet = new DBPropLSet(m_sysProperties,
0617: "LSET_" + graphProperties.getName(),
0618: m_lsetClassName);
0619: lSet.setPSet(pSet);
0620: graphProperties.addLSet(lSet);
0621:
0622: xactOp(xactCommit);
0623: if (autoOn)
0624: xactOp(xactAutoOn);
0625: } catch (Exception e) {
0626: errMsg = e.toString();
0627: }
0628:
0629: if (errMsg == null)
0630: return recreateSpecializedGraphs(graphProperties);
0631: else {
0632: xactOp(xactCommit); // maybe not needed but doesn't hurt
0633: xactOp(xactBegin);
0634: try {
0635: // clean-up
0636: if (didGraphIdAlloc) {
0637: graphIdDealloc(graphId);
0638: }
0639: } catch (Exception e) {
0640: }
0641: if (didTableCreate) {
0642: // make sure the order below matches
0643: // the order of creation above.
0644: if (reifTbl != null)
0645: try {
0646: deleteTable(reifTbl);
0647: } catch (Exception e) {
0648: }
0649: ;
0650: if (stmtTbl != null)
0651: try {
0652: deleteTable(stmtTbl);
0653: } catch (Exception e) {
0654: }
0655: ;
0656: }
0657: xactOp(xactCommit);
0658: if (autoOn)
0659: xactOp(xactAutoOn);
0660: return null;
0661: }
0662: }
0663:
0664: /**
0665: * Construct and return a list of specialized graphs to match those in the
0666: * store.
0667: *
0668: * @param graphProperties
0669: * A set of customization properties for the graph.
0670: */
0671: public List recreateSpecializedGraphs(DBPropGraph graphProperties) {
0672:
0673: List result = new ArrayList();
0674: int dbGraphId = graphProperties.getGraphId();
0675:
0676: // to ensure that reifier graphs occur before stmt graphs, make two passes
0677: String[] lsetTypes = { m_lsetClassName, m_lsetReifierClassName };
0678: int i;
0679: for (i = 0; i < 2; i++) {
0680: Iterator it = graphProperties.getAllLSets();
0681: while (it.hasNext()) {
0682: DBPropLSet lSetProps = (DBPropLSet) it.next();
0683: if (lSetProps.getType().equals(lsetTypes[i]))
0684: continue;
0685: DBPropPSet pSetProps = lSetProps.getPset();
0686:
0687: IPSet pSet = createIPSetInstanceFromName(pSetProps
0688: .getType(), pSetProps.getTable());
0689: result.add(createLSetInstanceFromName(lSetProps
0690: .getType(), pSet, dbGraphId));
0691: }
0692: }
0693:
0694: return result;
0695: }
0696:
0697: /**
0698: * Create a new IPSet instance of the named implementation class and set the db connection.
0699: *
0700: * @param pName name of a class that implements IPSet.
0701: * @return an instance of the named class with the db connection set.
0702: */
0703: private IPSet createIPSetInstanceFromName(String className,
0704: String tblName) {
0705: IPSet pSet = null;
0706: try {
0707: // get PSet
0708: pSet = (IPSet) Class.forName(className).newInstance();
0709: pSet.setDriver(this );
0710: pSet.setSQLType(ID_SQL_TYPE);
0711: pSet.setSkipDuplicateCheck(SKIP_DUPLICATE_CHECK);
0712: pSet.setSQLCache(m_sql);
0713: pSet.setCachePreparedStatements(CACHE_PREPARED_STATEMENTS);
0714: pSet.setTblName(tblName);
0715: } catch (Exception e) {
0716: logger.warn("Unable to create IPSet instance ", e);
0717: }
0718: return pSet;
0719: }
0720:
0721: private SpecializedGraph createLSetInstanceFromName(
0722: String lSetName, IPSet pset, int dbGraphID) {
0723: SpecializedGraph sg = null;
0724: try {
0725: Class cls = Class.forName(lSetName);
0726: Class[] params = { IPSet.class, Integer.class };
0727: java.lang.reflect.Constructor con = cls
0728: .getConstructor(params);
0729: Object[] args = { pset, new Integer(dbGraphID) };
0730: sg = (SpecializedGraph) con.newInstance(args);
0731: } catch (Exception e) {
0732: logger
0733: .error(
0734: "Unable to create instance of SpecializedGraph ",
0735: e);
0736: }
0737: return sg;
0738: }
0739:
0740: /**
0741: * Remove the specialized graph, erasing all trace of a Graph.
0742: * @param graphId The identity of the Graph which these specialized graphs should hold
0743: * @param graphProperties The properties for the graph to be removed.
0744: */
0745: public void removeSpecializedGraphs(DBPropGraph graphProperties,
0746: List specializedGraphs) {
0747:
0748: int graphId = graphProperties.getGraphId();
0749:
0750: if (xactOp(xactIsActive))
0751: throw new RDFRDBException(
0752: "Cannot remove graph while transaction is active.\n"
0753: + "Commit or abort transaction before removing graph");
0754:
0755: boolean autoIsOn = xactOp(xactAutoOff);
0756: xactOp(xactCommit);
0757: xactOp(xactBegin);
0758:
0759: // remove graph metadata from jena sys table in a xact
0760: String stmtTbl = graphProperties.getStmtTable();
0761: String reifTbl = graphProperties.getReifTable();
0762:
0763: // remove from system properties table
0764: // It is sufficient just to remove the lSet properties (it will
0765: // take care of deleting any pset properties automatically).
0766: m_dbProps.removeGraph(graphProperties);
0767:
0768: if (graphId != DEFAULT_ID)
0769: graphIdDealloc(graphId);
0770:
0771: xactOp(xactCommit);
0772: xactOp(xactBegin);
0773:
0774: /* now remove triples from statement tables.
0775: * if the graph is stored in its own tables, we
0776: * can simply delete those tables. else, the graph
0777: * shares tables with other graphs so we have to
0778: * remove each statement. */
0779:
0780: // check to see if statement tables for graph are shared
0781: boolean stInUse = true;
0782: boolean rtInUse = true;
0783:
0784: if (graphId != DEFAULT_ID) {
0785: stInUse = false;
0786: rtInUse = false;
0787: Iterator it = m_dbProps.getAllGraphs();
0788: while (it.hasNext()) {
0789: DBPropGraph gp = (DBPropGraph) it.next();
0790: if (gp.getStmtTable().equals(stmtTbl))
0791: stInUse = true;
0792: if (gp.getReifTable().equals(reifTbl))
0793: rtInUse = true;
0794: }
0795: }
0796: // now remove the statement tables or else delete all triples.
0797: if (stInUse || rtInUse) {
0798: Iterator it = specializedGraphs.iterator();
0799: while (it.hasNext()) {
0800: SpecializedGraph sg = (SpecializedGraph) it.next();
0801: removeSpecializedGraph(sg);
0802: }
0803: } else {
0804: deleteTable(stmtTbl);
0805: deleteTable(reifTbl);
0806: }
0807: xactOp(xactCommit);
0808: if (autoIsOn)
0809: xactOp(xactAutoOn);
0810: }
0811:
0812: /**
0813: * Remove specialized graph from the datastore.
0814: * @param graph is the graph to be removed.
0815: */
0816: private void removeSpecializedGraph(SpecializedGraph graph) {
0817: graph.clear();
0818: }
0819:
0820: /**
0821: * Method setDatabaseProperties.
0822: *
0823: * Sets the current properties for the database.
0824: *
0825: * @param databaseProperties is a Graph containing a full set of database properties
0826: */
0827: public void setDatabaseProperties(Graph databaseProperties) {
0828: SpecializedGraph toGraph = getSystemSpecializedGraph(true);
0829: // really need to start a transaction here
0830:
0831: // Here add code to check if the database has been used - if so,
0832: // it's too late to change the properties, so throw an exception
0833:
0834: toGraph.clear();
0835: SpecializedGraph.CompletionFlag complete = new SpecializedGraph.CompletionFlag();
0836: toGraph.add(databaseProperties, complete);
0837:
0838: // Now test the properties to see if it's a valid set - if not,
0839: // throw an exception - it's okay to check some things later (there's
0840: // no guarantee that every error will be caught here).
0841:
0842: // end transaction here.
0843: }
0844:
0845: /**
0846: * Method getDefaultModelProperties
0847: *
0848: * Return the default properties for a new model stored in this database.
0849: * If none are stored, then load default properties into the database.
0850: * @return Graph containg the default properties for a new model
0851: */
0852: public DBPropGraph getDefaultModelProperties() {
0853: SpecializedGraph sg = getSystemSpecializedGraph(true);
0854: DBPropGraph result = DBPropGraph.findPropGraphByName(sg,
0855: DEFAULT_PROPS);
0856: if (result == null) {
0857: logger.error("No default Model Properties found");
0858: // Construct the parameters that will be the
0859: // default settings for any graph added to this database
0860: //new DBPropGraph( m_sysProperties, "default", "generic");
0861: //result = DBPropGraph.findPropGraph(sg, "default");
0862: }
0863: return result;
0864: }
0865:
0866: /**
0867: * Test if the database has previously been formatted.
0868: *
0869: * @return boolean true if database is correctly formatted, false on any error.
0870: */
0871: public boolean isDBFormatOK() throws RDFRDBException {
0872: boolean result = true;
0873: boolean[] found = new boolean[SYSTEM_TABLE_CNT];
0874: int i = 0;
0875: for (i = 0; i < SYSTEM_TABLE_CNT; i++)
0876: found[i] = false;
0877: try {
0878: for (Iterator iter = getAllTables().iterator(); iter
0879: .hasNext();) {
0880: String tblName = (String) iter.next();
0881: for (i = 0; i < SYSTEM_TABLE_CNT; i++)
0882: if (SYSTEM_TABLE_NAME[i].equals(tblName))
0883: found[i] = true;
0884: }
0885:
0886: for (i = 0; i < SYSTEM_TABLE_CNT; i++) {
0887: if (!found[i]) {
0888: // mutex table is not required
0889: if (SYSTEM_TABLE_NAME[i].equals(MUTEX_TABLE))
0890: continue;
0891: result = false;
0892: }
0893: }
0894: } catch (Exception e1) {
0895: // An exception might be an unformatted or corrupt
0896: // db or a connection problem.
0897: throw new RDFRDBException(
0898: "Exception while checking db format - " + e1, e1);
0899: }
0900: return result;
0901: }
0902:
0903: /**
0904: * Converts string to form accepted by database.
0905: */
0906: public String stringToDBname(String aName) {
0907: String result = (DB_NAMES_TO_UPPER) ? aName.toUpperCase()
0908: : aName;
0909: return (result);
0910: }
0911:
0912: private static final int lockTryMax = 5; // max attempts to acquire/release lock
0913:
0914: /**
0915: * return true if the mutex is acquired, else false
0916: */
0917:
0918: public boolean tryLockDB() {
0919: boolean res = true;
0920: try {
0921: m_sql.runSQLGroup("lockDatabase", MUTEX_TABLE);
0922: } catch (SQLException e) {
0923: res = false;
0924: }
0925: return res;
0926: }
0927:
0928: public void lockDB() throws RDFRDBException {
0929: String err = "";
0930: int cnt = 0;
0931: while (cnt++ < lockTryMax) {
0932: if (tryLockDB())
0933: break;
0934: try {
0935: Thread.sleep((long) 5000);
0936: } catch (InterruptedException e) {
0937: err = err + " lockDB sleep interrupted" + e;
0938: }
0939: }
0940: if (cnt >= lockTryMax) {
0941: err = "Failed to lock database after "
0942: + lockTryMax
0943: + " attempts.\n"
0944: + err
0945: + "\n"
0946: + "Try later or else call DriverRDB.unlockDB() after ensuring\n"
0947: + "that no other Jena applications are using the database.";
0948: throw new RDFRDBException(err);
0949: }
0950: }
0951:
0952: /**
0953: * Release the mutex lock in the database.
0954: */
0955:
0956: public void unlockDB() throws RDFRDBException {
0957: String err;
0958: int cnt = 0;
0959: while (cnt++ < lockTryMax) {
0960: try {
0961: m_sql.runSQLGroup("unlockDatabase", MUTEX_TABLE);
0962: break;
0963: } catch (SQLException e) {
0964: err = "Failed to unlock database after " + lockTryMax
0965: + " attempts - " + e;
0966: try {
0967: Thread.sleep((long) 5000);
0968: } catch (InterruptedException e1) {
0969: err = err + " sleep failed" + e;
0970: }
0971: }
0972: if (cnt >= lockTryMax)
0973: throw new RDFRDBException(err);
0974: }
0975: }
0976:
0977: /* return true if the mutex is held. */
0978:
0979: public boolean DBisLocked() throws RDFRDBException {
0980: try {
0981: DatabaseMetaData dbmd = m_dbcon.getConnection()
0982: .getMetaData();
0983: String[] tableTypes = { "TABLE" };
0984: String prefixMatch = stringToDBname(TABLE_NAME_PREFIX + "%");
0985: ResultSet iter = dbmd.getTables(null, null, MUTEX_TABLE,
0986: tableTypes);
0987: try {
0988: return iter.next();
0989: } finally {
0990: iter.close();
0991: }
0992: } catch (SQLException e1) {
0993: throw new RDFRDBException("Internal SQL error in driver"
0994: + e1);
0995: }
0996: }
0997:
0998: /* (non-Javadoc)
0999: * @see com.hp.hpl.jena.graphRDB.IRDBDriver#cleanDB()
1000: */
1001: public void cleanDB() {
1002:
1003: // assumes database lock is not held.
1004: try {
1005: lockDB();
1006: } catch (RDFRDBException e) {
1007: throw new RDFRDBException(
1008: "DriverRDB.cleanDB() failed to acquire database lock:\n"
1009: + "("
1010: + e
1011: + ")\n."
1012: + "Try again or call DriverRDB.unlockDB() if necessary.");
1013: }
1014: // now clean the database
1015: doCleanDB(true);
1016: }
1017:
1018: /*
1019: * internal routine that does the actual work for cleanDB().
1020: * it assumes that the mutex is held and throws and exception
1021: * if not. it will optionally remove the mutex if dropMutex
1022: * is true.
1023: */
1024:
1025: protected void doCleanDB(boolean dropMutex) throws RDFRDBException {
1026: try {
1027: if (!DBisLocked()) {
1028: throw new RDFRDBException(
1029: "Internal error in driver - database not locked for cleaning.\n");
1030: }
1031: } catch (RDFRDBException e) {
1032: throw new RDFRDBException(
1033: "Exception when checking for database lock - \n"
1034: + e);
1035: }
1036: //ResultSet alltables=null;
1037: try {
1038: List tablesPresent = getAllTables();
1039: Iterator it = tablesPresent.iterator();
1040: // Do the MUTEX clean after all other tables.
1041: while (it.hasNext()) {
1042: String tblName = (String) it.next();
1043: if (tblName.equals(MUTEX_TABLE))
1044: continue;
1045: m_sql.runSQLGroup("dropTable", tblName);
1046: }
1047:
1048: // Mutex to be removed as well?
1049: if (dropMutex && tablesPresent.contains(MUTEX_TABLE))
1050: m_sql.runSQLGroup("dropTable", MUTEX_TABLE);
1051:
1052: if (PRE_ALLOCATE_ID)
1053: clearSequences();
1054:
1055: } catch (SQLException e1) {
1056: throw new RDFRDBException(
1057: "Internal error in driver while cleaning database\n"
1058: + "("
1059: + e1
1060: + ").\n"
1061: + "Database may be corrupted. Try cleanDB() again.");
1062: }
1063: m_sysProperties = null;
1064: if (prefixCache != null)
1065: prefixCache.clear();
1066: prefixCache = null;
1067: }
1068:
1069: protected List getAllTables() {
1070: try {
1071: DatabaseMetaData dbmd = m_dbcon.getConnection()
1072: .getMetaData();
1073: String[] tableTypes = { "TABLE" };
1074: String prefixMatch = stringToDBname(TABLE_NAME_PREFIX + "%");
1075: ResultSet rs = dbmd.getTables(null, null, prefixMatch,
1076: tableTypes);
1077: List tables = new ArrayList();
1078: while (rs.next())
1079: tables.add(rs.getString("TABLE_NAME"));
1080: rs.close();
1081: return tables;
1082: } catch (SQLException e1) {
1083: throw new RDFRDBException("Internal SQL error in driver - "
1084: + e1);
1085: }
1086: }
1087:
1088: /**
1089: * Drop all Jena-related sequences from database, if necessary.
1090: * Override in subclass if sequences must be explicitly deleted.
1091: */
1092: public void clearSequences() {
1093: }
1094:
1095: /**
1096: * Removes named sequence from the database, if it exists.
1097: * @param seqName
1098: */
1099: public void removeSequence(String seqName) {
1100: if (sequenceExists(seqName)) {
1101: try {
1102: m_sql.runSQLGroup("DropSequence", seqName);
1103: } catch (Exception e) {
1104: logger.warn("Unable to drop sequence " + seqName, e);
1105: }
1106: }
1107: }
1108:
1109: /**
1110: * Check database and see if named sequence exists.
1111: * @param seqName
1112: */
1113: public boolean sequenceExists(String seqName) {
1114: Object[] args = { seqName };
1115: ResultSet rs = null;
1116: boolean result = false;
1117: PreparedStatement ps = null;
1118: try {
1119: String op = "SelectSequenceName";
1120: ps = m_sql.getPreparedSQLStatement(op);
1121: ps.setString(1, seqName);
1122: rs = ps.executeQuery();
1123: result = rs.next();
1124: } catch (Exception e) {
1125: logger.error("Unable to select sequence " + seqName, e);
1126: } finally {
1127: if (rs != null)
1128: try {
1129: rs.close();
1130: } catch (SQLException e1) {
1131: throw new RDFRDBException(
1132: "Failed to get last inserted ID: " + e1);
1133: }
1134: if (ps != null)
1135: m_sql.returnPreparedSQLStatement(ps);
1136: }
1137: return result;
1138: }
1139:
1140: /**
1141: * Check database and see if named sequence exists.
1142: * @param seqName
1143: */
1144: public List getSequences() {
1145: List results = new ArrayList(10);
1146: Object[] args = {};
1147: ResultSet rs = null;
1148: PreparedStatement ps = null;
1149: try {
1150: String opname = "SelectJenaSequences";
1151: ps = m_sql.getPreparedSQLStatement(opname,
1152: TABLE_NAME_PREFIX);
1153: rs = ps.executeQuery();
1154: while (rs.next())
1155: results.add(rs.getString(1));
1156: //rs.close(); //Removed after jena 2.4.
1157: } catch (Exception e) {
1158: logger.error("Unable to select Jena sequences: ", e);
1159: } finally {
1160: if (rs != null)
1161: try {
1162: rs.close();
1163: } catch (SQLException e1) {
1164: throw new RDFRDBException(
1165: "Failed to get last inserted ID: " + e1);
1166: }
1167: if (ps != null)
1168: m_sql.returnPreparedSQLStatement(ps);
1169: }
1170: return results;
1171: }
1172:
1173: /**
1174: * Initialise a database ready to store RDF tables.
1175: * @throws RDFDBException if the is a problem opening the connection or an internal SQL error.
1176: * @deprecated Since Jena 2.0 this call is no longer needed - formatting
1177: * happens automatically as a side effect of creating Models - there should
1178: * be no need for an application to interact directly with the driver.
1179: */
1180: public void formatDB() throws RDFRDBException {
1181: }
1182:
1183: /**
1184: * Create a table for storing asserted or reified statements.
1185: *
1186: * @param graphId the graph which the table is created.
1187: * @param isReif true if table stores reified statements.
1188: * @return the name of the new table
1189: *
1190: */
1191: public String createTable(int graphId, boolean isReif) {
1192: String opname = isReif ? "createReifStatementTable"
1193: : "createStatementTable";
1194: int i = 0;
1195: String params[];
1196: while (true) {
1197: params = getCreateTableParams(graphId, isReif);
1198: try {
1199: m_sql.runSQLGroup(opname, params);
1200: break;
1201: } catch (SQLException e) {
1202: i++;
1203: if (i > 5) {
1204: logger.warn("Problem creating table", e);
1205: throw new RDFRDBException(
1206: "Failed to create table: " + params[0], e);
1207: }
1208: }
1209: }
1210: return params[0];
1211: }
1212:
1213: /**
1214: * Delete a table.
1215: *
1216: * @param tableName the name of the table to delete. *
1217: */
1218: public void deleteTable(String tableName) {
1219:
1220: String opname = "dropTable";
1221: PreparedStatement ps = null;
1222: try {
1223: ps = m_sql.getPreparedSQLStatement(opname, tableName);
1224: ps.executeUpdate();
1225: return;
1226: } catch (Exception e1) {
1227: throw new RDFRDBException("Failed to delete table ", e1);
1228: } finally {
1229: if (ps != null)
1230: m_sql.returnPreparedSQLStatement(ps);
1231: }
1232: }
1233:
1234: /**
1235: * Throws an UnsupportedOperation exception.
1236: *
1237: * @param opName name of the operation that's not supported.
1238: */
1239: private void notSupported(String opName) {
1240: throw new UnsupportedOperationException(opName);
1241: }
1242:
1243: protected static final int xactBegin = 0;
1244: protected static final int xactCommit = 1;
1245: protected static final int xactAbort = 2;
1246: protected static final int xactIsActive = 3;
1247: protected static final int xactAutoOff = 4;
1248: protected static final int xactAutoOn = 5;
1249: protected static final int xactBeginIfNone = 6;
1250:
1251: /**
1252: * Perform a transaction operation. For begin/commit/abort,
1253: * return true if success, false if fail. for xactIsActive,
1254: * return true if this driver has an active transaction,
1255: * else return false.
1256: * for beginIfNone, if there is a transaction running, return false, otherwise
1257: *
1258: */
1259: protected synchronized boolean xactOp(int op)
1260: throws RDFRDBException {
1261: try {
1262: return xaxtOpRaw(op);
1263: } catch (SQLException e) {
1264: throw new JenaException("Transaction support failed: ", e);
1265: }
1266: }
1267:
1268: private boolean xaxtOpRaw(int op) throws SQLException {
1269: boolean ret = true;
1270: if (op == xactBegin) {
1271: // start a transaction
1272: // always return true
1273: if (!inTransaction) {
1274: xactBegin();
1275: inTransaction = true;
1276: }
1277: } else if (op == xactBeginIfNone) {
1278: if (inTransaction)
1279: ret = false;
1280: else {
1281: xactBegin();
1282: inTransaction = true;
1283: }
1284: } else if (op == xactCommit) {
1285: // commit a transaction
1286: // always return true
1287: if (inTransaction) {
1288: xactCommit();
1289: inTransaction = false;
1290: }
1291: } else if (op == xactAbort) {
1292: // rollback a transaction
1293: // always return true
1294: if (inTransaction) {
1295: xactAbort();
1296: inTransaction = false;
1297: }
1298: } else if (op == xactIsActive) {
1299: // return true if xact is active, else false
1300: ret = inTransaction;
1301: } else if (op == xactAutoOff) {
1302: // disable autocommit
1303: // return true if autocommit is on, else false
1304: // begins a new transaction
1305: Connection c = m_sql.getConnection();
1306: ret = c.getAutoCommit();
1307: if (ret)
1308: xactBegin();
1309: inTransaction = true;
1310: } else if (op == xactAutoOn) {
1311: // enable autocommit
1312: // always return true
1313: if (inTransaction)
1314: throw new JenaException(
1315: "Can't enable AutoCommit in middle of existing transaction");
1316: Connection c = m_sql.getConnection();
1317: c.setAutoCommit(true);
1318: ret = true;
1319: } else
1320: throw new JenaException("Unknown transaction operation: "
1321: + op);
1322: return ret;
1323: }
1324:
1325: private void xactBegin() throws RDFRDBException {
1326: try {
1327: Connection c = m_sql.getConnection();
1328: try {
1329: if (c.getTransactionIsolation() != Connection.TRANSACTION_READ_COMMITTED) {
1330: c
1331: .setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
1332: }
1333: if (c.getAutoCommit()) {
1334: c.setAutoCommit(false);
1335: }
1336: } catch (Exception e) {
1337: e.printStackTrace();
1338: }
1339: // Starting a transaction could require us to lose any
1340: // cached prepared statements
1341: // for some jdbc drivers, currently I think all the drivers
1342: // we use are safe and
1343: // is a major performance hit so commented out for now.
1344: //m_sql.flushPreparedStatementCache();
1345: } catch (SQLException e) {
1346: throw new JenaException("Transaction begin failed: ", e);
1347: }
1348: }
1349:
1350: private void xactAbort() throws RDFRDBException {
1351: try {
1352: Connection c = m_sql.getConnection();
1353: c.rollback();
1354: c.commit();
1355: c.setAutoCommit(true);
1356: } catch (SQLException e) {
1357: throw new JenaException("Transaction rollback failed: ", e);
1358: }
1359: }
1360:
1361: private void xactCommit() throws RDFRDBException {
1362: try {
1363: Connection c = m_sql.getConnection();
1364: c.commit();
1365: try {
1366: c.setAutoCommit(true);
1367: } catch (Exception e) {
1368: e.printStackTrace();
1369: }
1370: // not sure why read_uncommitted is set, below. commented
1371: // out by kw.
1372: // c.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
1373: } catch (SQLException e) {
1374: throw new JenaException("Transaction commit failed: ", e);
1375: }
1376: }
1377:
1378: /**
1379: * If the underlying database connection supports transactions, turn
1380: * autocommit off, then begin a new transaction. Note that transactions are
1381: * associated with connections, not with Models. This
1382: */
1383: public synchronized void begin() throws RDFRDBException {
1384: if (transactionsSupported()) {
1385: xactOp(xactBegin);
1386: } else {
1387: notSupported("begin transaction");
1388: }
1389: }
1390:
1391: /**
1392: * If the underlying database connection supports transactions, call
1393: * commit(), then turn autocommit on.
1394: */
1395: public void commit() throws RDFRDBException {
1396: if (transactionsSupported()) {
1397: xactOp(xactCommit);
1398: } else {
1399: notSupported("commit transaction");
1400: }
1401: }
1402:
1403: /**
1404: * If underlying database connection supports transactions, call abort() on
1405: * the connection, then turn autocommit on.
1406: */
1407: public synchronized void abort() throws RDFRDBException {
1408: if (transactionsSupported()) {
1409: xactOp(xactAbort);
1410: } else {
1411: notSupported("abort transaction");
1412: }
1413: }
1414:
1415: /**
1416: * Return a string identifying underlying database type.
1417: *
1418: */
1419: public String getDatabaseType() {
1420: return (DATABASE_TYPE);
1421: }
1422:
1423: /**
1424: * Returns true if the underlying database supports transactions.
1425: */
1426: public boolean transactionsSupported() {
1427: if (m_transactionsSupported != null) {
1428: return (m_transactionsSupported.booleanValue());
1429: }
1430:
1431: if (m_dbcon != null) {
1432: try {
1433: Connection c = m_sql.getConnection();
1434: if (c != null) {
1435: m_transactionsSupported = new Boolean(c
1436: .getMetaData()
1437: .supportsMultipleTransactions());
1438: return (m_transactionsSupported.booleanValue());
1439: }
1440: } catch (SQLException e) {
1441: logger.error("SQL Exception caught ", e);
1442: }
1443: }
1444: return (false);
1445:
1446: }
1447:
1448: //--------------------------------------------------jena 1 backward compatability
1449:
1450: /**
1451: * Close the driver
1452: *
1453: * Nothing to do for now.
1454: *
1455: * @throws RDFDBException if there is an access problem
1456: * @deprecated Since Jena 2.0 this call is no longer required - just
1457: * close the DBConnection - there should be no need for an application
1458: * to interact directly with the driver.
1459: *
1460: */
1461:
1462: public void close() throws RDFRDBException {
1463: }
1464:
1465: /**
1466: * Returns true if the database layout supports multiple RDF models
1467: * in the same database.
1468: * @deprecated Since Jena 2.0 all databases support multiple models.
1469: */
1470:
1471: public boolean supportsMultipleModels() {
1472: return true;
1473: }
1474:
1475: /**
1476: * Returns true if the database layout supports implicit reification
1477: * of statements (i.e. statements can be treated as resources).
1478: * @deprecated Since Jena 2.0 the reification API has changed. The
1479: * new API is supported in all models, but the old Jena 1 API is no
1480: * longer supported. This call will return false to indicate
1481: * to old code that the old style of jena reification is not supported.
1482: */
1483:
1484: public boolean supportsJenaReification() {
1485: return false;
1486: }
1487:
1488: /*
1489: * The following routines are responsible for encoding nodes
1490: * as database structures. For each node type stored (currently,
1491: * literals, URI, blank), there are two possible encodings
1492: * depending on the node size. Small nodes may be stored
1493: * within a statement table. If the node is long (will not
1494: * fit within the statement table), it is be stored in a
1495: * separate table for that node type.
1496: *
1497: * In addition, for resources (URI, blank nodes), the URI
1498: * may be optionally compressed. Below, the possibilites
1499: * are enumerated.
1500: *
1501: * Literal Encoding in Statement Tables
1502: * Short Literal: Lv:[langLen]:[datatypeLen]:[langString][datatypeString]value[:]
1503: * Long Literal: Lr:dbid
1504: * Literal Encoding in Long Literal Table
1505: * Literal: Lv:[langLen]:[datatypeLen]:[langString][datatypeString]head[:] hash tail
1506: *
1507: * Comments:
1508: * L indicates a literal
1509: * v indicates a value
1510: * r indicates a reference to another table
1511: * : is used as a delimiter. note that MySQL trims trailing white space for
1512: * certain VARCHAR columns so an extra delimiter is appended when necessary
1513: * for those columns. it is not required for dbid, however.
1514: * dbid references the long literal table
1515: * langLen is the length of the language identifier for the literal
1516: * langString is the language identifier
1517: * datatypeLen is the length of the datatype for the literal
1518: * datatypeString is the datatype for the literal
1519: * value is the lexical form of the string
1520: * head is a prefix of value that can be indexed
1521: * hash is the CRC32 hash value for the tail
1522: * tail is the remainder of the value that cannot be indexed
1523: *
1524: *
1525: *
1526: * URI Encoding in Statement Tables
1527: * Short URI: Uv:[pfx_dbid]:URI[:]
1528: * Long URI: Ur:[pfx_dbid]:dbid
1529: * URI Encoding in Long URI Table
1530: * URI: Uv:head[:] hash tail
1531: *
1532: * Comments:
1533: * U indicates a URI
1534: * pfx_dbid references the prefix table. if the prefix is too
1535: * short (i.e., the length of the prefix is less than
1536: * URI_COMPRESS_LENGTH), the URI is not compressed and
1537: * pfx_dbid is null.
1538: * URI is the complete URI
1539: * other notation same as for literal encoding
1540: *
1541: * Blank Node Encoding in Statement Tables
1542: * Short URI: Bv:[pfx_dbid]:bnid[:]
1543: * Long URI: Br:[pfx_dbid]:dbid
1544: * Blank Encoding in Long URI Table
1545: * URI: Bv:head[:] hash tail
1546: *
1547: * Comments:
1548: * B indicates a blank node
1549: * bnid is the blank node identifier
1550: * other notation same as above
1551: * Note: currently, blank nodes are always stored uncompressed (pfix_dbid is null).
1552: *
1553: * Variable Node Encoding in Statement Tables
1554: * Variable Node: Vv:name
1555: *
1556: * Comments:
1557: * V indicates a variable node
1558: * v indicates a value
1559: * name is the variable name
1560: * Note: the length must be less than LONG_OBJECT_LENGTH
1561: *
1562: * ANY Node Encoding in Statement Tables
1563: * Variable Node: Av:
1564: *
1565: * Prefix Encoding in Prefix Table
1566: * Prefix: Pv:val[:] [hash] [tail]
1567: *
1568: * Comments:
1569: * P indicates a prefix
1570: * other notation same as above
1571: * hash and tail are only required for long prefixes.
1572: *
1573: */
1574:
1575: protected static String RDBCodeURI = "U";
1576: protected static String RDBCodeBlank = "B";
1577: protected static String RDBCodeLiteral = "L";
1578: protected static String RDBCodeVariable = "V";
1579: protected static String RDBCodeANY = "A";
1580: protected static String RDBCodePrefix = "P";
1581: protected static String RDBCodeValue = "v";
1582: protected static String RDBCodeRef = "r";
1583: protected static String RDBCodeDelim = ":";
1584: protected static char RDBCodeDelimChar = ':';
1585: protected static String RDBCodeInvalid = "X";
1586:
1587: /**
1588: * Convert a node to a string to be stored in a statement table.
1589: * @param Node The node to convert to a string. Must be a concrete node.
1590: * @param addIfLong If the node is a long object and is not in the database, add it.
1591: * @return the string or null if failure.
1592: */
1593: public String nodeToRDBString(Node node, boolean addIfLong)
1594: throws RDFRDBException {
1595: String res = null;
1596: if (node.isURI()) {
1597: String uri = new String(((Node_URI) node).getURI());
1598: if (uri.startsWith(RDBCodeURI)) {
1599: throw new RDFRDBException(
1600: "URI Node looks like a blank node: " + uri);
1601: }
1602: // TODO: need to write special version of splitNamespace for rdb.
1603: // or else, need a guarantee that splitNamespace never changes.
1604: // the problem is that if the splitNamespace algorithm changes,
1605: // then URI's may be encoded differently. so, URI's in existing
1606: // databases may become inaccessible.
1607: int pos = 0;
1608: boolean noCompress;
1609: String pfx;
1610: String qname;
1611: if (URI_COMPRESS == true) {
1612: pos = dbSplitNamespace(uri);
1613: if (uri.startsWith(DB.uri))
1614: noCompress = true;
1615: else
1616: noCompress = (pos == uri.length())
1617: || (pos <= URI_COMPRESS_LENGTH);
1618: } else
1619: noCompress = true;
1620: if (noCompress) {
1621: pfx = RDBCodeDelim + RDBCodeDelim;
1622: qname = uri;
1623: } else {
1624: // see if it's cached
1625: DBIDInt pfxid = URItoPrefix(uri, pos, addIfLong);
1626: if (pfxid == null)
1627: return res;
1628: pfx = RDBCodeDelim + ((DBIDInt) pfxid).getIntID()
1629: + RDBCodeDelim;
1630: qname = uri.substring(pos);
1631: }
1632: int encodeLen = RDBCodeURI.length() + 1 + pfx.length()
1633: + EOS_LEN;
1634: boolean URIisLong = objectIsLong(encodeLen, qname);
1635: if (URIisLong) {
1636: int dbid;
1637: // belongs in URI table
1638: DBIDInt URIid = getURIID(qname, addIfLong);
1639: if (URIid == null)
1640: return res;
1641: dbid = URIid.getIntID();
1642: res = new String(RDBCodeURI + RDBCodeRef + pfx + dbid);
1643: } else {
1644: res = RDBCodeURI + RDBCodeValue + pfx + qname + EOS;
1645: }
1646: } else if (node.isLiteral()) {
1647: // TODO: may need to encode literal value when datatype is not a string.
1648: Node_Literal litNode = (Node_Literal) node;
1649: String lval = litNode.getLiteralLexicalForm();
1650: String lang = litNode.getLiteralLanguage();
1651: String dtype = litNode.getLiteralDatatypeURI();
1652: String ld = litLangTypeToRDBString(lang, dtype);
1653: int encodeLen = RDBCodeLiteral.length() + 2 + ld.length()
1654: + EOS_LEN;
1655: boolean litIsLong = objectIsLong(encodeLen, lval);
1656:
1657: if (litIsLong) {
1658: int dbid;
1659:
1660: //System.err.println("Long literal("+lval.length()+" => "+encodeLen+")") ;
1661:
1662: // belongs in literal table
1663: DBIDInt lid = getLiteralID(litNode, addIfLong);
1664: if (lid == null)
1665: return res;
1666: dbid = lid.getIntID();
1667: res = new String(RDBCodeLiteral + RDBCodeRef
1668: + RDBCodeDelim + dbid);
1669: } else {
1670: res = new String(RDBCodeLiteral + RDBCodeValue
1671: + RDBCodeDelim + ld + lval + EOS);
1672: }
1673: } else if (node.isBlank()) {
1674: String bnid = node.getBlankNodeId().toString();
1675: String delims = "::";
1676: int encodeLen = RDBCodeBlank.length() + 1 + delims.length()
1677: + EOS_LEN;
1678: boolean BisLong = objectIsLong(encodeLen, bnid);
1679: if (BisLong) {
1680: int dbid;
1681: // belongs in URI table
1682: DBIDInt URIid = getBlankID(bnid, addIfLong);
1683: if (URIid == null)
1684: return res;
1685: dbid = URIid.getIntID();
1686: res = new String(RDBCodeBlank + RDBCodeRef + delims
1687: + dbid);
1688: } else {
1689: res = new String(RDBCodeBlank + RDBCodeValue + delims
1690: + bnid + EOS);
1691: }
1692:
1693: } else if (node.isVariable()) {
1694: String name = ((Node_Variable) node).getName();
1695: int len = name.length();
1696: if ((len + 3 + EOS_LEN) > LONG_OBJECT_LENGTH)
1697: throw new JenaException("Variable name too long: "
1698: + name);
1699: res = RDBCodeVariable + RDBCodeValue + RDBCodeDelim + name
1700: + EOS;
1701: } else if (node.equals(Node.ANY)) {
1702: res = RDBCodeANY + RDBCodeValue + RDBCodeDelim;
1703: } else {
1704: throw new RDFRDBException("Expected Concrete Node, got "
1705: + node.toString());
1706: }
1707: return res;
1708: }
1709:
1710: /**
1711: * Convert an RDB string to the node that it encodes. Return null if failure.
1712: * @param RDBstring The string to convert to a node.
1713: * @return The node or null if failure.
1714: */
1715: public Node RDBStringToNode(String RDBString)
1716: throws RDFRDBException {
1717: Node res = null;
1718: int len = RDBString.length();
1719: if (len < 3)
1720: throw new RDFRDBException("Bad RDBString Header: "
1721: + RDBString);
1722: String nodeType = RDBString.substring(0, 1);
1723: String valType = RDBString.substring(1, 2);
1724: if ((!(valType.equals(RDBCodeRef) || valType
1725: .equals(RDBCodeValue)))
1726: || (RDBString.charAt(2) != RDBCodeDelimChar))
1727: throw new RDFRDBException("Bad RDBString Header: "
1728: + RDBString);
1729:
1730: int pos = 3;
1731: int npos;
1732:
1733: if (nodeType.equals(RDBCodeURI)) {
1734: ParseInt pi = new ParseInt(pos);
1735: String prefix = "";
1736: RDBStringParseInt(RDBString, pi, false);
1737: if (pi.val != null) {
1738: if (URI_COMPRESS == false)
1739: throw new RDFRDBException(
1740: "Bad URI: Prefix Compression Disabled: "
1741: + RDBString);
1742: prefix = IDtoPrefix(pi.val.intValue());
1743: if (prefix == null)
1744: throw new RDFRDBException("Bad URI Prefix: "
1745: + RDBString);
1746: }
1747: pos = pi.pos + 1;
1748: String qname;
1749: if (valType.equals(RDBCodeRef)) {
1750: qname = IDtoURI(RDBString.substring(pos));
1751: if (qname == null)
1752: throw new RDFRDBException("Bad URI: " + RDBString);
1753: } else
1754: qname = RDBString.substring(pos, len - EOS_LEN);
1755:
1756: res = Node.createURI(prefix + qname);
1757:
1758: } else if (nodeType.equals(RDBCodeLiteral)) {
1759: res = RDBLiteralStringToLiteralNode(RDBString, len,
1760: valType, pos);
1761:
1762: } else if (nodeType.equals(RDBCodeBlank)) {
1763: String bstr = null;
1764: if (valType.equals(RDBCodeValue)) {
1765: bstr = RDBString.substring(4, len - EOS_LEN);
1766: } else {
1767: bstr = IDtoBlank(RDBString.substring(4));
1768: if (bstr == null)
1769: throw new RDFRDBException("Bad URI: " + RDBString);
1770: }
1771: res = Node.createAnon(new AnonId(bstr));
1772:
1773: } else if (nodeType.equals(RDBCodeVariable)) {
1774: String vname = RDBString.substring(3, len - EOS_LEN);
1775: res = Node.createVariable(vname);
1776:
1777: } else if (nodeType.equals(RDBCodeANY)) {
1778: res = Node.ANY;
1779:
1780: } else
1781: throw new RDFRDBException("Invalid RDBString Prefix, "
1782: + RDBString);
1783: return res;
1784: }
1785:
1786: /**
1787: Answer a literal Node constructed according to the RDB String.
1788:
1789: @param RDBString
1790: @param len
1791: @param valType
1792: @param pos
1793: @return
1794: */
1795: protected Node RDBLiteralStringToLiteralNode(String RDBString,
1796: int len, String valType, int pos) {
1797: ParseInt pi = new ParseInt(pos);
1798: String litString = null;
1799: if (valType.equals(RDBCodeRef)) {
1800: RDBStringParseInt(RDBString, pi, true);
1801: if (pi.val != null)
1802: litString = IDtoLiteral(pi.val.intValue());
1803: if (litString == null)
1804: throw new RDFRDBException("Bad Literal Reference: "
1805: + RDBString);
1806: } else
1807: litString = RDBString.substring(pos, len - EOS_LEN);
1808: len = litString.length();
1809: pi.pos = 0;
1810: RDBStringParseInt(litString, pi, false);
1811: int langLen = pi.val == null ? 0 : pi.val.intValue();
1812: pi.pos = pi.pos + 1;
1813: RDBStringParseInt(litString, pi, false);
1814: int dtypeLen = pi.val == null ? 0 : pi.val.intValue();
1815: pos = pi.pos + 1;
1816: if ((pos + langLen + dtypeLen) > len)
1817: throw new RDFRDBException("Malformed Literal: " + litString);
1818: String lang = litString.substring(pos, pos + langLen);
1819: pos = pos + langLen;
1820: String dtype = litString.substring(pos, pos + dtypeLen);
1821: String val = litString.substring(pos + dtypeLen);
1822: return createLiteral(val, lang, dtype);
1823: }
1824:
1825: /**
1826: Answer a Node literal with the indicated lexical form, language,
1827: and datatype. If the datatype is the empty string, there is no
1828: datatype. If the language is the empty string, there is no language.
1829:
1830: @param val
1831: @param lang
1832: @param dtype
1833: @return
1834: */
1835: protected Node createLiteral(String val, String lang, String dtype) {
1836: if (dtype.equals("")) {
1837: return Node.createLiteral(val, lang, null);
1838: } else {
1839: RDFDatatype dt = TypeMapper.getInstance()
1840: .getSafeTypeByName(dtype);
1841: return Node.createLiteral(val, lang, dt);
1842: }
1843: }
1844:
1845: /** This is cuurently a copy of Util.splitNamespace. It was
1846: * copied rather than used directly for two reasons. 1) in the
1847: * future it may be desirable to use a different split algorithm
1848: * for persistence. 2) the util version could change at any time,
1849: * which would render existing databases inaccessible. having a
1850: * copy allows the db version to evolve in a controlled way.
1851: *
1852: * Given an absolute URI, determine the split point between the namespace part
1853: * and the localname part.
1854: * If there is no valid localname part then the length of the
1855: * string is returned.
1856: * The algorithm tries to find the longest NCName at the end
1857: * of the uri, not immediately preceeded by the first colon
1858: * in the string.
1859: * @param uri
1860: * @return the index of the first character of the localname
1861: */
1862: public static int dbSplitNamespace(String uri) {
1863: char ch;
1864: int lg = uri.length();
1865: if (lg == 0)
1866: return 0;
1867: int j;
1868: int i;
1869: for (i = lg - 1; i >= 1; i--) {
1870: ch = uri.charAt(i);
1871: if (!XMLChar.isNCName(ch))
1872: break;
1873: }
1874: for (j = i + 1; j < lg; j++) {
1875: ch = uri.charAt(j);
1876: if (XMLChar.isNCNameStart(ch)) {
1877: if (uri.charAt(j - 1) == ':'
1878: && uri.lastIndexOf(':', j - 2) == -1)
1879: continue; // split "mailto:me" as "mailto:m" and "e" !
1880: else
1881: break;
1882: }
1883: }
1884: return j;
1885: }
1886:
1887: class ParseInt {
1888: int pos;
1889: Integer val;
1890:
1891: ParseInt(int p) {
1892: pos = p;
1893: }
1894: }
1895:
1896: protected void RDBStringParseInt(String RDBString, ParseInt pi,
1897: boolean toEnd) {
1898: int npos = toEnd ? RDBString.length() : RDBString.indexOf(
1899: RDBCodeDelimChar, pi.pos);
1900: if (npos < 0) {
1901: throw new RDFRDBException("Bad RDB String: " + RDBString);
1902: }
1903: String intStr = RDBString.substring(pi.pos, npos);
1904: pi.pos = npos;
1905: if (intStr.equals(""))
1906: pi.val = null;
1907: else
1908: try {
1909: pi.val = new Integer(intStr);
1910: } catch (NumberFormatException e1) {
1911: throw new RDFRDBException("Bad RDB String: "
1912: + RDBString);
1913: }
1914: return;
1915: }
1916:
1917: DBIDInt URItoPrefix(String uri, int pos, boolean add) {
1918: DBIDInt res;
1919: Object key = prefixCache.getByValue(uri.substring(0, pos));
1920: if (key == null) {
1921: RDBLongObject lobj = PrefixToLongObject(uri, pos);
1922: res = getLongObjectID(lobj, PREFIX_TABLE, add);
1923: if (res != null)
1924: prefixCache.put(res, uri.substring(0, pos));
1925: } else
1926: res = (DBIDInt) key;
1927: return res;
1928: }
1929:
1930: protected RDBLongObject PrefixToLongObject(String prefix, int split) {
1931: RDBLongObject res = new RDBLongObject();
1932: int headLen;
1933: int avail;
1934:
1935: res.head = RDBCodePrefix + RDBCodeValue + RDBCodeDelim;
1936: headLen = res.head.length();
1937: avail = INDEX_KEY_LENGTH - (headLen + EOS_LEN);
1938: if (split > avail) {
1939: res.head = res.head + prefix.substring(0, avail);
1940: res.tail = prefix.substring(avail, split);
1941: res.hash = stringToHash(res.tail);
1942: } else {
1943: res.head = res.head + prefix.substring(0, split);
1944: res.tail = "";
1945: }
1946: res.head = res.head + EOS;
1947: return res;
1948: }
1949:
1950: /**
1951: * Encode a literal node's lang and datatype as a string of the
1952: * form ":[langLen]:[datatypeLen]:[langString][dataTypeString]"
1953: * @return the string.
1954: */
1955: public String litLangTypeToRDBString(String lang, String dtype)
1956: throws RDFRDBException {
1957: String res = RDBCodeDelim;
1958: res = ((lang == null) ? "" : Integer.toString(lang.length()))
1959: + RDBCodeDelim;
1960: res = res
1961: + ((dtype == null) ? "" : Integer.toString(dtype
1962: .length())) + RDBCodeDelim;
1963: res = res + (lang == null ? "" : lang)
1964: + (dtype == null ? "" : dtype);
1965: return res;
1966: }
1967:
1968: /**
1969: * Check if an object is long, i.e., it exceeds the length
1970: * limit for storing in a statement table.
1971: * @return true if literal is long, else false.
1972: */
1973: protected boolean objectIsLong(int encodingLen, String objAsString) {
1974: return ((encodingLen + objAsString.length()) > LONG_OBJECT_LENGTH);
1975: }
1976:
1977: class RDBLongObject {
1978: String head; /* prefix of long object that can be indexed */
1979: long hash; /* hash encoding of tail */
1980: String tail; /* remainder of long object */
1981: }
1982:
1983: protected RDBLongObject literalToLongObject(Node_Literal node) {
1984: RDBLongObject res = new RDBLongObject();
1985: int headLen;
1986: int avail;
1987: String lang = node.getLiteralLanguage();
1988: String dtype = node.getLiteralDatatypeURI();
1989: String val = node.getLiteralLexicalForm();
1990: String langType = litLangTypeToRDBString(lang, dtype);
1991:
1992: res.head = RDBCodeLiteral + RDBCodeValue + RDBCodeDelim
1993: + langType;
1994: headLen = res.head.length();
1995: avail = INDEX_KEY_LENGTH - (headLen + EOS_LEN);
1996: if (val.length() > avail) {
1997: res.head = res.head + val.substring(0, avail);
1998: res.tail = val.substring(avail);
1999: res.hash = stringToHash(res.tail);
2000: } else {
2001: res.head = res.head + val;
2002: res.tail = "";
2003: }
2004: res.head = res.head + EOS;
2005: return res;
2006: }
2007:
2008: protected long stringToHash(String str) {
2009: CRC32 checksum = new CRC32();
2010: checksum.update(str.getBytes());
2011: return checksum.getValue();
2012: }
2013:
2014: /**
2015: * Return the database ID for the URI, if it exists
2016: */
2017: public DBIDInt getBlankID(String bstr, boolean add)
2018: throws RDFRDBException {
2019: RDBLongObject lobj = URIToLongObject(bstr, RDBCodeBlank);
2020: return getLongObjectID(lobj, LONG_URI_TABLE, add);
2021: }
2022:
2023: /**
2024: * Return the database ID for the URI, if it exists
2025: */
2026: public DBIDInt getURIID(String qname, boolean add)
2027: throws RDFRDBException {
2028: RDBLongObject lobj = URIToLongObject(qname, RDBCodeURI);
2029: return getLongObjectID(lobj, LONG_URI_TABLE, add);
2030: }
2031:
2032: protected RDBLongObject URIToLongObject(String qname, String code) {
2033: RDBLongObject res = new RDBLongObject();
2034: int headLen;
2035: int avail;
2036:
2037: res.head = code + RDBCodeValue + RDBCodeDelim;
2038: headLen = res.head.length();
2039: avail = INDEX_KEY_LENGTH - (headLen + EOS_LEN);
2040: if (qname.length() > avail) {
2041: res.head = res.head + qname.substring(0, avail);
2042: res.tail = qname.substring(avail);
2043: res.hash = stringToHash(res.tail);
2044: } else {
2045: res.head = res.head + qname;
2046: res.tail = "";
2047: }
2048: res.head = res.head + EOS;
2049: return res;
2050: }
2051:
2052: /**
2053: * Return the database ID for the literal, if it exists
2054: */
2055: public DBIDInt getLiteralID(Node_Literal lnode, boolean add)
2056: throws RDFRDBException {
2057: RDBLongObject lobj = literalToLongObject(lnode);
2058: return getLongObjectID(lobj, LONG_LIT_TABLE, add);
2059: }
2060:
2061: public DBIDInt getLongObjectID(RDBLongObject lobj, String table,
2062: boolean add) throws RDFRDBException {
2063: ResultSet rs = null;
2064: PreparedStatement ps = null;
2065: try {
2066: String opName = "getLongObjectID";
2067: if (lobj.tail.length() > 0)
2068: opName += "withChkSum";
2069: ps = m_sql.getPreparedSQLStatement(opName, table);
2070: ps.setString(1, lobj.head);
2071: if (lobj.tail.length() > 0)
2072: ps.setLong(2, lobj.hash);
2073:
2074: rs = ps.executeQuery();
2075: DBIDInt result = null;
2076: if (rs.next()) {
2077: result = wrapDBID(rs.getObject(1));
2078: } else {
2079: if (add)
2080: result = addRDBLongObject(lobj, table);
2081: }
2082:
2083: return result;
2084: } catch (SQLException e1) {
2085: // /* DEBUG */ System.out.println("Literal truncation (" + l.toString().length() + ") " + l.toString().substring(0, 150));
2086: throw new RDFRDBException("Failed to find literal", e1);
2087: } finally {
2088: if (rs != null)
2089: try {
2090: rs.close();
2091: } catch (SQLException e1) {
2092: throw new RDFRDBException(
2093: "Failed to get last inserted ID: " + e1);
2094: }
2095: if (ps != null)
2096: m_sql.returnPreparedSQLStatement(ps);
2097: }
2098: }
2099:
2100: /**
2101: * Insert a long object into the database.
2102: * This assumes the object is not already in the database.
2103: * @return the db index of the added literal
2104: */
2105: public DBIDInt addRDBLongObject(RDBLongObject lobj, String table)
2106: throws RDFRDBException {
2107: PreparedStatement ps = null;
2108:
2109: // // Because the long object bound has been reset to less than the actual table allocation.
2110: // if ( lobj.tail == null || lobj.tail.equals("") )
2111: // System.err.println("Unexpected : empty tail") ;
2112:
2113: try {
2114: int argi = 1;
2115: String opname = "insertLongObject";
2116: // If not pre-allocated , 1-Head / 2-Hash / 3-Tail
2117: // If pre-allocated , 1-Id, / 2-Head / 3-Hash [/ 4-Tail]
2118: ps = m_sql.getPreparedSQLStatement(opname, table);
2119: int dbid = 0; // init only needed to satisfy java compiler
2120: if (PRE_ALLOCATE_ID) {
2121: dbid = getInsertID(table);
2122: ps.setInt(argi++, dbid);
2123: }
2124: ps.setString(argi++, lobj.head);
2125:
2126: // Do the tail - this can be a large text-holding column, or a binary column
2127: // depending on the database.
2128:
2129: setLongObjectHashAndTail(ps, argi, lobj);
2130: argi += 2; // Hash and tail.
2131:
2132: ps.executeUpdate();
2133: if (!PRE_ALLOCATE_ID)
2134: dbid = getInsertID(table);
2135: return wrapDBID(new Integer(dbid));
2136:
2137: } catch (Exception e1) {
2138: /* DEBUG */System.out.println("Problem on long object (l="
2139: + lobj.head + ") " + e1);
2140: // System.out.println("ID is: " + id);
2141: throw new RDFRDBException("Failed to add long object ", e1);
2142: } finally {
2143: if (ps != null)
2144: m_sql.returnPreparedSQLStatement(ps);
2145: }
2146: }
2147:
2148: // Common way to get the sequence ID, even though the SQL statements are quite different.
2149: // MySQL is different (it overrides this in Driver_MySQL).
2150:
2151: public int getInsertID(String tableName) {
2152: DBIDInt result = null;
2153: PreparedStatement ps = null;
2154: try {
2155: ps = m_sql
2156: .getPreparedSQLStatement("getInsertID", tableName);
2157: ResultSet rs = ps.executeQuery();
2158: if (rs.next()) {
2159: result = wrapDBID(rs.getObject(1));
2160: } else
2161: throw new RDFRDBException("No insert ID");
2162: } catch (SQLException e) {
2163: throw new RDFRDBException("Failed to insert ID: " + e);
2164: } finally {
2165: if (ps != null)
2166: m_sql.returnPreparedSQLStatement(ps);
2167: }
2168: return result.getIntID();
2169: }
2170:
2171: // Different ways of inserting the tail value
2172: // 1/ As a text field, using .setString and letting JDBC encode the characters
2173: // 2/ As a binary field, using a BLOB of UTF-8 encoded bytes
2174:
2175: protected void setLongObjectHashAndTail(PreparedStatement ps,
2176: int argi, RDBLongObject lobj) throws SQLException {
2177: setLongObjectHashAndTail_Text(ps, argi, lobj);
2178: }
2179:
2180: protected void setLongObjectHashAndTail_Text(PreparedStatement ps,
2181: int argi, RDBLongObject lobj) throws SQLException {
2182: if (lobj.tail.length() > 0) {
2183: ps.setLong(argi++, lobj.hash);
2184: ps.setString(argi++, lobj.tail);
2185: } else {
2186: ps.setNull(argi++, java.sql.Types.BIGINT);
2187: ps.setNull(argi++, java.sql.Types.VARCHAR);
2188: }
2189:
2190: }
2191:
2192: protected void setLongObjectHashAndTail_Binary(
2193: PreparedStatement ps, int argi, RDBLongObject lobj)
2194: throws SQLException {
2195: if (lobj.tail.length() > 0)
2196: ps.setLong(argi++, lobj.hash);
2197: else
2198: ps.setNull(argi++, java.sql.Types.BIGINT);
2199:
2200: byte[] b = null;
2201: try {
2202: b = lobj.tail.getBytes("UTF-8");
2203: } catch (UnsupportedEncodingException ex) {
2204: // Can't happen - UTF-8 is required by Java.
2205: throw new RDFRDBException(
2206: "No UTF-8 encoding (setLongObjectHashAndTail_Binary)");
2207: }
2208: //System.out.println("bytes in : "+b.length) ;
2209: ps.setBytes(argi++, b);
2210: }
2211:
2212: /**
2213: * Return the prefix string that has the given prefix id.
2214: * @param prefixID - the dbid of the prefix.
2215: * @return the prefix string or null if it does not exist.
2216: */
2217: protected String IDtoPrefix(int prefixID) {
2218: // check cache
2219: DBIDInt dbid = new DBIDInt(prefixID);
2220: String res = (String) prefixCache.get(dbid);
2221: if (res != null)
2222: return res;
2223: else {
2224: res = IDtoString(prefixID, PREFIX_TABLE, RDBCodePrefix);
2225: prefixCache.put(dbid, res);
2226: return res;
2227: }
2228: }
2229:
2230: /**
2231: * Return the Blank node string that has the given database id.
2232: * @param bnID - the dbid of the blank node, as a string.
2233: * @return the Blank node string or null if it does not exist.
2234: */
2235: protected String IDtoBlank(String bnID) {
2236: return IDtoString(bnID, LONG_URI_TABLE, RDBCodeBlank);
2237: }
2238:
2239: /**
2240: * Return the URI string that has the given database id.
2241: * @param uriID - the dbid of the uri, as a string.
2242: * @return the uri string or null if it does not exist.
2243: */
2244: protected String IDtoURI(String uriID) {
2245: return IDtoString(uriID, LONG_URI_TABLE, RDBCodeURI);
2246: }
2247:
2248: /**
2249: * Return the long literal string that has the given database id.
2250: * @param litID - the dbid of the literal..
2251: * @return the long literal string or null if it does not exist.
2252: */
2253: protected String IDtoLiteral(int litID) {
2254: return IDtoString(litID, LONG_LIT_TABLE, RDBCodeLiteral);
2255: }
2256:
2257: protected String IDtoString(String dbidAsString, String table,
2258: String RDBcode) {
2259: int dbID;
2260: String res = null;
2261: try {
2262: dbID = Integer.parseInt(dbidAsString);
2263: } catch (NumberFormatException e1) {
2264: throw new RDFRDBException("Invalid Object ID: "
2265: + dbidAsString);
2266: }
2267: return IDtoString(dbID, table, RDBcode);
2268: }
2269:
2270: protected String IDtoString(int dbID, String table, String RDBcode) {
2271: String res = null;
2272: RDBLongObject lobj = IDtoLongObject(dbID, table);
2273: if (lobj == null)
2274: throw new RDFRDBException("Invalid Object ID: " + dbID);
2275: // debug check
2276: if (!lobj.head.substring(0, 3).equals(
2277: RDBcode + RDBCodeValue + RDBCodeDelim))
2278: throw new RDFRDBException("Malformed URI in Database: "
2279: + lobj.head);
2280: res = lobj.head.substring(3, lobj.head.length() - EOS_LEN);
2281: if (lobj.tail != null)
2282: res = res + lobj.tail;
2283: return res;
2284: }
2285:
2286: // Get whatever is strong under a long id.
2287: protected RDBLongObject IDtoLongObject(int dbid, String table) {
2288: RDBLongObject res = null;
2289: ResultSet rs = null;
2290: PreparedStatement ps = null;
2291: try {
2292: String opName = "getLongObject";
2293: ps = m_sql.getPreparedSQLStatement(opName, table);
2294: ps.setInt(1, dbid);
2295: rs = ps.executeQuery();
2296: if (rs.next()) {
2297: res = new RDBLongObject();
2298: res.head = rs.getString(1);
2299:
2300: switch (rs.getMetaData().getColumnType(2)) {
2301: case Types.VARCHAR:
2302: case Types.LONGVARCHAR:
2303: case Types.CHAR:
2304: res.tail = rs.getString(2);
2305: if (res.tail == null)
2306: res.tail = "";
2307: break;
2308: case Types.BLOB:
2309: case Types.LONGVARBINARY:
2310: byte[] b2 = rs.getBytes(2);
2311: if (b2 == null)
2312: // The meaning of "" is mixed in SQL.
2313: // Should not happen - we never store empty strings it the tail
2314: res.tail = "";
2315: else
2316: try {
2317: res.tail = new String(b2, 0, b2.length,
2318: "UTF-8");
2319: } catch (UnsupportedEncodingException ex) {
2320: ex.printStackTrace();
2321: }
2322: break;
2323: default:
2324: logger
2325: .fatal("Long object is of unexpected SQL type: "
2326: + rs.getMetaData().getColumnType(2));
2327: throw new RDFRDBException(
2328: "Long object is of unexpected SQL type: "
2329: + rs.getMetaData().getColumnType(2));
2330: }
2331: }
2332: } catch (SQLException e1) {
2333: // /* DEBUG */ System.out.println("Literal truncation (" + l.toString().length() + ") " + l.toString().substring(0, 150));
2334: throw new RDFRDBException("Failed to find literal", e1);
2335: } finally {
2336: if (rs != null)
2337: try {
2338: rs.close();
2339: } catch (SQLException e1) {
2340: throw new RDFRDBException(
2341: "Failed to get last inserted ID: " + e1);
2342: }
2343: if (ps != null)
2344: m_sql.returnPreparedSQLStatement(ps);
2345: }
2346: return res;
2347: }
2348:
2349: protected RDBLongObject IDtoLongObject(String idAsString,
2350: String table) {
2351: RDBLongObject res = null;
2352: int dbid;
2353: try {
2354: dbid = Integer.parseInt(idAsString);
2355: } catch (NumberFormatException e1) {
2356: throw new RDFRDBException("Invalid Object ID: "
2357: + idAsString);
2358: }
2359: return IDtoLongObject(dbid, table);
2360: }
2361:
2362: /**
2363: * Convert the raw SQL object used to store a database identifier into a java object
2364: * which meets the DBIDInt interface.
2365: */
2366: public DBIDInt wrapDBID(Object id) throws RDFRDBException {
2367: if (id instanceof Number) {
2368: return new DBIDInt(((Number) id).intValue());
2369: } else if (id == null) {
2370: return null;
2371: } else {
2372: throw new RDFRDBException("Unexpected DB identifier type: "
2373: + id);
2374: //return null;
2375: }
2376: }
2377:
2378: public String genSQLReifQualStmt() {
2379: return "stmt = ?";
2380: }
2381:
2382: public String genSQLReifQualAnyObj(boolean objIsStmt) {
2383: return "( subj = ? OR prop = ? OR obj = ?"
2384: + (objIsStmt ? " OR hasType = " + QUOTE_CHAR + "T"
2385: + QUOTE_CHAR + " )" : " )");
2386: }
2387:
2388: public String genSQLReifQualObj(char reifProp, boolean hasObj) {
2389: String qual = "";
2390: if (reifProp == 'T') {
2391: qual = "hasType = " + QUOTE_CHAR + "T" + QUOTE_CHAR;
2392: } else {
2393: String cmp = (hasObj ? " = ?" : " is not null");
2394: String col = null;
2395: if (reifProp == 'S')
2396: col = "subj";
2397: else if (reifProp == 'P')
2398: col = "prop";
2399: else if (reifProp == 'O')
2400: col = "obj";
2401: else
2402: throw new JenaException(
2403: "Undefined reification property");
2404:
2405: qual = col + cmp;
2406: }
2407: return qual;
2408: }
2409:
2410: protected String colidToColname(char colid) {
2411: if (colid == 'G')
2412: return "GraphID";
2413: if (colid == 'P')
2414: return "Prop";
2415: if (colid == 'S')
2416: return "Subj";
2417: if (colid == 'O')
2418: return "Obj";
2419: if (colid == 'N')
2420: return "Stmt";
2421: if (colid == 'T')
2422: return "HasType";
2423: throw new JenaException("Invalid column identifer: '" + colid
2424: + "\'");
2425: }
2426:
2427: protected String aliasToString(int alias) {
2428: return "A" + alias;
2429: }
2430:
2431: protected String colAliasToString(int alias, char colid) {
2432: return aliasToString(alias) + "." + colidToColname(colid);
2433: }
2434:
2435: /** Apply SQL escapes to a string */
2436: private String escapeQuoteSQLString(String str) {
2437: StringBuffer sBuff = new StringBuffer(str.length() + 10);
2438: sBuff.append(QUOTE_CHAR);
2439: for (int i = 0; i < str.length(); i++) {
2440: char ch = str.charAt(i);
2441: // Double up quotes
2442: if (ch == QUOTE_CHAR)
2443: sBuff.append(QUOTE_CHAR);
2444: sBuff.append(ch);
2445: }
2446: sBuff.append(QUOTE_CHAR);
2447: return sBuff.toString();
2448: }
2449:
2450: /*
2451: * there's a bug in the code below in that the literal is converted to
2452: * a string BEFORE the query is run. consequently, there's a race
2453: * condition. if the (long) literal is not in the database
2454: * when the query is compiled but is added prior to running the
2455: * query, then the query will (incorrectly) return no results.
2456: * for now, we'll ignore this case and document it as a bug.
2457: */
2458:
2459: public String genSQLQualConst(int alias, char pred, Node lit) {
2460: String val = nodeToRDBString(lit, false);
2461: if (val == null)
2462: // constant not in database.
2463: // should really optimize this and not
2464: // even run the query but ok for now.
2465: val = RDBCodeInvalid;
2466: String qval = escapeQuoteSQLString(val);
2467: return colAliasToString(alias, pred) + "=" + qval;
2468: }
2469:
2470: public String genSQLReifQualConst(int alias, char pred, Node lit) {
2471: String val = "";
2472: if ((pred == 'T') && (lit.equals(RDF.Nodes.Statement)))
2473: val = "T";
2474: else
2475: val = nodeToRDBString(lit, false);
2476: String qval = escapeQuoteSQLString(val);
2477: return colAliasToString(alias, pred) + "=" + qval;
2478: }
2479:
2480: public String genSQLQualParam(int alias, char pred) {
2481: return colAliasToString(alias, pred) + "=?";
2482: }
2483:
2484: public String genSQLQualGraphId(int alias, int graphId) {
2485: return colAliasToString(alias, 'G') + "=" + graphId;
2486: }
2487:
2488: public String genSQLJoin(int lhsAlias, char lhsCol, int rhsAlias,
2489: char rhsCol) {
2490: return colAliasToString(lhsAlias, lhsCol) + "="
2491: + colAliasToString(rhsAlias, rhsCol);
2492: }
2493:
2494: public String genSQLStringMatch(int alias, char col, String fun,
2495: String stringToMatch) {
2496: boolean ignCase = fun
2497: .equals(ExpressionFunctionURIs.J_startsWithInsensitive)
2498: || fun
2499: .equals(ExpressionFunctionURIs.J_endsWithInsensitive)
2500: || fun
2501: .equals(ExpressionFunctionURIs.J_containsInsensitive);
2502: boolean pfxMatch = fun
2503: .equals(ExpressionFunctionURIs.J_startsWith)
2504: || fun
2505: .equals(ExpressionFunctionURIs.J_startsWithInsensitive);
2506: String var = colAliasToString(alias, col);
2507: // generate string match operation for short literal or URI
2508: String qual = " ( " + genSQLStringMatchLHS(ignCase, var);
2509: qual += " " + genSQLStringMatchOp(ignCase, fun);
2510: qual += " "
2511: + genSQLStringMatchRHS(ignCase, pfxMatch, stringToMatch);
2512: // now match long URI or Bnode or, if object col, long literal
2513: qual += " " + genSQLOrKW() + genSQLStringMatchLHS(false, var);
2514: qual += " " + genSQLStringMatchOp(false, fun);
2515: qual += " " + genSQLStringMatchLong() + " )";
2516:
2517: return qual;
2518: }
2519:
2520: public String genSQLStringMatchLHS(boolean ignCase, String var) {
2521: return ignCase ? genSQLStringMatchLHS_IC(var) : var;
2522: }
2523:
2524: public String genSQLStringMatchLong() {
2525: return QUOTE_CHAR + stringMatchAnyChar() + stringMatchLongObj()
2526: + stringMatchAllChar() + QUOTE_CHAR;
2527: }
2528:
2529: public String genSQLStringMatchOp(boolean ignCase, String fun) {
2530: return ignCase ? genSQLStringMatchOp_IC(fun)
2531: : genSQLStringMatchOp(fun);
2532: }
2533:
2534: public String stringMatchAllChar() {
2535: return "%";
2536: }
2537:
2538: public String stringMatchAnyChar() {
2539: return "_";
2540: }
2541:
2542: public String stringMatchEscapeChar() {
2543: return "\\\\";
2544: }
2545:
2546: public String stringMatchLongObj() {
2547: return "r";
2548: }
2549:
2550: public String stringMatchShortObj() {
2551: return "v";
2552: }
2553:
2554: public String genSQLStringMatchRHS(boolean ignCase,
2555: boolean pfxMatch, String strToMatch) {
2556: boolean isEscaped = stringMatchNeedsEscape(strToMatch);
2557: if (isEscaped)
2558: strToMatch = addEscape(strToMatch);
2559: // for now, don't optimize for prefix match
2560: /*
2561: strToMatch = pfxMatch ? strToMatch + stringMatchAllChar() :
2562: stringMatchAllChar() + strToMatch;
2563: strToMatch = stringMatchAllChar() + strToMatch;
2564: strToMatch = nodeToRDBString(Node.createLiteral(strToMatch),false);
2565: if ( pfxMatch && STRINGS_TRIMMED )
2566: strToMatch = strToMatch.substring(0,strToMatch.length()-1);
2567: */
2568: strToMatch = stringMatchAnyChar() + stringMatchShortObj()
2569: + stringMatchAllChar() + strToMatch
2570: + stringMatchAllChar();
2571: strToMatch = QUOTE_CHAR + strToMatch + QUOTE_CHAR;
2572: String qual = ignCase ? genSQLStringMatchRHS_IC(strToMatch)
2573: : strToMatch;
2574: if (isEscaped)
2575: qual += genSQLStringMatchEscape();
2576:
2577: return qual;
2578: }
2579:
2580: public String genSQLStringMatchLHS_IC(String var) {
2581: return var;
2582: }
2583:
2584: public String genSQLStringMatchRHS_IC(String strToMatch) {
2585: return strToMatch;
2586: }
2587:
2588: public String genSQLStringMatchOp(String fun) {
2589: return genSQLLikeKW();
2590: }
2591:
2592: public String genSQLStringMatchOp_IC(String fun) {
2593: return genSQLLikeKW();
2594: }
2595:
2596: public boolean stringMatchNeedsEscape(String strToMatch) {
2597: return strToMatch.indexOf('_') >= 0;
2598: }
2599:
2600: public String addEscape(String strToMatch) {
2601: int i = strToMatch.indexOf('_');
2602: return strToMatch.substring(0, i) + stringMatchEscapeChar()
2603: + strToMatch.substring(i);
2604: }
2605:
2606: public String genSQLStringMatchEscape() {
2607: return "";
2608: }
2609:
2610: public String genSQLResList(int resIndex[], VarDesc[] binding) {
2611: String resList = "";
2612: int i, j;
2613: for (i = 0, j = 0; i < binding.length; i++) {
2614: VarDesc b = binding[i];
2615: if (!b.isArgVar()) {
2616: // next result variable
2617: resList += (j > 0 ? ", " : "")
2618: + colAliasToString(b.alias, b.column);
2619: if (j >= resIndex.length)
2620: throw new JenaException("Too many result columns");
2621: resIndex[j++] = b.mapIx;
2622: }
2623: }
2624: return resList;
2625: }
2626:
2627: public String genSQLFromList(int aliasCnt, String table) {
2628: int i;
2629: String resList = "";
2630: for (i = 0; i < aliasCnt; i++) {
2631: resList += (i > 0 ? ", " : "") + table + " "
2632: + aliasToString(i);
2633: }
2634: return resList;
2635:
2636: }
2637:
2638: public String genSQLLikeKW() {
2639: return "Like ";
2640: }
2641:
2642: public String genSQLEscapeKW() {
2643: return "Escape ";
2644: }
2645:
2646: public String genSQLSelectKW() {
2647: return "Select ";
2648: }
2649:
2650: public String genSQLFromKW() {
2651: return "From ";
2652: }
2653:
2654: public String genSQLWhereKW() {
2655: return "Where ";
2656: }
2657:
2658: public String genSQLOrKW() {
2659: return "Or ";
2660: }
2661:
2662: public String genSQLSelectStmt(String res, String from, String qual) {
2663: return genSQLSelectKW() + res + " " + genSQLFromKW() + from
2664: + " " + (qual.length() == 0 ? qual : genSQLWhereKW())
2665: + qual;
2666: }
2667:
2668: protected int getTableCount(int graphId) {
2669: ResultSet alltables = null;
2670: try {
2671: DatabaseMetaData dbmd = m_dbcon.getConnection()
2672: .getMetaData();
2673: String[] tableTypes = { "TABLE" };
2674: int res = 0;
2675: String tblPattern = TABLE_NAME_PREFIX + "g"
2676: + Integer.toString(graphId) + "%";
2677: tblPattern = stringToDBname(tblPattern);
2678: alltables = dbmd.getTables(null, null, tblPattern,
2679: tableTypes);
2680: while (alltables.next()) {
2681: res += 1;
2682: }
2683: return res;
2684: } catch (SQLException e1) {
2685: throw new RDFRDBException("Internal SQL error in driver - "
2686: + e1);
2687: } finally {
2688: if (alltables != null)
2689: try {
2690: alltables.close();
2691: } catch (SQLException e1) {
2692: throw new RDFRDBException(
2693: "Failed to get last inserted ID: " + e1);
2694: }
2695: }
2696: }
2697:
2698: /*
2699: * getters and setters for database options
2700: */
2701:
2702: public int getLongObjectLengthMax() {
2703: return LONG_OBJECT_LENGTH_MAX;
2704: }
2705:
2706: public int getLongObjectLength() {
2707: return LONG_OBJECT_LENGTH;
2708: }
2709:
2710: public void setLongObjectLength(int len) {
2711: checkDbUninitialized();
2712: if (len > LONG_OBJECT_LENGTH_MAX)
2713: throw new JenaException(
2714: "LongObjectLength exceeds maximum value for database ("
2715: + +LONG_OBJECT_LENGTH_MAX + ")");
2716: LONG_OBJECT_LENGTH = len;
2717: }
2718:
2719: public int getIndexKeyLengthMax() {
2720: return INDEX_KEY_LENGTH_MAX;
2721: }
2722:
2723: public int getIndexKeyLength() {
2724: return INDEX_KEY_LENGTH;
2725: }
2726:
2727: public void setIndexKeyLength(int len) {
2728: checkDbUninitialized();
2729: if (len > INDEX_KEY_LENGTH_MAX)
2730: throw new JenaException(
2731: "IndexKeyLength exceeds maximum value for database ("
2732: + INDEX_KEY_LENGTH_MAX + ")");
2733: INDEX_KEY_LENGTH = len;
2734: }
2735:
2736: public boolean getIsTransactionDb() {
2737: return IS_XACT_DB;
2738: }
2739:
2740: public void setIsTransactionDb(boolean bool) {
2741: checkDbUninitialized();
2742: if (bool == false)
2743: throw new JenaException(
2744: "setIsTransactionDb unsupported for this database engine");
2745: }
2746:
2747: public boolean getDoCompressURI() {
2748: return URI_COMPRESS;
2749: }
2750:
2751: public void setDoCompressURI(boolean bool) {
2752: checkDbUninitialized();
2753: URI_COMPRESS = bool;
2754: }
2755:
2756: public int getCompressURILength() {
2757: return URI_COMPRESS_LENGTH;
2758: }
2759:
2760: public void setCompressURILength(int len) {
2761: checkDbUninitialized();
2762: URI_COMPRESS_LENGTH = len;
2763: }
2764:
2765: public boolean getDoDuplicateCheck() {
2766: return !SKIP_DUPLICATE_CHECK;
2767: }
2768:
2769: public void setDoDuplicateCheck(boolean bool) {
2770: SKIP_DUPLICATE_CHECK = !bool;
2771: }
2772:
2773: protected boolean dbIsOpen() {
2774: return (m_sysProperties != null);
2775: }
2776:
2777: protected void checkDbIsOpen() {
2778: if (!dbIsOpen())
2779: throw new JenaException("Database not open");
2780: }
2781:
2782: protected void checkDbUninitialized() {
2783: if (dbIsOpen() || isDBFormatOK())
2784: throw new JenaException(
2785: "Database configuration option cannot be set after database is formatted");
2786: }
2787:
2788: public String getTableNamePrefix() {
2789: return TABLE_NAME_PREFIX;
2790: }
2791:
2792: public void setTableNamePrefix(String prefix) {
2793: if (dbIsOpen())
2794: throw new JenaException(
2795: "Table name prefix must be set before opening or connecting to a model.");
2796: /* sanity check that the new prefix length is not too long.
2797: * we have to add a few characters to the given prefix to
2798: * account for the index names (see the createStatementTable
2799: * template in the etc/<db>.sql files).
2800: */
2801: String sav = TABLE_NAME_PREFIX;
2802: String testpfx = prefix;
2803: int i;
2804: for (i = 0; i < MAXIMUM_INDEX_COLUMNS; i++)
2805: testpfx += "X";
2806: setTableNames(testpfx);
2807: // now see if the table names will be too long with this "prefix".
2808: try {
2809: String s = genTableName(10, 10, true);
2810: s = genTableName(10, 10, false);
2811: } catch (RDFRDBException e) {
2812: setTableNames(sav);
2813: throw new JenaException("New prefix (\"" + prefix
2814: + "\") is too long and will cause table names \n"
2815: + "to exceed maximum length for database ("
2816: + TABLE_NAME_LENGTH_MAX + ").");
2817: }
2818: // all ok. switch to the new prefix.
2819: setTableNames(prefix);
2820: }
2821:
2822: /** generate a table name and verify that it does not
2823: * exceed the maximum length.
2824: */
2825:
2826: protected String genTableName(int graphId, int tblId, boolean isReif) {
2827: String res = stringToDBname(TABLE_NAME_PREFIX
2828: + "g"
2829: + Integer.toString(graphId)
2830: + "t"
2831: + Integer.toString(tblId)
2832: + (isReif ? REIF_TABLE_NAME_SUFFIX
2833: : STMT_TABLE_NAME_SUFFIX));
2834: if (res.length() > TABLE_NAME_LENGTH_MAX)
2835: throw new RDFRDBException("New table name (\"" + res
2836: + "\") exceeds maximum length for database ("
2837: + TABLE_NAME_LENGTH_MAX + ").");
2838: return res;
2839: }
2840:
2841: /** Names of jena system tables.
2842: protected String [] SYSTEM_TABLE_NAME; */
2843:
2844: protected void setTableNames(String prefix) {
2845: TABLE_NAME_PREFIX = stringToDBname(prefix);
2846: int i = 0;
2847: SYSTEM_TABLE_NAME = new String[6];
2848: SYSTEM_TABLE_NAME[i++] = SYSTEM_STMT_TABLE = stringToDBname(TABLE_NAME_PREFIX
2849: + "sys_stmt");
2850: SYSTEM_TABLE_NAME[i++] = LONG_LIT_TABLE = stringToDBname(TABLE_NAME_PREFIX
2851: + "long_lit");
2852: SYSTEM_TABLE_NAME[i++] = LONG_URI_TABLE = stringToDBname(TABLE_NAME_PREFIX
2853: + "long_uri");
2854: SYSTEM_TABLE_NAME[i++] = PREFIX_TABLE = stringToDBname(TABLE_NAME_PREFIX
2855: + "prefix");
2856: SYSTEM_TABLE_NAME[i++] = GRAPH_TABLE = stringToDBname(TABLE_NAME_PREFIX
2857: + "graph");
2858: SYSTEM_TABLE_NAME[i++] = MUTEX_TABLE = stringToDBname(TABLE_NAME_PREFIX
2859: + "mutex");
2860: SYSTEM_TABLE_CNT = i;
2861: }
2862:
2863: /**
2864: * Return the number of system tables.
2865: */
2866:
2867: public int getSystemTableCount() {
2868: return SYSTEM_TABLE_CNT;
2869: }
2870:
2871: /**
2872: * Return the name of a system table
2873: */
2874:
2875: public String getSystemTableName(int i) {
2876: return ((i < 0) || (i >= SYSTEM_TABLE_CNT)) ? null
2877: : SYSTEM_TABLE_NAME[i];
2878: }
2879:
2880: public String getStoreWithModel() {
2881: return STORE_WITH_MODEL;
2882: }
2883:
2884: public void setStoreWithModel(String modelName) {
2885: String name = null;
2886: if ((modelName != null) && !modelName.equals(""))
2887: name = modelName;
2888: STORE_WITH_MODEL = name;
2889: }
2890:
2891: public int getCompressCacheSize() {
2892: checkDbIsOpen();
2893: return prefixCache.getLimit();
2894: }
2895:
2896: public void setCompressCacheSize(int count) {
2897: checkDbIsOpen();
2898: prefixCache.setLimit(count);
2899: }
2900:
2901: }
2902:
2903: /*
2904: * (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
2905: * All rights reserved.
2906: *
2907: * Redistribution and use in source and binary forms, with or without
2908: * modification, are permitted provided that the following conditions
2909: * are met:
2910: * 1. Redistributions of source code must retain the above copyright
2911: * notice, this list of conditions and the following disclaimer.
2912: * 2. Redistributions in binary form must reproduce the above copyright
2913: * notice, this list of conditions and the following disclaimer in the
2914: * documentation and/or other materials provided with the distribution.
2915: * 3. The name of the author may not be used to endorse or promote products
2916: * derived from this software without specific prior written permission.
2917:
2918: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2919: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2920: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2921: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2922: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2923: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2924: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2925: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2926: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2927: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2928: */
|