private boolean sameResultAsLast(Session s, Value[] params, Value[] lastParams, long lastEval) { if (!cacheableChecked) { long max = getMaxDataModificationId(); noCache = max == Long.MAX_VALUE; cacheableChecked = true; } if (noCache) { return false; } Database db = s.getDatabase(); for (int i = 0; i < params.length; i++) { Value a = lastParams[i], b = params[i]; if (a.getType() != b.getType() || !db.areEqual(a, b)) { return false; } } if (!isEverything(ExpressionVisitor.DETERMINISTIC_VISITOR) || !isEverything(ExpressionVisitor.INDEPENDENT_VISITOR)) { return false; } if (db.getModificationDataId() > lastEval && getMaxDataModificationId() > lastEval) { return false; } return true; }
/** * Add or update a key value pair. * * @param key the key * @param value the new value */ public void put(Value key, V value) { checkSizePut(); int index = getIndex(key); int plus = 1; int deleted = -1; do { Value k = keys[index]; if (k == null) { // found an empty record if (deleted >= 0) { index = deleted; deletedCount--; } size++; keys[index] = key; values[index] = value; return; } else if (k == ValueNull.DELETED) { // found a deleted record if (deleted < 0) { deleted = index; } } else if (k.equals(key)) { // update existing values[index] = value; return; } index = (index + plus++) & mask; } while (plus <= len); // no space DbException.throwInternalError("hashmap is full"); }
@Override public Value getValue(Session session) { Value l = left.getValue(session); if (l == ValueNull.INSTANCE) { return l; } boolean result = false; boolean hasNull = false; for (Expression e : valueList) { Value r = e.getValue(session); if (r == ValueNull.INSTANCE) { hasNull = true; } else { r = r.convertTo(l.getType()); result = Comparison.compareNotNull(database, l, r, Comparison.EQUAL); if (result) { break; } } } if (!result && hasNull) { return ValueNull.INSTANCE; } return ValueBoolean.get(result); }
@Override public Cursor find(Session session, SearchRow first, SearchRow last) { long min, max; if (first == null || mainIndexColumn < 0) { min = Long.MIN_VALUE; } else { Value v = first.getValue(mainIndexColumn); if (v == null) { min = 0; } else { min = v.getLong(); } } if (last == null || mainIndexColumn < 0) { max = Long.MAX_VALUE; } else { Value v = last.getValue(mainIndexColumn); if (v == null) { max = Long.MAX_VALUE; } else { max = v.getLong(); } } TransactionMap<Value, Value> map = getMap(session); return new MVStoreCursor(session, map.keyIterator(ValueLong.get(min)), max); }
/** * Convert an object to the default Java object for the given SQL type. For example, LOB objects * are converted to java.sql.Clob / java.sql.Blob. * * @param v the value * @return the object */ Object convertToDefaultObject(Value v) { Object o; switch (v.getType()) { case Value.CLOB: { if (SysProperties.RETURN_LOB_OBJECTS) { int id = getNextId(TraceObject.CLOB); o = new JdbcClob(this, v, id); } else { o = v.getObject(); } break; } case Value.BLOB: { if (SysProperties.RETURN_LOB_OBJECTS) { int id = getNextId(TraceObject.BLOB); o = new JdbcBlob(this, v, id); } else { o = v.getObject(); } break; } case Value.JAVA_OBJECT: o = Utils.deserialize(v.getBytesNoCopy()); break; default: o = v.getObject(); } return o; }
/** * Remember that the given LOB value must be un-linked (disconnected from the table) at commit. * * @param v the value */ public void unlinkAtCommit(Value v) { if (SysProperties.CHECK && !v.isLinked()) { DbException.throwInternalError(); } if (unlinkLobMap == null) { unlinkLobMap = New.hashMap(); } unlinkLobMap.put(v.toString(), v); }
/** * Get the sample size, if set. * * @param session the session * @return the sample size */ int getSampleSizeValue(Session session) { if (sampleSizeExpr == null) { return 0; } Value v = sampleSizeExpr.optimize(session).getValue(session); if (v == ValueNull.INSTANCE) { return 0; } return v.getInt(); }
private boolean isHavingNullOrFalse(Value[] row) { if (havingIndex >= 0) { Value v = row[havingIndex]; if (v == ValueNull.INSTANCE) { return true; } if (!Boolean.TRUE.equals(v.getBoolean())) { return true; } } return false; }
private void writeValue(Value v) throws IOException { if (v.getType() == Value.CLOB || v.getType() == Value.BLOB) { if (v instanceof ValueLobDb) { ValueLobDb lob = (ValueLobDb) v; if (lob.isStored()) { long id = lob.getLobId(); lobs.put(id, new CachedInputStream(null)); } } } transfer.writeValue(v); }
/** * Get the key from the row. * * @param row the row * @param ifEmpty the value to use if the row is empty * @param ifNull the value to use if the column is NULL * @return the key */ long getKey(SearchRow row, long ifEmpty, long ifNull) { if (row == null) { return ifEmpty; } Value v = row.getValue(mainIndexColumn); if (v == null) { throw DbException.throwInternalError(row.toString()); } else if (v == ValueNull.INSTANCE) { return ifNull; } return v.getLong(); }
@Override public Cursor findFirstOrLast(Session session, boolean first) { TransactionMap<Value, Value> map = getMap(session); Value v = first ? map.firstKey() : map.lastKey(); if (v == null) { return new MVStoreCursor(session, Collections.<Value>emptyList().iterator(), 0); } long key = v.getLong(); MVStoreCursor cursor = new MVStoreCursor(session, Arrays.asList((Value) ValueLong.get(key)).iterator(), key); cursor.next(); return cursor; }
@Override public String toString() { StatementBuilder buff = new StatementBuilder("( /* key:"); buff.append(getKey()); if (version != 0) { buff.append(" v:" + version); } buff.append(" */ "); for (Value v : data) { buff.appendExceptFirst(", "); buff.append(v == null ? "null" : v.getTraceSQL()); } return buff.append(')').toString(); }
@Override public int getMemory() { if (memory == 0) { int len = data.length; memory = Constants.MEMORY_OBJECT + len * Constants.MEMORY_POINTER; for (int i = 0; i < len; i++) { Value v = data[i]; if (v != null) { memory += v.getMemory(); } } } return memory; }
private void endTransaction() { if (removeLobMap != null && removeLobMap.size() > 0) { if (database.getMvStore() == null) { // need to flush the transaction log, because we can't unlink // lobs if the commit record is not written database.flush(); } for (Value v : removeLobMap.values()) { v.remove(); } removeLobMap = null; } unlockAll(); }
/** * Returns the length. * * @return the length */ public long length() throws SQLException { try { debugCodeCall("length"); checkClosed(); if (value.getType() == Value.CLOB) { long precision = value.getPrecision(); if (precision > 0) { return precision; } } return IOUtils.copyAndCloseInput(value.getReader(), null, Long.MAX_VALUE); } catch (Exception e) { throw logAndConvert(e); } }
public MVPrimaryIndex( Database db, MVTable table, int id, IndexColumn[] columns, IndexType indexType) { this.mvTable = table; initBaseIndex(table, id, table.getName() + "_DATA", columns, indexType); int[] sortTypes = new int[columns.length]; for (int i = 0; i < columns.length; i++) { sortTypes[i] = SortOrder.ASCENDING; } ValueDataType keyType = new ValueDataType(null, null, null); ValueDataType valueType = new ValueDataType(db.getCompareMode(), db, sortTypes); mapName = "table." + getId(); dataMap = mvTable.getTransaction(null).openMap(mapName, keyType, valueType); Value k = dataMap.lastKey(); lastKey = k == null ? 0 : k.getLong(); }
/** * Returns a substring. * * @param pos the position (the first character is at position 1) * @param length the number of characters * @return the string */ public String getSubString(long pos, int length) throws SQLException { try { if (isDebugEnabled()) { debugCode("getSubString(" + pos + ", " + length + ");"); } checkClosed(); if (pos < 1) { throw DbException.getInvalidValueException("pos", pos); } if (length < 0) { throw DbException.getInvalidValueException("length", length); } StringWriter writer = new StringWriter(Math.min(Constants.IO_BUFFER_SIZE, length)); Reader reader = value.getReader(); try { IOUtils.skipFully(reader, pos - 1); IOUtils.copyAndCloseInput(reader, writer, length); } finally { reader.close(); } return writer.toString(); } catch (Exception e) { throw logAndConvert(e); } }
private void testDouble(boolean useFloat) { double[] d = {Double.NEGATIVE_INFINITY, -1, 0, 1, Double.POSITIVE_INFINITY, Double.NaN}; Value[] values = new Value[d.length]; for (int i = 0; i < d.length; i++) { Value v = useFloat ? (Value) ValueFloat.get((float) d[i]) : (Value) ValueDouble.get(d[i]); values[i] = v; assertTrue(values[i].compareTypeSafe(values[i], null) == 0); assertTrue(v.equals(v)); assertEquals(i < 2 ? -1 : i > 2 ? 1 : 0, v.getSignum()); } for (int i = 0; i < d.length - 1; i++) { assertTrue(values[i].compareTypeSafe(values[i + 1], null) < 0); assertTrue(values[i + 1].compareTypeSafe(values[i], null) > 0); assertTrue(!values[i].equals(values[i + 1])); } }
/** * Convert the timestamp to the specified time zone. * * @param value the timestamp (might be ValueNull) * @param calendar the calendar * @return the timestamp using the correct time zone */ public static Timestamp convertTimestamp(Value value, Calendar calendar) { if (value == ValueNull.INSTANCE) { return null; } ValueTimestamp ts = (ValueTimestamp) value.convertTo(Value.TIMESTAMP); Calendar cal = (Calendar) calendar.clone(); cal.clear(); cal.setLenient(true); long dateValue = ts.getDateValue(); long nanos = ts.getTimeNanos(); long millis = nanos / 1000000; nanos -= millis * 1000000; long s = millis / 1000; millis -= s * 1000; long m = s / 60; s -= m * 60; long h = m / 60; m -= h * 60; setCalendarFields( cal, yearFromDateValue(dateValue), monthFromDateValue(dateValue), dayFromDateValue(dateValue), (int) h, (int) m, (int) s, (int) millis); long ms = cal.getTimeInMillis(); Timestamp x = new Timestamp(ms); x.setNanos((int) (nanos + millis * 1000000)); return x; }
/** * Set the value of the given variable for this session. * * @param name the name of the variable (may not be null) * @param value the new value (may not be null) */ public void setVariable(String name, Value value) { initVariables(); modificationId++; Value old; if (value == ValueNull.INSTANCE) { old = variables.remove(name); } else { // link LOB values, to make sure we have our own object value = value.copy(database, LobStorageFrontend.TABLE_ID_SESSION_VARIABLE); old = variables.put(name, value); } if (old != null) { // remove the old value (in case it is a lob) old.remove(); } }
public int getMemory() { if (memory != MEMORY_CALCULATE) { return memory; } int m = Constants.MEMORY_ROW; if (data != null) { int len = data.length; m += Constants.MEMORY_OBJECT + len * Constants.MEMORY_POINTER; for (int i = 0; i < len; i++) { Value v = data[i]; if (v != null) { m += v.getMemory(); } } } return m; }
/** * A record is added to a table, or removed from a table. * * @param session the session * @param tableId the table id * @param row the row to add * @param add true if the row is added, false if it is removed */ void logAddOrRemoveRow(Session session, int tableId, Row row, boolean add) { if (trace.isDebugEnabled()) { trace.debug( "log " + (add ? "+" : "-") + " s: " + session.getId() + " table: " + tableId + " row: " + row); } session.addLogPos(logSectionId, logPos); logPos++; Data data = dataBuffer; data.reset(); int columns = row.getColumnCount(); data.writeVarInt(columns); data.checkCapacity(row.getByteCount(data)); if (session.isRedoLogBinaryEnabled()) { for (int i = 0; i < columns; i++) { data.writeValue(row.getValue(i)); } } else { for (int i = 0; i < columns; i++) { Value v = row.getValue(i); if (v.getType() == Value.BYTES) { data.writeValue(ValueNull.INSTANCE); } else { data.writeValue(v); } } } Data buffer = getBuffer(); buffer.writeByte((byte) (add ? ADD : REMOVE)); buffer.writeVarInt(session.getId()); buffer.writeVarInt(tableId); buffer.writeVarLong(row.getKey()); if (add) { buffer.writeVarInt(data.length()); buffer.checkCapacity(data.length()); buffer.write(data.getBytes(), 0, data.length()); } write(buffer); }
/** * Get the value for this key. This method returns null if the key was not found. * * @param key the key * @return the value for the given key */ public V get(Value key) { int index = getIndex(key); int plus = 1; do { Value k = keys[index]; if (k == null) { // found an empty record return null; } else if (k == ValueNull.DELETED) { // found a deleted record } else if (k.equals(key)) { // found it return values[index]; } index = (index + plus++) & mask; } while (plus <= len); return null; }
/** * Returns the reader. * * @return the reader */ public Reader getCharacterStream() throws SQLException { try { debugCodeCall("getCharacterStream"); checkClosed(); return value.getReader(); } catch (Exception e) { throw logAndConvert(e); } }
@Override public void addTemporaryLob(Value v) { if (v.getType() != Value.CLOB && v.getType() != Value.BLOB) { return; } if (v.getTableId() == LobStorageFrontend.TABLE_RESULT || v.getTableId() == LobStorageFrontend.TABLE_TEMP) { if (temporaryResultLobs == null) { temporaryResultLobs = new LinkedList<TimeoutValue>(); } temporaryResultLobs.add(new TimeoutValue(v)); } else { if (temporaryLobs == null) { temporaryLobs = new ArrayList<Value>(); } temporaryLobs.add(v); } }
@Override public void add(Session session, Row row) { if (mainIndexColumn == -1) { if (row.getKey() == 0) { row.setKey(++lastKey); } } else { long c = row.getValue(mainIndexColumn).getLong(); row.setKey(c); } if (mvTable.getContainsLargeObject()) { for (int i = 0, len = row.getColumnCount(); i < len; i++) { Value v = row.getValue(i); Value v2 = v.link(database, getId()); if (v2.isLinked()) { session.unlinkAtCommitStop(v2); } if (v != v2) { row.setValue(i, v2); } } } TransactionMap<Value, Value> map = getMap(session); Value key = ValueLong.get(row.getKey()); Value old = map.getLatest(key); if (old != null) { String sql = "PRIMARY KEY ON " + table.getSQL(); if (mainIndexColumn >= 0 && mainIndexColumn < indexColumns.length) { sql += "(" + indexColumns[mainIndexColumn].getSQL() + ")"; } DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql); e.setSource(this); throw e; } try { map.put(key, ValueArray.get(row.getValueList())); } catch (IllegalStateException e) { throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName()); } lastKey = Math.max(lastKey, row.getKey()); }
/** * Returns the input stream. * * @return the input stream */ public InputStream getAsciiStream() throws SQLException { try { debugCodeCall("getAsciiStream"); checkClosed(); String s = value.getString(); return IOUtils.getInputStreamFromString(s); } catch (Exception e) { throw logAndConvert(e); } }
@Override public void remove(Session session, Row row) { if (mvTable.getContainsLargeObject()) { for (int i = 0, len = row.getColumnCount(); i < len; i++) { Value v = row.getValue(i); if (v.isLinked()) { session.unlinkAtCommit(v); } } } TransactionMap<Value, Value> map = getMap(session); try { Value old = map.remove(ValueLong.get(row.getKey())); if (old == null) { throw DbException.get( ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1, getSQL() + ": " + row.getKey()); } } catch (IllegalStateException e) { throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName()); } }
/** * Get a writer to update the Clob. This is only supported for new, empty Clob objects that were * created with Connection.createClob() or createNClob(). The Clob is created in a separate * thread, and the object is only updated when Writer.close() is called. The position must be 1, * meaning the whole Clob data is set. * * @param pos where to start writing (the first character is at position 1) * @return a writer */ public Writer setCharacterStream(long pos) throws SQLException { try { if (isDebugEnabled()) { debugCodeCall("setCharacterStream(" + pos + ");"); } checkClosed(); if (pos != 1) { throw DbException.getInvalidValueException("pos", pos); } if (value.getPrecision() != 0) { throw DbException.getInvalidValueException("length", value.getPrecision()); } final JdbcConnection c = conn; // PipedReader / PipedWriter are a lot slower // than PipedInputStream / PipedOutputStream // (Sun/Oracle Java 1.6.0_20) final PipedInputStream in = new PipedInputStream(); final Task task = new Task() { public void call() { value = c.createClob(IOUtils.getReader(in), -1); } }; PipedOutputStream out = new PipedOutputStream(in) { public void close() throws IOException { super.close(); try { task.get(); } catch (Exception e) { throw DbException.convertToIOException(e); } } }; task.execute(); return IOUtils.getBufferedWriter(out); } catch (Exception e) { throw logAndConvert(e); } }
/** * Create a {@link SortOrder} object given the list of {@link SelectOrderBy} objects. The * expression list is extended if necessary. * * @param orderList a list of {@link SelectOrderBy} elements * @param expressionCount the number of columns in the query * @return the {@link SortOrder} object */ public SortOrder prepareOrder(ArrayList<SelectOrderBy> orderList, int expressionCount) { int size = orderList.size(); int[] index = new int[size]; int[] sortType = new int[size]; for (int i = 0; i < size; i++) { SelectOrderBy o = orderList.get(i); int idx; boolean reverse = false; Expression expr = o.columnIndexExpr; Value v = expr.getValue(null); if (v == ValueNull.INSTANCE) { // parameter not yet set - order by first column idx = 0; } else { idx = v.getInt(); if (idx < 0) { reverse = true; idx = -idx; } idx -= 1; if (idx < 0 || idx >= expressionCount) { throw DbException.get(ErrorCode.ORDER_BY_NOT_IN_RESULT, "" + (idx + 1)); } } index[i] = idx; boolean desc = o.descending; if (reverse) { desc = !desc; } int type = desc ? SortOrder.DESCENDING : SortOrder.ASCENDING; if (o.nullsFirst) { type += SortOrder.NULLS_FIRST; } else if (o.nullsLast) { type += SortOrder.NULLS_LAST; } sortType[i] = type; } return new SortOrder(session.getDatabase(), index, sortType, orderList); }