001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.index;
007:
008: import java.sql.PreparedStatement;
009: import java.sql.ResultSet;
010: import java.sql.SQLException;
011:
012: import org.h2.constant.ErrorCode;
013: import org.h2.engine.Constants;
014: import org.h2.engine.Session;
015: import org.h2.message.Message;
016: import org.h2.result.Row;
017: import org.h2.result.SearchRow;
018: import org.h2.table.IndexColumn;
019: import org.h2.table.TableLink;
020: import org.h2.value.Value;
021: import org.h2.value.ValueNull;
022:
023: /**
024: * A linked index is a index for a linked (remote) table.
025: * It is backed by an index on the remote table which is accessed over JDBC.
026: */
027: public class LinkedIndex extends BaseIndex {
028:
029: private TableLink link;
030: private String targetTableName;
031:
032: public LinkedIndex(TableLink table, int id, IndexColumn[] columns,
033: IndexType indexType) {
034: super (table, id, null, columns, indexType);
035: link = table;
036: targetTableName = link.getQualifiedTable();
037: }
038:
039: public String getCreateSQL() {
040: return null;
041: }
042:
043: public void close(Session session) throws SQLException {
044: }
045:
046: private boolean isNull(Value v) {
047: return v == null || v == ValueNull.INSTANCE;
048: }
049:
050: public void add(Session session, Row row) throws SQLException {
051: StringBuffer buff = new StringBuffer("INSERT INTO ");
052: buff.append(targetTableName);
053: buff.append(" VALUES(");
054: for (int i = 0, j = 0; i < row.getColumnCount(); i++) {
055: Value v = row.getValue(i);
056: if (j > 0) {
057: buff.append(',');
058: }
059: j++;
060: if (isNull(v)) {
061: buff.append("NULL");
062: } else {
063: buff.append('?');
064: }
065: }
066: buff.append(')');
067: String sql = buff.toString();
068: try {
069: PreparedStatement prep = link.getPreparedStatement(sql);
070: for (int i = 0, j = 0; i < row.getColumnCount(); i++) {
071: Value v = row.getValue(i);
072: if (v != null && v != ValueNull.INSTANCE) {
073: v.set(prep, j + 1);
074: j++;
075: }
076: }
077: prep.executeUpdate();
078: rowCount++;
079: } catch (SQLException e) {
080: throw wrapException(sql, e);
081: }
082: }
083:
084: public Cursor find(Session session, SearchRow first, SearchRow last)
085: throws SQLException {
086: StringBuffer buff = new StringBuffer();
087: for (int i = 0; first != null && i < first.getColumnCount(); i++) {
088: Value v = first.getValue(i);
089: if (v != null) {
090: if (buff.length() != 0) {
091: buff.append(" AND ");
092: }
093: buff.append(table.getColumn(i).getSQL());
094: buff.append(">=?");
095: }
096: }
097: for (int i = 0; last != null && i < last.getColumnCount(); i++) {
098: Value v = last.getValue(i);
099: if (v != null) {
100: if (buff.length() != 0) {
101: buff.append(" AND ");
102: }
103: buff.append(table.getColumn(i).getSQL());
104: buff.append("<=?");
105: }
106: }
107: if (buff.length() > 0) {
108: buff.insert(0, " WHERE ");
109: }
110: buff.insert(0, "SELECT * FROM " + targetTableName + " T");
111: String sql = buff.toString();
112: try {
113: PreparedStatement prep = link.getPreparedStatement(sql);
114: int j = 0;
115: for (int i = 0; first != null && i < first.getColumnCount(); i++) {
116: Value v = first.getValue(i);
117: if (v != null) {
118: v.set(prep, j + 1);
119: j++;
120: }
121: }
122: for (int i = 0; last != null && i < last.getColumnCount(); i++) {
123: Value v = last.getValue(i);
124: if (v != null) {
125: v.set(prep, j + 1);
126: j++;
127: }
128: }
129: ResultSet rs = prep.executeQuery();
130: return new LinkedCursor(table, rs, session);
131: } catch (SQLException e) {
132: throw wrapException(sql, e);
133: }
134: }
135:
136: public int getLookupCost(int rowCount) {
137: for (int i = 0, j = 1;; i++) {
138: j *= 10;
139: if (j > rowCount) {
140: return i + 1;
141: }
142: }
143: }
144:
145: public double getCost(Session session, int[] masks)
146: throws SQLException {
147: return 100 + getCostRangeIndex(masks, rowCount
148: + Constants.COST_ROW_OFFSET);
149: }
150:
151: public void remove(Session session) throws SQLException {
152: }
153:
154: public void truncate(Session session) throws SQLException {
155: }
156:
157: public void checkRename() throws SQLException {
158: throw Message.getUnsupportedException();
159: }
160:
161: public boolean needRebuild() {
162: return false;
163: }
164:
165: public boolean canGetFirstOrLast() {
166: return false;
167: }
168:
169: public SearchRow findFirstOrLast(Session session, boolean first)
170: throws SQLException {
171: // TODO optimization: could get the first or last value (in any case;
172: // maybe not optimized)
173: throw Message.getUnsupportedException();
174: }
175:
176: public void remove(Session session, Row row) throws SQLException {
177: StringBuffer buff = new StringBuffer("DELETE FROM ");
178: buff.append(targetTableName);
179: buff.append(" WHERE ");
180: for (int i = 0; i < row.getColumnCount(); i++) {
181: if (i > 0) {
182: buff.append("AND ");
183: }
184: buff.append(table.getColumn(i).getSQL());
185: Value v = row.getValue(i);
186: if (isNull(v)) {
187: buff.append(" IS NULL ");
188: } else {
189: buff.append("=? ");
190: }
191: }
192: String sql = buff.toString();
193: try {
194: PreparedStatement prep = link.getPreparedStatement(sql);
195: for (int i = 0, j = 0; i < row.getColumnCount(); i++) {
196: Value v = row.getValue(i);
197: if (!isNull(v)) {
198: v.set(prep, j + 1);
199: j++;
200: }
201: }
202: int count = prep.executeUpdate();
203: rowCount -= count;
204: } catch (SQLException e) {
205: throw wrapException(sql, e);
206: }
207: }
208:
209: public void update(Session session, Row oldRow, Row newRow)
210: throws SQLException {
211: StringBuffer buff = new StringBuffer("UPDATE ");
212: buff.append(targetTableName).append(" SET ");
213: for (int i = 0; i < newRow.getColumnCount(); i++) {
214: if (i > 0) {
215: buff.append(", ");
216: }
217: buff.append(table.getColumn(i).getSQL()).append("=?");
218: }
219: buff.append(" WHERE ");
220: for (int i = 0; i < oldRow.getColumnCount(); i++) {
221: if (i > 0) {
222: buff.append("AND ");
223: }
224: buff.append(table.getColumn(i).getSQL());
225: Value v = oldRow.getValue(i);
226: if (isNull(v)) {
227: buff.append(" IS NULL ");
228: } else {
229: buff.append("=? ");
230: }
231: }
232: String sql = buff.toString();
233: try {
234: int j = 1;
235: PreparedStatement prep = link.getPreparedStatement(sql);
236: for (int i = 0; i < newRow.getColumnCount(); i++) {
237: newRow.getValue(i).set(prep, j);
238: j++;
239: }
240: for (int i = 0; i < oldRow.getColumnCount(); i++) {
241: Value v = oldRow.getValue(i);
242: if (!isNull(v)) {
243: v.set(prep, j);
244: j++;
245: }
246: }
247: int count = prep.executeUpdate();
248: // this has no effect but at least it allows to debug the update count
249: rowCount = rowCount + count - count;
250: } catch (SQLException e) {
251: throw wrapException(sql, e);
252: }
253: }
254:
255: private SQLException wrapException(String sql, SQLException e) {
256: return Message.getSQLException(
257: ErrorCode.ERROR_ACCESSING_LINKED_TABLE_2, new String[] {
258: sql, e.toString() }, e);
259: }
260:
261: }
|