EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.table]

COVERAGE SUMMARY FOR SOURCE FILE [TableLink.java]

nameclass, %method, %block, %line, %
TableLink.java100% (1/1)79%  (34/43)89%  (1162/1311)88%  (287.8/328)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class TableLink100% (1/1)79%  (34/43)89%  (1162/1311)88%  (287.8/328)
addIndex (Session, String, int, IndexColumn [], IndexType, boolean, String): ... 0%   (0/1)0%   (0/3)0%   (0/1)
checkSupportAlter (): void 0%   (0/1)0%   (0/3)0%   (0/1)
getDiskSpaceUsed (): long 0%   (0/1)0%   (0/2)0%   (0/1)
getDropSQL (): String 0%   (0/1)0%   (0/10)0%   (0/1)
getUniqueIndex (): Index 0%   (0/1)0%   (0/20)0%   (0/5)
isLockedExclusively (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
isOracle (): boolean 0%   (0/1)0%   (0/5)0%   (0/1)
truncate (Session): void 0%   (0/1)0%   (0/3)0%   (0/1)
unlock (Session): void 0%   (0/1)0%   (0/1)0%   (0/1)
convertColumnName (String): String 100% (1/1)39%  (14/36)36%  (2.5/7)
TableLink (Schema, int, String, String, String, String, String, String, Strin... 100% (1/1)65%  (43/66)80%  (16/20)
close (Session): void 100% (1/1)67%  (12/18)91%  (4.6/5)
convertScale (int, int): int 100% (1/1)75%  (6/8)75%  (3/4)
reusePreparedStatement (PreparedStatement, String): void 100% (1/1)75%  (15/20)93%  (3.7/4)
getRowCount (Session): long 100% (1/1)87%  (34/39)80%  (8/10)
validateConvertUpdateSequence (Session, Row): void 100% (1/1)88%  (29/33)88%  (7/8)
convertPrecision (int, long): long 100% (1/1)88%  (22/25)89%  (8/9)
getCreateSQL (): String 100% (1/1)95%  (92/97)94%  (15/16)
readMetaData (): void 100% (1/1)96%  (509/531)91%  (115/126)
execute (String, ArrayList, boolean): PreparedStatement 100% (1/1)98%  (150/153)97%  (34/35)
addIndex (ArrayList, IndexType): void 100% (1/1)100% (23/23)100% (5/5)
addRow (Session, Row): void 100% (1/1)100% (9/9)100% (3/3)
canDrop (): boolean 100% (1/1)100% (2/2)100% (1/1)
canGetRowCount (): boolean 100% (1/1)100% (2/2)100% (1/1)
checkReadOnly (): void 100% (1/1)100% (7/7)100% (3/3)
checkRename (): void 100% (1/1)100% (1/1)100% (1/1)
checkWritingAllowed (): void 100% (1/1)100% (1/1)100% (1/1)
connect (): void 100% (1/1)100% (55/55)100% (15/15)
getDefaultValue (Session, Column): Value 100% (1/1)100% (2/2)100% (1/1)
getIndexes (): ArrayList 100% (1/1)100% (3/3)100% (1/1)
getMaxDataModificationId (): long 100% (1/1)100% (2/2)100% (1/1)
getQualifiedTable (): String 100% (1/1)100% (3/3)100% (1/1)
getRowCountApproximation (): long 100% (1/1)100% (2/2)100% (1/1)
getScanIndex (Session): Index 100% (1/1)100% (3/3)100% (1/1)
getTableType (): String 100% (1/1)100% (2/2)100% (1/1)
isDeterministic (): boolean 100% (1/1)100% (2/2)100% (1/1)
lock (Session, boolean, boolean): boolean 100% (1/1)100% (2/2)100% (1/1)
removeChildrenAndResources (Session): void 100% (1/1)100% (33/33)100% (8/8)
removeRow (Session, Row): void 100% (1/1)100% (9/9)100% (3/3)
setGlobalTemporary (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setReadOnly (boolean): void 100% (1/1)100% (4/4)100% (2/2)
updateRows (Prepared, Session, RowList): void 100% (1/1)100% (47/47)100% (15/15)
wrapException (String, Exception): DbException 100% (1/1)100% (18/18)100% (2/2)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 */
6package org.h2.table;
7 
8import java.sql.DatabaseMetaData;
9import java.sql.PreparedStatement;
10import java.sql.ResultSet;
11import java.sql.ResultSetMetaData;
12import java.sql.SQLException;
13import java.sql.Statement;
14import java.sql.Types;
15import java.util.ArrayList;
16import java.util.HashMap;
17 
18import org.h2.api.ErrorCode;
19import org.h2.command.Prepared;
20import org.h2.engine.Session;
21import org.h2.engine.UndoLogRecord;
22import org.h2.index.Index;
23import org.h2.index.IndexType;
24import org.h2.index.LinkedIndex;
25import org.h2.jdbc.JdbcSQLException;
26import org.h2.message.DbException;
27import org.h2.result.Row;
28import org.h2.result.RowList;
29import org.h2.schema.Schema;
30import org.h2.util.JdbcUtils;
31import org.h2.util.MathUtils;
32import org.h2.util.New;
33import org.h2.util.StatementBuilder;
34import org.h2.util.StringUtils;
35import org.h2.value.DataType;
36import org.h2.value.Value;
37import org.h2.value.ValueDate;
38import org.h2.value.ValueTime;
39import org.h2.value.ValueTimestamp;
40 
41/**
42 * A linked table contains connection information for a table accessible by
43 * JDBC. The table may be stored in a different database.
44 */
45public class TableLink extends Table {
46 
47    private static final int MAX_RETRY = 2;
48 
49    private static final long ROW_COUNT_APPROXIMATION = 100000;
50 
51    private final String originalSchema;
52    private String driver, url, user, password, originalTable, qualifiedTableName;
53    private TableLinkConnection conn;
54    private HashMap<String, PreparedStatement> preparedMap = New.hashMap();
55    private final ArrayList<Index> indexes = New.arrayList();
56    private final boolean emitUpdates;
57    private LinkedIndex linkedIndex;
58    private DbException connectException;
59    private boolean storesLowerCase;
60    private boolean storesMixedCase;
61    private boolean storesMixedCaseQuoted;
62    private boolean supportsMixedCaseIdentifiers;
63    private boolean globalTemporary;
64    private boolean readOnly;
65 
66    public TableLink(Schema schema, int id, String name, String driver,
67            String url, String user, String password, String originalSchema,
68            String originalTable, boolean emitUpdates, boolean force) {
69        super(schema, id, name, false, true);
70        this.driver = driver;
71        this.url = url;
72        this.user = user;
73        this.password = password;
74        this.originalSchema = originalSchema;
75        this.originalTable = originalTable;
76        this.emitUpdates = emitUpdates;
77        try {
78            connect();
79        } catch (DbException e) {
80            if (!force) {
81                throw e;
82            }
83            Column[] cols = { };
84            setColumns(cols);
85            linkedIndex = new LinkedIndex(this, id, IndexColumn.wrap(cols),
86                    IndexType.createNonUnique(false));
87            indexes.add(linkedIndex);
88        }
89    }
90 
91    private void connect() {
92        connectException = null;
93        for (int retry = 0;; retry++) {
94            try {
95                conn = database.getLinkConnection(driver, url, user, password);
96                synchronized (conn) {
97                    try {
98                        readMetaData();
99                        return;
100                    } catch (Exception e) {
101                        // could be SQLException or RuntimeException
102                        conn.close(true);
103                        conn = null;
104                        throw DbException.convert(e);
105                    }
106                }
107            } catch (DbException e) {
108                if (retry >= MAX_RETRY) {
109                    connectException = e;
110                    throw e;
111                }
112            }
113        }
114    }
115 
116    private void readMetaData() throws SQLException {
117        DatabaseMetaData meta = conn.getConnection().getMetaData();
118        storesLowerCase = meta.storesLowerCaseIdentifiers();
119        storesMixedCase = meta.storesMixedCaseIdentifiers();
120        storesMixedCaseQuoted = meta.storesMixedCaseQuotedIdentifiers();
121        supportsMixedCaseIdentifiers = meta.supportsMixedCaseIdentifiers();
122        ResultSet rs = meta.getTables(null, originalSchema, originalTable, null);
123        if (rs.next() && rs.next()) {
124            throw DbException.get(ErrorCode.SCHEMA_NAME_MUST_MATCH, originalTable);
125        }
126        rs.close();
127        rs = meta.getColumns(null, originalSchema, originalTable, null);
128        int i = 0;
129        ArrayList<Column> columnList = New.arrayList();
130        HashMap<String, Column> columnMap = New.hashMap();
131        String catalog = null, schema = null;
132        while (rs.next()) {
133            String thisCatalog = rs.getString("TABLE_CAT");
134            if (catalog == null) {
135                catalog = thisCatalog;
136            }
137            String thisSchema = rs.getString("TABLE_SCHEM");
138            if (schema == null) {
139                schema = thisSchema;
140            }
141            if (!StringUtils.equals(catalog, thisCatalog) ||
142                    !StringUtils.equals(schema, thisSchema)) {
143                // if the table exists in multiple schemas or tables,
144                // use the alternative solution
145                columnMap.clear();
146                columnList.clear();
147                break;
148            }
149            String n = rs.getString("COLUMN_NAME");
150            n = convertColumnName(n);
151            int sqlType = rs.getInt("DATA_TYPE");
152            long precision = rs.getInt("COLUMN_SIZE");
153            precision = convertPrecision(sqlType, precision);
154            int scale = rs.getInt("DECIMAL_DIGITS");
155            scale = convertScale(sqlType, scale);
156            int displaySize = MathUtils.convertLongToInt(precision);
157            int type = DataType.convertSQLTypeToValueType(sqlType);
158            Column col = new Column(n, type, precision, scale, displaySize);
159            col.setTable(this, i++);
160            columnList.add(col);
161            columnMap.put(n, col);
162        }
163        rs.close();
164        if (originalTable.indexOf('.') < 0 && !StringUtils.isNullOrEmpty(schema)) {
165            qualifiedTableName = schema + "." + originalTable;
166        } else {
167            qualifiedTableName = originalTable;
168        }
169        // check if the table is accessible
170        Statement stat = null;
171        try {
172            stat = conn.getConnection().createStatement();
173            rs = stat.executeQuery("SELECT * FROM " +
174                    qualifiedTableName + " T WHERE 1=0");
175            if (columnList.size() == 0) {
176                // alternative solution
177                ResultSetMetaData rsMeta = rs.getMetaData();
178                for (i = 0; i < rsMeta.getColumnCount();) {
179                    String n = rsMeta.getColumnName(i + 1);
180                    n = convertColumnName(n);
181                    int sqlType = rsMeta.getColumnType(i + 1);
182                    long precision = rsMeta.getPrecision(i + 1);
183                    precision = convertPrecision(sqlType, precision);
184                    int scale = rsMeta.getScale(i + 1);
185                    scale = convertScale(sqlType, scale);
186                    int displaySize = rsMeta.getColumnDisplaySize(i + 1);
187                    int type = DataType.getValueTypeFromResultSet(rsMeta, i + 1);
188                    Column col = new Column(n, type, precision, scale, displaySize);
189                    col.setTable(this, i++);
190                    columnList.add(col);
191                    columnMap.put(n, col);
192                }
193            }
194            rs.close();
195        } catch (Exception e) {
196            throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, e,
197                    originalTable + "(" + e.toString() + ")");
198        } finally {
199            JdbcUtils.closeSilently(stat);
200        }
201        Column[] cols = new Column[columnList.size()];
202        columnList.toArray(cols);
203        setColumns(cols);
204        int id = getId();
205        linkedIndex = new LinkedIndex(this, id, IndexColumn.wrap(cols),
206                IndexType.createNonUnique(false));
207        indexes.add(linkedIndex);
208        try {
209            rs = meta.getPrimaryKeys(null, originalSchema, originalTable);
210        } catch (Exception e) {
211            // Some ODBC bridge drivers don't support it:
212            // some combinations of "DataDirect SequeLink(R) for JDBC"
213            // http://www.datadirect.com/index.ssp
214            rs = null;
215        }
216        String pkName = "";
217        ArrayList<Column> list;
218        if (rs != null && rs.next()) {
219            // the problem is, the rows are not sorted by KEY_SEQ
220            list = New.arrayList();
221            do {
222                int idx = rs.getInt("KEY_SEQ");
223                if (pkName == null) {
224                    pkName = rs.getString("PK_NAME");
225                }
226                while (list.size() < idx) {
227                    list.add(null);
228                }
229                String col = rs.getString("COLUMN_NAME");
230                col = convertColumnName(col);
231                Column column = columnMap.get(col);
232                if (idx == 0) {
233                    // workaround for a bug in the SQLite JDBC driver
234                    list.add(column);
235                } else {
236                    list.set(idx - 1, column);
237                }
238            } while (rs.next());
239            addIndex(list, IndexType.createPrimaryKey(false, false));
240            rs.close();
241        }
242        try {
243            rs = meta.getIndexInfo(null, originalSchema, originalTable, false, true);
244        } catch (Exception e) {
245            // Oracle throws an exception if the table is not found or is a
246            // SYNONYM
247            rs = null;
248        }
249        String indexName = null;
250        list = New.arrayList();
251        IndexType indexType = null;
252        if (rs != null) {
253            while (rs.next()) {
254                if (rs.getShort("TYPE") == DatabaseMetaData.tableIndexStatistic) {
255                    // ignore index statistics
256                    continue;
257                }
258                String newIndex = rs.getString("INDEX_NAME");
259                if (pkName.equals(newIndex)) {
260                    continue;
261                }
262                if (indexName != null && !indexName.equals(newIndex)) {
263                    addIndex(list, indexType);
264                    indexName = null;
265                }
266                if (indexName == null) {
267                    indexName = newIndex;
268                    list.clear();
269                }
270                boolean unique = !rs.getBoolean("NON_UNIQUE");
271                indexType = unique ? IndexType.createUnique(false, false) :
272                        IndexType.createNonUnique(false);
273                String col = rs.getString("COLUMN_NAME");
274                col = convertColumnName(col);
275                Column column = columnMap.get(col);
276                list.add(column);
277            }
278            rs.close();
279        }
280        if (indexName != null) {
281            addIndex(list, indexType);
282        }
283    }
284 
285    private static long convertPrecision(int sqlType, long precision) {
286        // workaround for an Oracle problem:
287        // for DATE columns, the reported precision is 7
288        // for DECIMAL columns, the reported precision is 0
289        switch (sqlType) {
290        case Types.DECIMAL:
291        case Types.NUMERIC:
292            if (precision == 0) {
293                precision = 65535;
294            }
295            break;
296        case Types.DATE:
297            precision = Math.max(ValueDate.PRECISION, precision);
298            break;
299        case Types.TIMESTAMP:
300            precision = Math.max(ValueTimestamp.PRECISION, precision);
301            break;
302        case Types.TIME:
303            precision = Math.max(ValueTime.PRECISION, precision);
304            break;
305        }
306        return precision;
307    }
308 
309    private static int convertScale(int sqlType, int scale) {
310        // workaround for an Oracle problem:
311        // for DECIMAL columns, the reported precision is -127
312        switch (sqlType) {
313        case Types.DECIMAL:
314        case Types.NUMERIC:
315            if (scale < 0) {
316                scale = 32767;
317            }
318            break;
319        }
320        return scale;
321    }
322 
323    private String convertColumnName(String columnName) {
324        if ((storesMixedCase || storesLowerCase) &&
325                columnName.equals(StringUtils.toLowerEnglish(columnName))) {
326            columnName = StringUtils.toUpperEnglish(columnName);
327        } else if (storesMixedCase && !supportsMixedCaseIdentifiers) {
328            // TeraData
329            columnName = StringUtils.toUpperEnglish(columnName);
330        } else if (storesMixedCase && storesMixedCaseQuoted) {
331            // MS SQL Server (identifiers are case insensitive even if quoted)
332            columnName = StringUtils.toUpperEnglish(columnName);
333        }
334        return columnName;
335    }
336 
337    private void addIndex(ArrayList<Column> list, IndexType indexType) {
338        Column[] cols = new Column[list.size()];
339        list.toArray(cols);
340        Index index = new LinkedIndex(this, 0, IndexColumn.wrap(cols), indexType);
341        indexes.add(index);
342    }
343 
344    @Override
345    public String getDropSQL() {
346        return "DROP TABLE IF EXISTS " + getSQL();
347    }
348 
349    @Override
350    public String getCreateSQL() {
351        StringBuilder buff = new StringBuilder("CREATE FORCE ");
352        if (isTemporary()) {
353            if (globalTemporary) {
354                buff.append("GLOBAL ");
355            } else {
356                buff.append("LOCAL ");
357            }
358            buff.append("TEMPORARY ");
359        }
360        buff.append("LINKED TABLE ").append(getSQL());
361        if (comment != null) {
362            buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment));
363        }
364        buff.append('(').
365            append(StringUtils.quoteStringSQL(driver)).
366            append(", ").
367            append(StringUtils.quoteStringSQL(url)).
368            append(", ").
369            append(StringUtils.quoteStringSQL(user)).
370            append(", ").
371            append(StringUtils.quoteStringSQL(password)).
372            append(", ").
373            append(StringUtils.quoteStringSQL(originalTable)).
374            append(')');
375        if (emitUpdates) {
376            buff.append(" EMIT UPDATES");
377        }
378        if (readOnly) {
379            buff.append(" READONLY");
380        }
381        buff.append(" /*" + JdbcSQLException.HIDE_SQL + "*/");
382        return buff.toString();
383    }
384 
385    @Override
386    public Index addIndex(Session session, String indexName, int indexId,
387            IndexColumn[] cols, IndexType indexType, boolean create,
388            String indexComment) {
389        throw DbException.getUnsupportedException("LINK");
390    }
391 
392    @Override
393    public boolean lock(Session session, boolean exclusive, boolean forceLockEvenInMvcc) {
394        // nothing to do
395        return false;
396    }
397 
398    @Override
399    public boolean isLockedExclusively() {
400        return false;
401    }
402 
403    @Override
404    public Index getScanIndex(Session session) {
405        return linkedIndex;
406    }
407 
408    private void checkReadOnly() {
409        if (readOnly) {
410            throw DbException.get(ErrorCode.DATABASE_IS_READ_ONLY);
411        }
412    }
413 
414    @Override
415    public void removeRow(Session session, Row row) {
416        checkReadOnly();
417        getScanIndex(session).remove(session, row);
418    }
419 
420    @Override
421    public void addRow(Session session, Row row) {
422        checkReadOnly();
423        getScanIndex(session).add(session, row);
424    }
425 
426    @Override
427    public void close(Session session) {
428        if (conn != null) {
429            try {
430                conn.close(false);
431            } finally {
432                conn = null;
433            }
434        }
435    }
436 
437    @Override
438    public synchronized long getRowCount(Session session) {
439        String sql = "SELECT COUNT(*) FROM " + qualifiedTableName;
440        try {
441            PreparedStatement prep = execute(sql, null, false);
442            ResultSet rs = prep.getResultSet();
443            rs.next();
444            long count = rs.getLong(1);
445            rs.close();
446            reusePreparedStatement(prep, sql);
447            return count;
448        } catch (Exception e) {
449            throw wrapException(sql, e);
450        }
451    }
452 
453    /**
454     * Wrap a SQL exception that occurred while accessing a linked table.
455     *
456     * @param sql the SQL statement
457     * @param ex the exception from the remote database
458     * @return the wrapped exception
459     */
460    public static DbException wrapException(String sql, Exception ex) {
461        SQLException e = DbException.toSQLException(ex);
462        return DbException.get(ErrorCode.ERROR_ACCESSING_LINKED_TABLE_2,
463                e, sql, e.toString());
464    }
465 
466    public String getQualifiedTable() {
467        return qualifiedTableName;
468    }
469 
470    /**
471     * Execute a SQL statement using the given parameters. Prepared
472     * statements are kept in a hash map to avoid re-creating them.
473     *
474     * @param sql the SQL statement
475     * @param params the parameters or null
476     * @param reusePrepared if the prepared statement can be re-used immediately
477     * @return the prepared statement, or null if it is re-used
478     */
479    public PreparedStatement execute(String sql, ArrayList<Value> params,
480            boolean reusePrepared) {
481        if (conn == null) {
482            throw connectException;
483        }
484        for (int retry = 0;; retry++) {
485            try {
486                synchronized (conn) {
487                    PreparedStatement prep = preparedMap.remove(sql);
488                    if (prep == null) {
489                        prep = conn.getConnection().prepareStatement(sql);
490                    }
491                    if (trace.isDebugEnabled()) {
492                        StatementBuilder buff = new StatementBuilder();
493                        buff.append(getName()).append(":\n").append(sql);
494                        if (params != null && params.size() > 0) {
495                            buff.append(" {");
496                            int i = 1;
497                            for (Value v : params) {
498                                buff.appendExceptFirst(", ");
499                                buff.append(i++).append(": ").append(v.getSQL());
500                            }
501                            buff.append('}');
502                        }
503                        buff.append(';');
504                        trace.debug(buff.toString());
505                    }
506                    if (params != null) {
507                        for (int i = 0, size = params.size(); i < size; i++) {
508                            Value v = params.get(i);
509                            v.set(prep, i + 1);
510                        }
511                    }
512                    prep.execute();
513                    if (reusePrepared) {
514                        reusePreparedStatement(prep, sql);
515                        return null;
516                    }
517                    return prep;
518                }
519            } catch (SQLException e) {
520                if (retry >= MAX_RETRY) {
521                    throw DbException.convert(e);
522                }
523                conn.close(true);
524                connect();
525            }
526        }
527    }
528 
529    @Override
530    public void unlock(Session s) {
531        // nothing to do
532    }
533 
534    @Override
535    public void checkRename() {
536        // ok
537    }
538 
539    @Override
540    public void checkSupportAlter() {
541        throw DbException.getUnsupportedException("LINK");
542    }
543 
544    @Override
545    public void truncate(Session session) {
546        throw DbException.getUnsupportedException("LINK");
547    }
548 
549    @Override
550    public boolean canGetRowCount() {
551        return true;
552    }
553 
554    @Override
555    public boolean canDrop() {
556        return true;
557    }
558 
559    @Override
560    public String getTableType() {
561        return Table.TABLE_LINK;
562    }
563 
564    @Override
565    public void removeChildrenAndResources(Session session) {
566        super.removeChildrenAndResources(session);
567        close(session);
568        database.removeMeta(session, getId());
569        driver = null;
570        url = user = password = originalTable = null;
571        preparedMap = null;
572        invalidate();
573    }
574 
575    public boolean isOracle() {
576        return url.startsWith("jdbc:oracle:");
577    }
578 
579    @Override
580    public ArrayList<Index> getIndexes() {
581        return indexes;
582    }
583 
584    @Override
585    public long getMaxDataModificationId() {
586        // data may have been modified externally
587        return Long.MAX_VALUE;
588    }
589 
590    @Override
591    public Index getUniqueIndex() {
592        for (Index idx : indexes) {
593            if (idx.getIndexType().isUnique()) {
594                return idx;
595            }
596        }
597        return null;
598    }
599 
600    @Override
601    public void updateRows(Prepared prepared, Session session, RowList rows) {
602        boolean deleteInsert;
603        checkReadOnly();
604        if (emitUpdates) {
605            for (rows.reset(); rows.hasNext();) {
606                prepared.checkCanceled();
607                Row oldRow = rows.next();
608                Row newRow = rows.next();
609                linkedIndex.update(oldRow, newRow);
610                session.log(this, UndoLogRecord.DELETE, oldRow);
611                session.log(this, UndoLogRecord.INSERT, newRow);
612            }
613            deleteInsert = false;
614        } else {
615            deleteInsert = true;
616        }
617        if (deleteInsert) {
618            super.updateRows(prepared, session, rows);
619        }
620    }
621 
622    public void setGlobalTemporary(boolean globalTemporary) {
623        this.globalTemporary = globalTemporary;
624    }
625 
626    public void setReadOnly(boolean readOnly) {
627        this.readOnly = readOnly;
628    }
629 
630    @Override
631    public long getRowCountApproximation() {
632        return ROW_COUNT_APPROXIMATION;
633    }
634 
635    @Override
636    public long getDiskSpaceUsed() {
637        return 0;
638    }
639 
640    /**
641     * Add this prepared statement to the list of cached statements.
642     *
643     * @param prep the prepared statement
644     * @param sql the SQL statement
645     */
646    public void reusePreparedStatement(PreparedStatement prep, String sql) {
647        synchronized (conn) {
648            preparedMap.put(sql, prep);
649        }
650    }
651 
652    @Override
653    public boolean isDeterministic() {
654        return false;
655    }
656 
657    /**
658     * Linked tables don't know if they are readonly. This overwrites
659     * the default handling.
660     */
661    @Override
662    public void checkWritingAllowed() {
663        // only the target database can verify this
664    }
665 
666    /**
667     * Convert the values if required. Default values are not set (kept as
668     * null).
669     *
670     * @param session the session
671     * @param row the row
672     */
673    @Override
674    public void validateConvertUpdateSequence(Session session, Row row) {
675        for (int i = 0; i < columns.length; i++) {
676            Value value = row.getValue(i);
677            if (value != null) {
678                // null means use the default value
679                Column column = columns[i];
680                Value v2 = column.validateConvertUpdateSequence(session, value);
681                if (v2 != value) {
682                    row.setValue(i, v2);
683                }
684            }
685        }
686    }
687 
688    /**
689     * Get or generate a default value for the given column. Default values are
690     * not set (kept as null).
691     *
692     * @param session the session
693     * @param column the column
694     * @return the value
695     */
696    @Override
697    public Value getDefaultValue(Session session, Column column) {
698        return null;
699    }
700 
701}

[all classes][org.h2.table]
EMMA 2.0.5312 (C) Vladimir Roubtsov