001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 2008.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.sail.rdbms.schema;
007:
008: import java.sql.SQLException;
009: import java.util.List;
010: import java.util.Map;
011:
012: /**
013: * Manages the life-cycle of the rows in a single predicate table.
014: *
015: * @author James Leigh
016: *
017: */
018: public class TripleTable {
019: public static int tables_created;
020: public static int total_st;
021: public static final boolean UNIQUE_INDEX_TRIPLES = true;
022: private static final String[] PKEY = { "obj", "subj", "ctx" };
023: private static final String[] SUBJ_INDEX = { "subj" };
024: private static final String[] CTX_INDEX = { "ctx" };
025: private static final String[] PRED_PKEY = { "obj", "subj", "pred",
026: "ctx" };
027: private static final String[] PRED_INDEX = { "pred" };
028: private RdbmsTable table;
029: private ValueTypes objTypes = new ValueTypes();
030: private ValueTypes subjTypes = new ValueTypes();
031: private boolean initialize;
032: private boolean predColumnPresent;
033: private boolean indexed;
034: private IdSequence ids;
035:
036: public TripleTable(RdbmsTable table) {
037: this .table = table;
038: }
039:
040: public void setIdSequence(IdSequence ids) {
041: this .ids = ids;
042: }
043:
044: public boolean isPredColumnPresent() {
045: return predColumnPresent;
046: }
047:
048: public void setPredColumnPresent(boolean present) {
049: predColumnPresent = present;
050: }
051:
052: public void setIndexed(boolean indexingTriples) {
053: indexed = true;
054: }
055:
056: public synchronized void initTable() throws SQLException {
057: if (initialize)
058: return;
059: table.createTransactionalTable(buildTableColumns());
060: tables_created++;
061: total_st++;
062: if (UNIQUE_INDEX_TRIPLES) {
063: if (isPredColumnPresent()) {
064: table.index(PRED_PKEY);
065: total_st++;
066: } else {
067: table.index(PKEY);
068: total_st++;
069: }
070: }
071: if (indexed) {
072: createIndex();
073: }
074: initialize = true;
075: }
076:
077: public void reload() throws SQLException {
078: table.count();
079: if (table.size() > 0) {
080: ValueType[] values = ValueType.values();
081: String[] OBJ_CONTAINS = new String[values.length];
082: String[] SUBJ_CONTAINS = new String[values.length];
083: StringBuilder sb = new StringBuilder();
084: for (int i = 0, n = values.length; i < n; i++) {
085: sb.delete(0, sb.length());
086: ValueType code = values[i];
087: sb.append("MAX(CASE WHEN obj BETWEEN ").append(
088: ids.minId(code));
089: sb.append(" AND ").append(ids.maxId(code));
090: sb.append(" THEN 1 ELSE 0 END)");
091: OBJ_CONTAINS[i] = sb.toString();
092: sb.delete(0, sb.length());
093: sb.append("MAX(CASE WHEN subj BETWEEN ").append(
094: ids.minId(code));
095: sb.append(" AND ").append(ids.maxId(code));
096: sb.append(" THEN 1 ELSE 0 END)");
097: SUBJ_CONTAINS[i] = sb.toString();
098: }
099: int[] aggregate = table.aggregate(OBJ_CONTAINS);
100: for (int i = 0; i < aggregate.length; i++) {
101: if (aggregate[i] == 1) {
102: objTypes.add(values[i]);
103: }
104: }
105: aggregate = table.aggregate(SUBJ_CONTAINS);
106: for (int i = 0; i < aggregate.length; i++) {
107: if (aggregate[i] == 1) {
108: subjTypes.add(values[i]);
109: }
110: }
111:
112: }
113: initialize = true;
114: }
115:
116: public void close() throws SQLException {
117: table.close();
118: }
119:
120: public boolean isIndexed() throws SQLException {
121: return table.getIndexes().size() > 1;
122: }
123:
124: public void createIndex() throws SQLException {
125: if (isPredColumnPresent()) {
126: table.index(PRED_INDEX);
127: total_st++;
128: }
129: table.index(SUBJ_INDEX);
130: total_st++;
131: table.index(CTX_INDEX);
132: total_st++;
133: }
134:
135: public void dropIndex() throws SQLException {
136: for (Map.Entry<String, List<String>> e : table.getIndexes()
137: .entrySet()) {
138: if (!e.getValue().contains("OBJ")
139: && !e.getValue().contains("obj")) {
140: table.dropIndex(e.getKey());
141: }
142: }
143: }
144:
145: public boolean isReady() {
146: return initialize;
147: }
148:
149: public void blockUntilReady() throws SQLException {
150: if (initialize)
151: return;
152: initTable();
153: }
154:
155: public String getName() throws SQLException {
156: return table.getName();
157: }
158:
159: public String getNameWhenReady() throws SQLException {
160: blockUntilReady();
161: return table.getName();
162: }
163:
164: public ValueTypes getObjTypes() {
165: return objTypes;
166: }
167:
168: public void setObjTypes(ValueTypes valueTypes) {
169: this .objTypes.merge(valueTypes);
170: }
171:
172: public ValueTypes getSubjTypes() {
173: return subjTypes;
174: }
175:
176: public void setSubjTypes(ValueTypes valueTypes) {
177: this .subjTypes.merge(valueTypes);
178: }
179:
180: public void modified(int addedCount, int removedCount)
181: throws SQLException {
182: blockUntilReady();
183: table.modified(addedCount, removedCount);
184: table.optimize();
185: if (isEmpty()) {
186: objTypes.reset();
187: subjTypes.reset();
188: }
189: }
190:
191: public boolean isEmpty() throws SQLException {
192: blockUntilReady();
193: return table.size() == 0;
194: }
195:
196: @Override
197: public String toString() {
198: return table.getName();
199: }
200:
201: public void drop() throws SQLException {
202: blockUntilReady();
203: table.drop();
204: }
205:
206: protected CharSequence buildTableColumns() {
207: StringBuilder sb = new StringBuilder();
208: sb.append(" ctx ").append(ids.getSqlType()).append(
209: " NOT NULL,\n");
210: sb.append(" subj ").append(ids.getSqlType()).append(
211: " NOT NULL,\n");
212: if (isPredColumnPresent()) {
213: sb.append(" pred ").append(ids.getSqlType()).append(
214: " NOT NULL,\n");
215: }
216: sb.append(" obj ").append(ids.getSqlType()).append(
217: " NOT NULL\n");
218: return sb;
219: }
220: }
|