imbajin commented on code in PR #833:
URL: 
https://github.com/apache/hugegraph-toolchain/pull/833#discussion_r4350417987


##########
hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/util/ReadOnlyGremlin.java:
##########
@@ -65,13 +143,183 @@ public static void validate(String query) {
         if (!"V".equals(start) && !"E".equals(start)) {
             throw invalid();
         }
-        parser.arguments(0);
-        parser.chain(STEPS, 0);
+        List<Step> steps = new ArrayList<>();
+        steps.add(new Step(start, parser.arguments(0), 0));
+        parser.chain(STEPS.keySet(), 0, steps);
         parser.consume(';');
         parser.whitespace();
         if (parser.position != query.length()) {
             throw invalid();
         }
+        return new Traversal(steps, query.length());
+    }
+
+    private enum Output {
+        PRESERVE, EXPAND, REDUCE, COLLECT, LIMIT, CONSUME
+    }
+
+    static final class Traversal {
+
+        private final List<Step> steps;
+        private final int endPosition;
+
+        private Traversal(List<Step> steps, int endPosition) {
+            this.steps = List.copyOf(steps);
+            this.endPosition = endPosition;
+        }
+
+        int limitPosition(boolean preview, int limit) {
+            // Preview limits include the sentinel row used to report 
truncation.
+            BigInteger previewSize = BigInteger.valueOf((long) limit - 1);
+            Analysis result = this.analyze(previewSize);
+            if (preview && result.largeCollection) {
+                throw new 
ExternalException("gremlin.preview.collection_requires_async", previewSize);
+            }
+            return !result.singleResult && (preview || result.bound == null) ? 
result.position : -1;
+        }
+
+        private Analysis analyze(BigInteger previewSize) {
+            BigInteger bound = null;
+            boolean singleResult = false;
+            boolean largeCollection = false;
+            boolean nestedCollection = false;
+            int position = this.endPosition;
+            for (int i = 0; i < this.steps.size(); i++) {
+                Step step = this.steps.get(i);
+                switch (STEPS.get(step.name)) {
+                    case CONSUME:
+                        // Cap the traversal before its final consumer, 
preserving
+                        // the List/Set/value/boolean result type. Further 
methods
+                        // would operate on materialized results, outside this 
grammar.
+                        if (i != this.steps.size() - 1) {
+                            return new Analysis(-1, bound, true, false);
+                        }
+                        position = step.dotPosition;
+                        if ("hasNext".equals(step.name)) {
+                            largeCollection = false;
+                            nestedCollection = false;
+                        }
+                        break;
+                    case EXPAND:
+                        bound = null;
+                        singleResult = false;
+                        largeCollection = false;
+                        break;
+                    case REDUCE:
+                        largeCollection = false;
+                        nestedCollection = false;
+                        if (!step.localScope()) {
+                            bound = BigInteger.ONE;
+                            singleResult = true;
+                        }
+                        break;
+                    case COLLECT:
+                        // The keyed group overloads accumulate a side effect
+                        // while passing each input traverser through.
+                        if (("group".equals(step.name) || 
"groupCount".equals(step.name)) &&
+                            !step.arguments.isEmpty()) {
+                            break;
+                        }
+                        if (!step.localScope()) {
+                            // A single List/Map can still download an entire
+                            // graph. A later outer limit cannot shrink its 
input.
+                            largeCollection |= bound == null || 
bound.compareTo(previewSize) > 0;
+                            // group() values are collections; unfolding its 
Map
+                            // returns entries that retain those complete 
values.
+                            nestedCollection |= "group".equals(step.name) && 
largeCollection;

Review Comment:
   The specific double-fold counterexample reported here is repaired at 
7a6f3af6672d1c5f7ea4dae57820b117daad17c5. Independent bounded 
parser/TinkerGraph checks confirmed rejection while preserving the checked safe 
controls. Resolving this specific finding does not establish overall preview 
completeness, current CI success, or full acceptance; the broader review 
remains incomplete.



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to