public async Task <IQueryResult <T> > QueryAsync <T>(string statement, QueryOptions options) { if (string.IsNullOrEmpty(options.CurrentContextId)) { options.ClientContextId(Guid.NewGuid().ToString()); } // does this query use a prepared plan? if (options.IsAdHoc) { // don't use prepared plan, execute query directly options.Statement(statement); return(await ExecuteQuery <T>(options, options.Serializer ?? _serializer).ConfigureAwait(false)); } // try find cached query plan if (_queryCache.TryGetValue(statement, out var queryPlan)) { // if an upgrade has happened, don't use query plans that have an encoded plan if (!EnhancedPreparedStatementsEnabled || string.IsNullOrWhiteSpace(queryPlan.EncodedPlan)) { // plan is valid, execute query with it options.Prepared(queryPlan, statement); return(await ExecuteQuery <T>(options, options.Serializer ?? _serializer).ConfigureAwait(false)); } // entry is stale, remove from cache _queryCache.TryRemove(statement, out _); } // create prepared statement var prepareStatement = statement; if (!prepareStatement.StartsWith("PREPARE ", StringComparison.InvariantCultureIgnoreCase)) { prepareStatement = $"PREPARE {statement}"; } // set prepared statement options.Statement(prepareStatement); // server supports combined prepare & execute if (EnhancedPreparedStatementsEnabled) { // execute combined prepare & execute query options.AutoExecute(true); var result = await ExecuteQuery <T>(options, options.Serializer ?? _serializer).ConfigureAwait(false); // add/replace query plan name in query cache if (result is StreamingQueryResult <T> streamingResult) // NOTE: hack to not make 'PreparedPlanName' property public { var plan = new QueryPlan { Name = streamingResult.PreparedPlanName, Text = statement }; _queryCache.AddOrUpdate(statement, plan, (k, p) => plan); } return(result); } // older style, prepare then execute var preparedResult = await ExecuteQuery <QueryPlan>(options, _queryPlanSerializer).ConfigureAwait(false); queryPlan = await preparedResult.FirstAsync().ConfigureAwait(false); // add plan to cache and execute _queryCache.TryAdd(statement, queryPlan); options.Prepared(queryPlan, statement); // execute query using plan return(await ExecuteQuery <T>(options, options.Serializer ?? _serializer).ConfigureAwait(false)); }
public async Task <IQueryResult <T> > QueryAsync <T>(string statement, QueryOptions options) { //It's possible to reuse the queryoptions which may cause odd threading behaviour //So we'll clone it if it has already been used options = options.CloneIfUsedAlready(); if (string.IsNullOrEmpty(options.CurrentContextId)) { options.ClientContextId(Guid.NewGuid().ToString()); } using var rootSpan = RootSpan(OuterRequestSpans.ServiceSpan.N1QLQuery, options) .WithOperationId(options) .WithStatement(statement) .WithLocalAddress(); // does this query use a prepared plan? if (options.IsAdHoc) { // don't use prepared plan, execute query directly options.Statement(statement); return(await ExecuteQuery <T>(options, options.Serializer ?? _serializer, rootSpan).ConfigureAwait(false)); } // try find cached query plan if (_queryCache.TryGetValue(statement, out var queryPlan)) { // if an upgrade has happened, don't use query plans that have an encoded plan if (!EnhancedPreparedStatementsEnabled || string.IsNullOrWhiteSpace(queryPlan.EncodedPlan)) { using var prepareAndExecuteSpan = _tracer.RequestSpan(OuterRequestSpans.ServiceSpan.Internal.PrepareAndExecute, rootSpan); // plan is valid, execute query with it options.Prepared(queryPlan, statement); return(await ExecuteQuery <T>(options, options.Serializer ?? _serializer, rootSpan).ConfigureAwait(false)); } // entry is stale, remove from cache _queryCache.TryRemove(statement, out _); } // create prepared statement var prepareStatement = statement; if (!prepareStatement.StartsWith("PREPARE ", StringComparison.InvariantCultureIgnoreCase)) { prepareStatement = $"PREPARE {statement}"; } // set prepared statement options.Statement(prepareStatement); // server supports combined prepare & execute if (EnhancedPreparedStatementsEnabled) { _logger.LogDebug("Using enhanced prepared statement behavior for request {currentContextId}", options.CurrentContextId); // execute combined prepare & execute query options.AutoExecute(true); var result = await ExecuteQuery <T>(options, options.Serializer ?? _serializer, rootSpan).ConfigureAwait(false); // add/replace query plan name in query cache if (result is StreamingQueryResult <T> streamingResult) // NOTE: hack to not make 'PreparedPlanName' property public { var plan = new QueryPlan { Name = streamingResult.PreparedPlanName, Text = statement }; _queryCache.AddOrUpdate(statement, plan, (k, p) => plan); } return(result); } _logger.LogDebug("Using legacy prepared statement behavior for request {currentContextId}", options.CurrentContextId); // older style, prepare then execute var preparedResult = await ExecuteQuery <QueryPlan>(options, _queryPlanSerializer, rootSpan).ConfigureAwait(false); queryPlan = await preparedResult.FirstAsync().ConfigureAwait(false); // add plan to cache and execute _queryCache.TryAdd(statement, queryPlan); options.Prepared(queryPlan, statement); // execute query using plan return(await ExecuteQuery <T>(options, options.Serializer ?? _serializer, rootSpan).ConfigureAwait(false)); }