use of org.apache.ignite.internal.processors.query.h2.H2PooledConnection in project ignite by apache.
the class GridReduceQueryExecutor method createMergeTable.
/**
* @param conn Connection.
* @param qry Query.
* @param explain Explain.
* @return Table.
* @throws IgniteCheckedException If failed.
*/
@SuppressWarnings("unchecked")
private ReduceTable createMergeTable(H2PooledConnection conn, GridCacheSqlQuery qry, boolean explain) throws IgniteCheckedException {
try {
Session ses = H2Utils.session(conn);
CreateTableData data = new CreateTableData();
data.tableName = "T___";
data.schema = ses.getDatabase().getSchema(ses.getCurrentSchemaName());
data.create = true;
if (!explain) {
LinkedHashMap<String, ?> colsMap = qry.columns();
assert colsMap != null;
ArrayList<Column> cols = new ArrayList<>(colsMap.size());
for (Map.Entry<String, ?> e : colsMap.entrySet()) {
String alias = e.getKey();
GridSqlType type = (GridSqlType) e.getValue();
assert !F.isEmpty(alias);
Column col0;
if (type == GridSqlType.UNKNOWN) {
// Special case for parameter being set at the top of the query (e.g. SELECT ? FROM ...).
// Re-map it to STRING in the same way it is done in H2, because any argument can be cast
// to string.
col0 = new Column(alias, Value.STRING);
} else {
col0 = new Column(alias, type.type(), type.precision(), type.scale(), type.displaySize());
}
cols.add(col0);
}
data.columns = cols;
} else
data.columns = planColumns();
boolean sortedIndex = !F.isEmpty(qry.sortColumns());
ReduceTable tbl = new ReduceTable(data);
ArrayList<Index> idxs = new ArrayList<>(2);
if (explain) {
idxs.add(new UnsortedReduceIndexAdapter(ctx, tbl, sortedIndex ? MERGE_INDEX_SORTED : MERGE_INDEX_UNSORTED));
} else if (sortedIndex) {
List<GridSqlSortColumn> sortCols = (List<GridSqlSortColumn>) qry.sortColumns();
SortedReduceIndexAdapter sortedMergeIdx = new SortedReduceIndexAdapter(ctx, tbl, MERGE_INDEX_SORTED, GridSqlSortColumn.toIndexColumns(tbl, sortCols));
idxs.add(ReduceTable.createScanIndex(sortedMergeIdx));
idxs.add(sortedMergeIdx);
} else
idxs.add(new UnsortedReduceIndexAdapter(ctx, tbl, MERGE_INDEX_UNSORTED));
tbl.indexes(idxs);
return tbl;
} catch (Exception e) {
throw new IgniteCheckedException(e);
}
}
use of org.apache.ignite.internal.processors.query.h2.H2PooledConnection in project ignite by apache.
the class GridReduceQueryExecutor method createReduceQueryRun.
/**
* Query run factory method.
*
* @param conn H2 connection.
* @param mapQueries Map queries.
* @param nodes Target nodes.
* @param pageSize Page size.
* @param nodeToSegmentsCnt Segments per-index.
* @param skipMergeTbl Skip merge table flag.
* @param explain Explain query flag.
* @param dataPageScanEnabled DataPage scan enabled flag.
* @return Reduce query run.
*/
@NotNull
private ReduceQueryRun createReduceQueryRun(H2PooledConnection conn, List<GridCacheSqlQuery> mapQueries, Collection<ClusterNode> nodes, int pageSize, Map<ClusterNode, Integer> nodeToSegmentsCnt, boolean skipMergeTbl, boolean explain, Boolean dataPageScanEnabled) {
final ReduceQueryRun r = new ReduceQueryRun(mapQueries.size(), pageSize, dataPageScanEnabled);
int tblIdx = 0;
int replicatedQrysCnt = 0;
for (GridCacheSqlQuery mapQry : mapQueries) {
Reducer reducer;
if (skipMergeTbl)
reducer = UnsortedOneWayReducer.createDummy(ctx);
else {
ReduceTable tbl;
try {
tbl = createMergeTable(conn, mapQry, explain);
} catch (IgniteCheckedException e) {
throw new IgniteException(e);
}
reducer = tbl.getReducer();
fakeTable(conn, tblIdx++).innerTable(tbl);
}
// If the query has only replicated tables, we have to run it on a single node only.
if (!mapQry.isPartitioned()) {
ClusterNode node = F.rand(nodes);
mapQry.node(node.id());
replicatedQrysCnt++;
// Replicated tables can have only 1 segment.
reducer.setSources(singletonMap(node, 1));
} else
reducer.setSources(nodeToSegmentsCnt);
reducer.setPageSize(r.pageSize());
r.reducers().add(reducer);
}
int cnt = nodeToSegmentsCnt.values().stream().mapToInt(i -> i).sum();
r.init((r.reducers().size() - replicatedQrysCnt) * cnt + replicatedQrysCnt);
return r;
}
use of org.apache.ignite.internal.processors.query.h2.H2PooledConnection in project ignite by apache.
the class GridMapQueryExecutor method onQueryRequest0.
/**
* @param node Node authored request.
* @param qryId Query ID.
* @param reqId Request ID.
* @param segmentId index segment ID.
* @param schemaName Schema name.
* @param qrys Queries to execute.
* @param cacheIds Caches which will be affected by these queries.
* @param topVer Topology version.
* @param partsMap Partitions map for unstable topology.
* @param parts Explicit partitions for current node.
* @param pageSize Page size.
* @param distributedJoins Query distributed join mode.
* @param enforceJoinOrder Enforce join order H2 flag.
* @param replicated Replicated only flag.
* @param timeout Query timeout.
* @param params Query parameters.
* @param lazy Streaming flag.
* @param mvccSnapshot MVCC snapshot.
* @param dataPageScanEnabled If data page scan is enabled.
*/
private void onQueryRequest0(final ClusterNode node, final long qryId, final long reqId, final int segmentId, final String schemaName, final Collection<GridCacheSqlQuery> qrys, final List<Integer> cacheIds, final AffinityTopologyVersion topVer, final Map<UUID, int[]> partsMap, final int[] parts, final int pageSize, final boolean distributedJoins, final boolean enforceJoinOrder, final boolean replicated, final int timeout, final Object[] params, boolean lazy, @Nullable final MvccSnapshot mvccSnapshot, Boolean dataPageScanEnabled, boolean treatReplicatedAsPartitioned) {
boolean performanceStatsEnabled = ctx.performanceStatistics().enabled();
if (performanceStatsEnabled)
IoStatisticsQueryHelper.startGatheringQueryStatistics();
// Prepare to run queries.
GridCacheContext<?, ?> mainCctx = mainCacheContext(cacheIds);
MapNodeResults nodeRess = resultsForNode(node.id());
MapQueryResults qryResults = null;
PartitionReservation reserved = null;
QueryContext qctx = null;
// We don't use try with resources on purpose - the catch block must also be executed in the context of this span.
TraceSurroundings trace = MTC.support(ctx.tracing().create(SQL_QRY_EXEC_REQ, MTC.span()).addTag(SQL_QRY_TEXT, () -> qrys.stream().map(GridCacheSqlQuery::query).collect(Collectors.joining("; "))));
try {
if (topVer != null) {
// Reserve primary for topology version or explicit partitions.
reserved = h2.partitionReservationManager().reservePartitions(cacheIds, topVer, parts, node.id(), reqId);
if (reserved.failed()) {
sendRetry(node, reqId, segmentId, reserved.error());
return;
}
}
// Prepare query context.
DistributedJoinContext distributedJoinCtx = null;
if (distributedJoins && !replicated) {
distributedJoinCtx = new DistributedJoinContext(topVer, partsMap, node.id(), reqId, segmentId, pageSize);
}
qctx = new QueryContext(segmentId, h2.backupFilter(topVer, parts, treatReplicatedAsPartitioned), distributedJoinCtx, mvccSnapshot, reserved, true);
qryResults = new MapQueryResults(h2, reqId, qrys.size(), mainCctx, lazy, qctx);
// qctx is set, we have to release reservations inside of it.
reserved = null;
if (distributedJoinCtx != null)
qryCtxRegistry.setShared(node.id(), reqId, qctx);
if (nodeRess.put(reqId, segmentId, qryResults) != null)
throw new IllegalStateException();
if (nodeRess.cancelled(reqId)) {
qryCtxRegistry.clearShared(node.id(), reqId);
nodeRess.cancelRequest(reqId);
throw new QueryCancelledException();
}
// Run queries.
int qryIdx = 0;
boolean evt = mainCctx != null && mainCctx.events().isRecordable(EVT_CACHE_QUERY_EXECUTED);
for (GridCacheSqlQuery qry : qrys) {
H2PooledConnection conn = h2.connections().connection(schemaName);
H2Utils.setupConnection(conn, qctx, distributedJoins, enforceJoinOrder, lazy);
MapQueryResult res = new MapQueryResult(h2, mainCctx, node.id(), qry, params, conn, log);
qryResults.addResult(qryIdx, res);
try {
res.lock();
// Ensure we are on the target node for this replicated query.
if (qry.node() == null || (segmentId == 0 && qry.node().equals(ctx.localNodeId()))) {
String sql = qry.query();
Collection<Object> params0 = F.asList(qry.parameters(params));
PreparedStatement stmt = conn.prepareStatement(sql, H2StatementCache.queryFlags(distributedJoins, enforceJoinOrder));
H2Utils.bindParameters(stmt, params0);
MapH2QueryInfo qryInfo = new MapH2QueryInfo(stmt, qry.query(), node.id(), qryId, reqId, segmentId);
ResultSet rs = h2.executeSqlQueryWithTimer(stmt, conn, sql, timeout, qryResults.queryCancel(qryIdx), dataPageScanEnabled, qryInfo);
if (evt) {
ctx.event().record(new CacheQueryExecutedEvent<>(node, "SQL query executed.", EVT_CACHE_QUERY_EXECUTED, CacheQueryType.SQL.name(), mainCctx.name(), null, qry.query(), null, null, params, node.id(), null));
}
assert rs instanceof JdbcResultSet : rs.getClass();
if (qryResults.cancelled()) {
rs.close();
throw new QueryCancelledException();
}
res.openResult(rs, qryInfo);
final GridQueryNextPageResponse msg = prepareNextPage(nodeRess, node, qryResults, qryIdx, segmentId, pageSize, dataPageScanEnabled);
if (msg != null)
sendNextPage(node, msg);
} else {
assert !qry.isPartitioned();
qryResults.closeResult(qryIdx);
}
qryIdx++;
} finally {
try {
res.unlockTables();
} finally {
res.unlock();
}
}
}
if (!lazy)
qryResults.releaseQueryContext();
} catch (Throwable e) {
if (qryResults != null) {
nodeRess.remove(reqId, segmentId, qryResults);
qryResults.close();
// If a query is cancelled before execution is started partitions have to be released.
if (!lazy || !qryResults.isAllClosed())
qryResults.releaseQueryContext();
} else
releaseReservations(qctx);
if (e instanceof QueryCancelledException)
sendError(node, reqId, e);
else {
SQLException sqlEx = X.cause(e, SQLException.class);
if (sqlEx != null && sqlEx.getErrorCode() == ErrorCode.STATEMENT_WAS_CANCELED)
sendQueryCancel(node, reqId);
else {
GridH2RetryException retryErr = X.cause(e, GridH2RetryException.class);
if (retryErr != null) {
final String retryCause = String.format("Failed to execute non-collocated query (will retry) [localNodeId=%s, rmtNodeId=%s, reqId=%s, " + "errMsg=%s]", ctx.localNodeId(), node.id(), reqId, retryErr.getMessage());
sendRetry(node, reqId, segmentId, retryCause);
} else {
QueryRetryException qryRetryErr = X.cause(e, QueryRetryException.class);
if (qryRetryErr != null)
sendError(node, reqId, qryRetryErr);
else {
if (e instanceof Error) {
U.error(log, "Failed to execute local query.", e);
throw (Error) e;
}
U.warn(log, "Failed to execute local query.", e);
sendError(node, reqId, e);
}
}
}
}
} finally {
if (reserved != null)
reserved.release();
if (trace != null)
trace.close();
if (performanceStatsEnabled) {
IoStatisticsHolder stat = IoStatisticsQueryHelper.finishGatheringQueryStatistics();
if (stat.logicalReads() > 0 || stat.physicalReads() > 0) {
ctx.performanceStatistics().queryReads(GridCacheQueryType.SQL_FIELDS, node.id(), reqId, stat.logicalReads(), stat.physicalReads());
}
}
}
}
use of org.apache.ignite.internal.processors.query.h2.H2PooledConnection in project ignite by apache.
the class QueryParser method parseH2.
/**
* Parse and split query if needed, cache either two-step query or statement.
*
* @param schemaName Schema name.
* @param qry Query.
* @param batched Batched flag.
* @param remainingAllowed Whether multiple statements are allowed.
* @return Parsing result.
*/
@SuppressWarnings("IfMayBeConditional")
private QueryParserResult parseH2(String schemaName, SqlFieldsQuery qry, boolean batched, boolean remainingAllowed) {
try (H2PooledConnection c = connMgr.connection(schemaName)) {
// For queries that are explicitly local, we rely on the flag specified in the query
// because this parsing result will be cached and used for queries directly.
// For other queries, we enforce join order at this stage to avoid premature optimizations
// (and therefore longer parsing) as long as there'll be more parsing at split stage.
boolean enforceJoinOrderOnParsing = (!qry.isLocal() || qry.isEnforceJoinOrder());
QueryContext qctx = QueryContext.parseContext(idx.backupFilter(null, null), qry.isLocal());
H2Utils.setupConnection(c, qctx, false, enforceJoinOrderOnParsing, false);
PreparedStatement stmt = null;
try {
stmt = c.prepareStatementNoCache(qry.getSql());
if (qry.isLocal() && GridSqlQueryParser.checkMultipleStatements(stmt))
throw new IgniteSQLException("Multiple statements queries are not supported for local queries.", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
GridSqlQueryParser.PreparedWithRemaining prep = GridSqlQueryParser.preparedWithRemaining(stmt);
Prepared prepared = prep.prepared();
if (GridSqlQueryParser.isExplainUpdate(prepared))
throw new IgniteSQLException("Explains of update queries are not supported.", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
// Get remaining query and check if it is allowed.
SqlFieldsQuery remainingQry = null;
if (!F.isEmpty(prep.remainingSql())) {
checkRemainingAllowed(remainingAllowed);
remainingQry = cloneFieldsQuery(qry).setSql(prep.remainingSql());
}
// Prepare new query.
SqlFieldsQuery newQry = cloneFieldsQuery(qry).setSql(prepared.getSQL());
final int paramsCnt = prepared.getParameters().size();
Object[] argsOrig = qry.getArgs();
Object[] args = null;
Object[] remainingArgs = null;
if (!batched && paramsCnt > 0) {
if (argsOrig == null || argsOrig.length < paramsCnt)
// Not enough parameters, but we will handle this later on execution phase.
args = argsOrig;
else {
args = Arrays.copyOfRange(argsOrig, 0, paramsCnt);
if (paramsCnt != argsOrig.length)
remainingArgs = Arrays.copyOfRange(argsOrig, paramsCnt, argsOrig.length);
}
} else
remainingArgs = argsOrig;
newQry.setArgs(args);
QueryDescriptor newQryDesc = queryDescriptor(schemaName, newQry);
if (remainingQry != null)
remainingQry.setArgs(remainingArgs);
final List<JdbcParameterMeta> paramsMeta;
try {
paramsMeta = H2Utils.parametersMeta(stmt.getParameterMetaData());
assert prepared.getParameters().size() == paramsMeta.size();
} catch (IgniteCheckedException | SQLException e) {
throw new IgniteSQLException("Failed to get parameters metadata", IgniteQueryErrorCode.UNKNOWN, e);
}
// Do actual parsing.
if (CommandProcessor.isCommand(prepared)) {
GridSqlStatement cmdH2 = new GridSqlQueryParser(false, log).parse(prepared);
QueryParserResultCommand cmd = new QueryParserResultCommand(null, cmdH2, false);
return new QueryParserResult(newQryDesc, queryParameters(newQry), remainingQry, paramsMeta, null, null, cmd);
} else if (CommandProcessor.isCommandNoOp(prepared)) {
QueryParserResultCommand cmd = new QueryParserResultCommand(null, null, true);
return new QueryParserResult(newQryDesc, queryParameters(newQry), remainingQry, paramsMeta, null, null, cmd);
} else if (GridSqlQueryParser.isDml(prepared)) {
QueryParserResultDml dml = prepareDmlStatement(newQryDesc, prepared);
return new QueryParserResult(newQryDesc, queryParameters(newQry), remainingQry, paramsMeta, null, dml, null);
} else if (!prepared.isQuery()) {
throw new IgniteSQLException("Unsupported statement: " + newQry.getSql(), IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
}
// Parse SELECT.
GridSqlQueryParser parser = new GridSqlQueryParser(false, log);
GridSqlQuery selectStmt = (GridSqlQuery) parser.parse(prepared);
List<Integer> cacheIds = parser.cacheIds();
Integer mvccCacheId = mvccCacheIdForSelect(parser.objectsMap());
// Calculate if query is in fact can be executed locally.
boolean loc = qry.isLocal();
if (!loc) {
if (parser.isLocalQuery())
loc = true;
}
// If this is a local query, check if it must be split.
boolean locSplit = false;
if (loc) {
GridCacheContext cctx = parser.getFirstPartitionedCache();
if (cctx != null && cctx.config().getQueryParallelism() > 1)
locSplit = true;
}
// Split is required either if query is distributed, or when it is local, but executed
// over segmented PARTITIONED case. In this case multiple map queries will be executed against local
// node stripes in parallel and then merged through reduce process.
boolean splitNeeded = !loc || locSplit;
String forUpdateQryOutTx = null;
String forUpdateQryTx = null;
GridCacheTwoStepQuery forUpdateTwoStepQry = null;
boolean forUpdate = GridSqlQueryParser.isForUpdateQuery(prepared);
// column to be able to lock selected rows further.
if (forUpdate) {
// We have checked above that it's not an UNION query, so it's got to be SELECT.
assert selectStmt instanceof GridSqlSelect;
// Check FOR UPDATE invariants: only one table, MVCC is there.
if (cacheIds.size() != 1)
throw new IgniteSQLException("SELECT FOR UPDATE is supported only for queries " + "that involve single transactional cache.");
if (mvccCacheId == null)
throw new IgniteSQLException("SELECT FOR UPDATE query requires transactional cache " + "with MVCC enabled.", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
// We need a copy because we are going to modify AST a bit. We do not want to modify original select.
GridSqlSelect selForUpdate = ((GridSqlSelect) selectStmt).copySelectForUpdate();
// Clear forUpdate flag to run it as a plain query.
selForUpdate.forUpdate(false);
((GridSqlSelect) selectStmt).forUpdate(false);
// Remember sql string without FOR UPDATE clause.
forUpdateQryOutTx = selForUpdate.getSQL();
GridSqlAlias keyCol = keyColumn(selForUpdate);
selForUpdate.addColumn(keyCol, true);
// Remember sql string without FOR UPDATE clause and with _key column.
forUpdateQryTx = selForUpdate.getSQL();
// Prepare additional two-step query for FOR UPDATE case.
if (splitNeeded) {
c.schema(newQry.getSchema());
forUpdateTwoStepQry = GridSqlQuerySplitter.split(c, selForUpdate, forUpdateQryTx, newQry.isCollocated(), newQry.isDistributedJoins(), newQry.isEnforceJoinOrder(), locSplit, idx, paramsCnt, log);
}
}
GridCacheTwoStepQuery twoStepQry = null;
if (splitNeeded) {
GridSubqueryJoinOptimizer.pullOutSubQueries(selectStmt);
c.schema(newQry.getSchema());
twoStepQry = GridSqlQuerySplitter.split(c, selectStmt, newQry.getSql(), newQry.isCollocated(), newQry.isDistributedJoins(), newQry.isEnforceJoinOrder(), locSplit, idx, paramsCnt, log);
}
List<GridQueryFieldMetadata> meta = H2Utils.meta(stmt.getMetaData());
QueryParserResultSelect select = new QueryParserResultSelect(selectStmt, twoStepQry, forUpdateTwoStepQry, meta, cacheIds, mvccCacheId, forUpdateQryOutTx, forUpdateQryTx);
return new QueryParserResult(newQryDesc, queryParameters(newQry), remainingQry, paramsMeta, select, null, null);
} catch (IgniteCheckedException | SQLException e) {
throw new IgniteSQLException("Failed to parse query. " + e.getMessage(), IgniteQueryErrorCode.PARSING, e);
} finally {
U.close(stmt, log);
}
}
}
use of org.apache.ignite.internal.processors.query.h2.H2PooledConnection in project ignite by apache.
the class SchemaManager method onCacheTypeCreated.
/**
* Registers new class description.
*
* @param cacheInfo Cache info.
* @param idx Indexing.
* @param type Type descriptor.
* @param isSql Whether SQL enabled.
* @throws IgniteCheckedException If failed.
*/
public void onCacheTypeCreated(GridCacheContextInfo cacheInfo, IgniteH2Indexing idx, GridQueryTypeDescriptor type, boolean isSql) throws IgniteCheckedException {
String schemaName = schemaName(cacheInfo.name());
H2TableDescriptor tblDesc = new H2TableDescriptor(idx, schemaName, type, cacheInfo, isSql);
H2Schema schema = schema(schemaName);
try (H2PooledConnection conn = connMgr.connection(schema.schemaName())) {
GridH2Table h2tbl = createTable(schema.schemaName(), schema, tblDesc, conn);
schema.add(tblDesc);
if (dataTables.putIfAbsent(h2tbl.identifier(), h2tbl) != null)
throw new IllegalStateException("Table already exists: " + h2tbl.identifierString());
} catch (SQLException e) {
throw new IgniteCheckedException("Failed to register query type: " + tblDesc, e);
}
}
Aggregations