zstan commented on code in PR #13366:
URL: https://github.com/apache/ignite/pull/13366#discussion_r3728218617


##########
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java:
##########
@@ -581,6 +598,290 @@ private FieldsQueryCursor<List<?>> 
executeDdl(RootQuery<Row> qry, DdlPlan plan)
         }
     }
 
+    /**
+     * Executes a {@code SELECT ... FOR UPDATE} plan.
+     *
+     * <ol>
+     *   <li>Validates that the current transaction is PESSIMISTIC.</li>
+     *   <li>Runs the inner SELECT with hidden key, value, and version columns 
and materialises all rows.</li>
+     *   <li>Builds cache entries from the hidden columns.</li>
+     *   <li>Creates a savepoint, acquires pessimistic locks via {@code 
lockTxEntries()},
+     *       and releases the savepoint on success (or rolls back on 
failure).</li>
+     *   <li>Repeats the SELECT and lock attempt after a concurrent version 
change while the deadline permits.</li>
+     *   <li>Returns a cursor with only the user-visible columns (the appended 
_KEY is stripped).</li>
+     * </ol>
+     */
+    private FieldsQueryCursor<List<?>> executeForUpdate(RootQuery<Row> qry, 
SelectForUpdatePlan plan) {
+        GridNearTxLocal userTx = Commons.queryTransaction(qry.context(), 
ctx.cache().context());
+
+        if (userTx == null || !userTx.pessimistic())
+            throw new IgniteSQLException(
+                
IgniteResource.INSTANCE.selectForUpdateRequiresPessimisticTx().str(),
+                IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+        long waitMs = waitMillis(plan);
+
+        // Zero means that retries are limited only by the transaction or 
query timeout.
+        long deadline = waitMs > 0
+            ? U.currentTimeMillis() + waitMs
+            : waitMs < 0 ? U.currentTimeMillis() : 0L;
+
+        RootQuery<Row> selectQry = qry;
+
+        while (true) {
+            FieldsQueryCursor<List<?>> cursor = tryExecuteForUpdate(selectQry, 
plan, userTx, waitMs, deadline);

Review Comment:
   i already wrote upper: let`\s simplify code a bit ? I see that "deadline" 
only helpful in this function if "waitMs > 0" - thus i think you can pass only 
"waitMs" here.



##########
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java:
##########
@@ -581,6 +598,290 @@ private FieldsQueryCursor<List<?>> 
executeDdl(RootQuery<Row> qry, DdlPlan plan)
         }
     }
 
+    /**
+     * Executes a {@code SELECT ... FOR UPDATE} plan.
+     *
+     * <ol>
+     *   <li>Validates that the current transaction is PESSIMISTIC.</li>
+     *   <li>Runs the inner SELECT with hidden key, value, and version columns 
and materialises all rows.</li>
+     *   <li>Builds cache entries from the hidden columns.</li>
+     *   <li>Creates a savepoint, acquires pessimistic locks via {@code 
lockTxEntries()},
+     *       and releases the savepoint on success (or rolls back on 
failure).</li>
+     *   <li>Repeats the SELECT and lock attempt after a concurrent version 
change while the deadline permits.</li>
+     *   <li>Returns a cursor with only the user-visible columns (the appended 
_KEY is stripped).</li>
+     * </ol>
+     */
+    private FieldsQueryCursor<List<?>> executeForUpdate(RootQuery<Row> qry, 
SelectForUpdatePlan plan) {
+        GridNearTxLocal userTx = Commons.queryTransaction(qry.context(), 
ctx.cache().context());
+
+        if (userTx == null || !userTx.pessimistic())
+            throw new IgniteSQLException(
+                
IgniteResource.INSTANCE.selectForUpdateRequiresPessimisticTx().str(),
+                IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+        long waitMs = waitMillis(plan);
+
+        // Zero means that retries are limited only by the transaction or 
query timeout.
+        long deadline = waitMs > 0
+            ? U.currentTimeMillis() + waitMs
+            : waitMs < 0 ? U.currentTimeMillis() : 0L;
+
+        RootQuery<Row> selectQry = qry;
+
+        while (true) {
+            FieldsQueryCursor<List<?>> cursor = tryExecuteForUpdate(selectQry, 
plan, userTx, waitMs, deadline);
+
+            if (cursor != null)
+                return cursor;
+
+            if (deadline != 0 && U.currentTimeMillis() >= deadline) {
+                throw new IgniteSQLException(
+                    IgniteResource.INSTANCE.selectForUpdateLockFailed().str(),
+                    IgniteQueryErrorCode.CONCURRENT_UPDATE);
+            }
+
+            // The previous query has already been closed after 
materialisation, so retry with a fresh root query.
+            selectQry = qry.retryQuery();

Review Comment:
   misunderstand here - if we already obtain materialisation = results ? then - 
cursor will return a bit upper. Seems here you need to write smth. like : "in 
case of failed lock acquisition ... " and seems all we need here is just to 
reset RootNode.state ? Use a constructor is ok but let`\s rename method like: 
   selectQry = qry.clone(); ?



##########
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/SelectForUpdatePlan.java:
##########
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite.prepare;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.ignite.cache.CacheEntry;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Query plan for {@code SELECT ... FOR UPDATE} statements.
+ *
+ * <p>Wraps an inner {@link MultiStepQueryPlan} whose SELECT list has {@code 
_KEY}, {@code _VAL},
+ * and {@code _VER} columns of the tables participating in the query appended 
at the end. At execution time
+ * the executor:
+ * <ol>
+ *   <li>Runs the inner plan and materialises the full result set.</li>
+ *   <li>Extracts the hidden columns described by {@link #lockTargets} from 
every row.</li>
+ *   <li>Creates {@link CacheEntry} to lock in the current transaction.</li>
+ *   <li>Calls {@code cache.lockTxEntries(entries, waitMs)} to acquire 
pessimistic locks.</li>
+ *   <li>Repeats the inner plan when a selected entry version changes before 
locking.</li>
+ *   <li>Returns only the first {@link #userColumnCount} columns to the 
caller.</li>
+ * </ol>
+ */
+public class SelectForUpdatePlan extends AbstractQueryPlan {
+    /** Inner plan: SELECT with _KEY, _VAL, and _VER columns appended at the 
end. */
+    private final MultiStepQueryPlan innerPlan;
+
+    /** Number of user-visible result columns (total inner columns minus the 
appended _KEY, _VAL, and _VER). */
+    private final int userColumnCount;
+
+    /**
+     * Lock-wait limit from the {@code FOR UPDATE} clause:
+     * {@code null} = use transaction timeout, {@code 0L} = NOWAIT,
+     * positive value = WAIT n seconds.
+     */
+    @Nullable private final Long waitSeconds;
+
+    /** Tables whose rows must be locked and positions of their hidden columns 
in the inner result. */
+    private final List<LockTarget> lockTargets;
+
+    /** */
+    public SelectForUpdatePlan(
+        MultiStepQueryPlan innerPlan,
+        int userColumnCount,
+        @Nullable Long waitSeconds,
+        List<LockTarget> lockTargets
+    ) {
+        super(innerPlan.query());
+
+        this.innerPlan = innerPlan;
+        this.userColumnCount = userColumnCount;
+        this.waitSeconds = waitSeconds;
+        this.lockTargets = Collections.unmodifiableList(new 
ArrayList<>(lockTargets));

Review Comment:
   ```suggestion
           this.lockTargets = List.copyOf(lockTargets);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to