@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); }
@Override public Cursor find(Session session, SearchRow first, SearchRow last) { ValueLong min, max; if (first == null) { min = MIN; } else if (mainIndexColumn < 0) { min = ValueLong.get(first.getKey()); } else { ValueLong v = (ValueLong) first.getValue(mainIndexColumn); if (v == null) { min = ValueLong.get(first.getKey()); } else { min = v; } } if (last == null) { max = MAX; } else if (mainIndexColumn < 0) { max = ValueLong.get(last.getKey()); } else { ValueLong v = (ValueLong) last.getValue(mainIndexColumn); if (v == null) { max = ValueLong.get(last.getKey()); } else { max = v; } } TransactionMap<Value, Value> map = getMap(session); return new MVStoreCursor(session, map.entryIterator(min), max); }
/** * Check if one of the columns is NULL and multiple rows with NULL are allowed using the current * compatibility mode for unique indexes. Note: NULL behavior is complicated in SQL. * * @param newRow the row to check * @return true if one of the columns is null and multiple nulls in unique indexes are allowed */ protected boolean containsNullAndAllowMultipleNull(SearchRow newRow) { Mode mode = database.getMode(); // 1. 对于唯一索引,必须完全唯一,适用于Derby/HSQLDB/MSSQLServer if (mode.uniqueIndexSingleNull) { // 不允许出现: // (x, null) // (x, null) // 也不允许出现: // (null, null) // (null, null) return false; } else if (mode.uniqueIndexSingleNullExceptAllColumnsAreNull) { // 2. 对于唯一索引,索引记录可以全为null,适用于Oracle // 不允许出现: // (x, null) // (x, null) // 但是允许出现: // (null, null) // (null, null) for (int index : columnIds) { Value v = newRow.getValue(index); if (v != ValueNull.INSTANCE) { return false; } } return true; } // 3. 对于唯一索引,只要一个为null,就是合法的,适用于REGULAR(即H2)/DB2/MySQL/PostgreSQL // 即允许出现: // (x, null) // (x, null) // 也允许出现: // (null, null) // (null, null) // 也就是说,只要相同的两条索引记录包含null即可 for (int index : columnIds) { Value v = newRow.getValue(index); if (v == ValueNull.INSTANCE) { return true; } } // 4. 对于唯一索引,没有null时是不允许出现两条相同的索引记录的 return false; }
/** {@inheritDoc} */ @Override public int compareRows(SearchRow rowData, SearchRow compare) { if (rowData == compare) return 0; for (int i = 0, len = indexColumns.length; i < len; i++) { int index = columnIds[i]; Value v1 = rowData.getValue(index); Value v2 = compare.getValue(index); if (v1 == null || v2 == null) return 0; int c = compareValues(v1, v2, indexColumns[i].sortType); if (c != 0) return c; } return 0; }
@Override public int compareRows(SearchRow rowData, SearchRow compare) { // 只比较索引字段,并不一定是所有字段 if (rowData == compare) { return 0; } for (int i = 0, len = indexColumns.length; i < len; i++) { int index = columnIds[i]; Value v = compare.getValue(index); if (v == null) { // 只要compare中有null值就认为无法比较,直接认为rowData和compare相等(通常在查询时在where中再比较) // can't compare further return 0; } int c = compareValues(rowData.getValue(index), v, indexColumns[i].sortType); if (c != 0) { return c; } } return 0; }
@Override public Cursor findFirstOrLast(Session session, boolean first) { if (closed) { throw DbException.throwInternalError(); } if (first) { // TODO optimization: this loops through NULL Cursor cursor = find(session, null, null); while (cursor.next()) { SearchRow row = cursor.getSearchRow(); Value v = row.getValue(columnIds[0]); if (v != ValueNull.INSTANCE) { return cursor; } } return cursor; } TreeNode x = root, n; while (x != null) { n = x.right; if (n == null) { break; } x = n; } TreeCursor cursor = new TreeCursor(this, x, null, null); if (x == null) { return cursor; } // TODO optimization: this loops through NULL elements do { SearchRow row = cursor.getSearchRow(); if (row == null) { break; } Value v = row.getValue(columnIds[0]); if (v != ValueNull.INSTANCE) { return cursor; } } while (cursor.previous()); return cursor; }
/** * 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(); }
private void queryDistinct(ResultTarget result, long limitRows) { // limitRows must be long, otherwise we get an int overflow // if limitRows is at or near Integer.MAX_VALUE // limitRows is never 0 here if (limitRows > 0 && offsetExpr != null) { int offset = offsetExpr.getValue(session).getInt(); if (offset > 0) { limitRows += offset; } } int rowNumber = 0; setCurrentRowNumber(0); Index index = topTableFilter.getIndex(); SearchRow first = null; int columnIndex = index.getColumns()[0].getColumnId(); while (true) { setCurrentRowNumber(rowNumber + 1); Cursor cursor = index.findNext(session, first, null); if (!cursor.next()) { break; } SearchRow found = cursor.getSearchRow(); Value value = found.getValue(columnIndex); if (first == null) { first = topTableFilter.getTable().getTemplateSimpleRow(true); } first.setValue(columnIndex, value); Value[] row = {value}; result.addRow(row); rowNumber++; if ((sort == null || sortUsingIndex) && limitRows > 0 && rowNumber >= limitRows) { break; } if (sampleSize > 0 && rowNumber >= sampleSize) { break; } } }
public Cursor find(Session session, SearchRow first, SearchRow last) { if (recursive) { if (view.getRecursiveResult() != null) { ResultInterface r = view.getRecursiveResult(); r.reset(); return new ViewCursor(table, r); } if (query == null) { query = (Query) createSession.prepare(querySQL, true); planSQL = query.getPlanSQL(); } if (!(query instanceof SelectUnion)) { throw DbException.get(ErrorCode.SYNTAX_ERROR_2, "recursive queries without UNION ALL"); } SelectUnion union = (SelectUnion) query; if (union.getUnionType() != SelectUnion.UNION_ALL) { throw DbException.get(ErrorCode.SYNTAX_ERROR_2, "recursive queries without UNION ALL"); } Query left = union.getLeft(); ResultInterface r = left.query(0); LocalResult result = union.getEmptyResult(); while (r.next()) { result.addRow(r.currentRow()); } Query right = union.getRight(); r.reset(); view.setRecursiveResult(r); while (true) { r = right.query(0); if (r.getRowCount() == 0) { break; } while (r.next()) { result.addRow(r.currentRow()); } r.reset(); view.setRecursiveResult(r); } return new ViewCursor(table, result); } ArrayList<Parameter> paramList = query.getParameters(); for (int i = 0; originalParameters != null && i < originalParameters.size(); i++) { Parameter orig = originalParameters.get(i); int idx = orig.getIndex(); Value value = orig.getValue(session); setParameter(paramList, idx, value); } int len; if (first != null) { len = first.getColumnCount(); } else if (last != null) { len = last.getColumnCount(); } else { len = 0; } int idx = originalParameters == null ? 0 : originalParameters.size(); idx += view.getParameterOffset(); for (int i = 0; i < len; i++) { if (first != null) { Value v = first.getValue(i); if (v != null) { int x = idx++; setParameter(paramList, x, v); } } // for equality, only one parameter is used (first == last) if (last != null && indexMasks[i] != IndexCondition.EQUALITY) { Value v = last.getValue(i); if (v != null) { int x = idx++; setParameter(paramList, x, v); } } } ResultInterface result = query.query(0); return new ViewCursor(table, result); }