/** * 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); }
@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()); }
@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()); } }
private Cursor find(Row row) { if (index == null) { // for the case "in(select ...)", the query might // use an optimization and not create the index // up front createIndex(); } Cursor cursor = index.find(session, row, row); while (cursor.next()) { SearchRow found = cursor.getSearchRow(); boolean ok = true; Database db = session.getDatabase(); for (int i = 0; i < row.getColumnCount(); i++) { if (!db.areEqual(row.getValue(i), found.getValue(i))) { ok = false; break; } } if (ok) { return cursor; } } return null; }
private void writeRow(Data buff, Row r) { buff.checkCapacity(1 + Data.LENGTH_INT * 8); buff.writeByte((byte) 1); buff.writeInt(r.getMemory()); int columnCount = r.getColumnCount(); buff.writeInt(columnCount); buff.writeLong(r.getKey()); buff.writeInt(r.getVersion()); buff.writeInt(r.isDeleted() ? 1 : 0); buff.writeInt(r.getSessionId()); for (int i = 0; i < columnCount; i++) { Value v = r.getValue(i); buff.checkCapacity(1); if (v == null) { buff.writeByte((byte) 0); } else { buff.writeByte((byte) 1); if (v.getType() == Value.CLOB || v.getType() == Value.BLOB) { // need to keep a reference to temporary lobs, // otherwise the temp file is deleted if (v.getSmall() == null && v.getTableId() == 0) { if (lobs == null) { lobs = New.arrayList(); } // need to create a copy, otherwise, // if stored multiple times, it may be renamed // and then not found v = v.copyToTemp(); lobs.add(v); } } buff.checkCapacity(buff.getValueLen(v)); buff.writeValue(v); } } }
/** * @param cctx Cache context. * @param qry Query. * @param keepPortable Keep portable. * @return Cursor. */ public Iterator<List<?>> query( GridCacheContext<?, ?> cctx, GridCacheTwoStepQuery qry, boolean keepPortable) { for (int attempt = 0; ; attempt++) { if (attempt != 0) { try { Thread.sleep(attempt * 10); // Wait for exchange. } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new CacheException("Query was interrupted.", e); } } long qryReqId = reqIdGen.incrementAndGet(); QueryRun r = new QueryRun(); r.pageSize = qry.pageSize() <= 0 ? GridCacheTwoStepQuery.DFLT_PAGE_SIZE : qry.pageSize(); r.idxs = new ArrayList<>(qry.mapQueries().size()); String space = cctx.name(); r.conn = (JdbcConnection) h2.connectionForSpace(space); AffinityTopologyVersion topVer = h2.readyTopologyVersion(); List<String> extraSpaces = extraSpaces(space, qry.spaces()); Collection<ClusterNode> nodes; // Explicit partition mapping for unstable topology. Map<ClusterNode, IntArray> partsMap = null; if (isPreloadingActive(cctx, extraSpaces)) { if (cctx.isReplicated()) nodes = replicatedUnstableDataNodes(cctx, extraSpaces); else { partsMap = partitionedUnstableDataNodes(cctx, extraSpaces); nodes = partsMap == null ? null : partsMap.keySet(); } } else nodes = stableDataNodes(topVer, cctx, extraSpaces); if (nodes == null) continue; // Retry. assert !nodes.isEmpty(); if (cctx.isReplicated() || qry.explain()) { assert qry.explain() || !nodes.contains(ctx.discovery().localNode()) : "We must be on a client node."; // Select random data node to run query on a replicated data or get EXPLAIN PLAN from a // single node. nodes = Collections.singleton(F.rand(nodes)); } int tblIdx = 0; final boolean skipMergeTbl = !qry.explain() && qry.skipMergeTable(); for (GridCacheSqlQuery mapQry : qry.mapQueries()) { GridMergeIndex idx; if (!skipMergeTbl) { GridMergeTable tbl; try { tbl = createMergeTable(r.conn, mapQry, qry.explain()); } catch (IgniteCheckedException e) { throw new IgniteException(e); } idx = tbl.getScanIndex(null); fakeTable(r.conn, tblIdx++).setInnerTable(tbl); } else idx = GridMergeIndexUnsorted.createDummy(); for (ClusterNode node : nodes) idx.addSource(node.id()); r.idxs.add(idx); } r.latch = new CountDownLatch(r.idxs.size() * nodes.size()); runs.put(qryReqId, r); try { if (ctx.clientDisconnected()) { throw new CacheException( "Query was cancelled, client node disconnected.", new IgniteClientDisconnectedException( ctx.cluster().clientReconnectFuture(), "Client node disconnected.")); } Collection<GridCacheSqlQuery> mapQrys = qry.mapQueries(); if (qry.explain()) { mapQrys = new ArrayList<>(qry.mapQueries().size()); for (GridCacheSqlQuery mapQry : qry.mapQueries()) mapQrys.add(new GridCacheSqlQuery("EXPLAIN " + mapQry.query(), mapQry.parameters())); } if (nodes.size() != 1 || !F.first(nodes).isLocal()) { // Marshall params for remotes. Marshaller m = ctx.config().getMarshaller(); for (GridCacheSqlQuery mapQry : mapQrys) mapQry.marshallParams(m); } boolean retry = false; if (send( nodes, new GridQueryRequest(qryReqId, r.pageSize, space, mapQrys, topVer, extraSpaces, null), partsMap)) { awaitAllReplies(r, nodes); Object state = r.state.get(); if (state != null) { if (state instanceof CacheException) { CacheException err = (CacheException) state; if (err.getCause() instanceof IgniteClientDisconnectedException) throw err; throw new CacheException("Failed to run map query remotely.", err); } if (state instanceof AffinityTopologyVersion) { retry = true; // If remote node asks us to retry then we have outdated full partition map. h2.awaitForReadyTopologyVersion((AffinityTopologyVersion) state); } } } else // Send failed. retry = true; Iterator<List<?>> resIter = null; if (!retry) { if (qry.explain()) return explainPlan(r.conn, space, qry); if (skipMergeTbl) { List<List<?>> res = new ArrayList<>(); assert r.idxs.size() == 1 : r.idxs; GridMergeIndex idx = r.idxs.get(0); Cursor cur = idx.findInStream(null, null); while (cur.next()) { Row row = cur.get(); int cols = row.getColumnCount(); List<Object> resRow = new ArrayList<>(cols); for (int c = 0; c < cols; c++) resRow.add(row.getValue(c).getObject()); res.add(resRow); } resIter = res.iterator(); } else { GridCacheSqlQuery rdc = qry.reduceQuery(); // Statement caching is prohibited here because we can't guarantee correct merge index // reuse. ResultSet res = h2.executeSqlQueryWithTimer( space, r.conn, rdc.query(), F.asList(rdc.parameters()), false); resIter = new Iter(res); } } for (GridMergeIndex idx : r.idxs) { if (!idx.fetchedAll()) // We have to explicitly cancel queries on remote nodes. send(nodes, new GridQueryCancelRequest(qryReqId), null); } if (retry) { if (Thread.currentThread().isInterrupted()) throw new IgniteInterruptedCheckedException("Query was interrupted."); continue; } return new GridQueryCacheObjectsIterator(resIter, cctx, keepPortable); } catch (IgniteCheckedException | RuntimeException e) { U.closeQuiet(r.conn); if (e instanceof CacheException) throw (CacheException) e; Throwable cause = e; if (e instanceof IgniteCheckedException) { Throwable disconnectedErr = ((IgniteCheckedException) e).getCause(IgniteClientDisconnectedException.class); if (disconnectedErr != null) cause = disconnectedErr; } throw new CacheException("Failed to run reduce query locally.", cause); } finally { if (!runs.remove(qryReqId, r)) U.warn(log, "Query run was already removed: " + qryReqId); if (!skipMergeTbl) { for (int i = 0, mapQrys = qry.mapQueries().size(); i < mapQrys; i++) fakeTable(null, i).setInnerTable(null); // Drop all merge tables. } } } }