github-actions[bot] commented on code in PR #69385:
URL: https://github.com/apache/doris/pull/69385#discussion_r4335152775


##########
fe/fe-filesystem/fe-filesystem-spi/src/main/java/org/apache/doris/filesystem/spi/S3CompatibleFileSystem.java:
##########
@@ -1140,6 +1178,53 @@ public static Pattern compileGlobPattern(String 
keyPattern) {
         return 
Pattern.compile(globToRegex(GlobPattern.expandNumericRanges(keyPattern)));
     }
 
+    private static List<String> expandDeterministicKeyPattern(String pattern, 
int maxPaths) {
+        // Keep the original deterministic-path boundary, including escaped 
glob literals.
+        if (maxPaths <= 0 || pattern.isEmpty() || 
pattern.endsWith(DIR_MARKER_SUFFIX)
+                || pattern.indexOf('*') >= 0 || pattern.indexOf('?') >= 0 || 
pattern.indexOf('\\') >= 0) {
+            return null;
+        }
+        List<String> expanded = expandGlobListPrefixes(pattern, false, 
maxPaths);
+        if (expanded == null || expanded.stream().anyMatch(key -> 
key.isEmpty() || key.endsWith(DIR_MARKER_SUFFIX))) {
+            return null;
+        }
+        List<String> keys = new ArrayList<>(new LinkedHashSet<>(expanded));
+        keys.sort(UTF8_BINARY_ORDER);
+        return keys;
+    }
+
+    private RemoteObject headObjectIfPresent(String path) throws IOException {
+        try {
+            return objStorage.headObject(path);
+        } catch (IOException e) {
+            if (isNotFoundError(e)) {
+                return null;
+            }
+            throw e;
+        }
+    }
+
+    private GlobListing globListDeterministicKeys(String bucket, String 
patternPrefix, String base,
+            List<String> keys) throws IOException {
+        List<FileEntry> files = new ArrayList<>();
+        String lastMatchedKey = "";
+        for (String key : keys) {
+            RemoteObject obj = headObjectIfPresent(base + key);
+            if (obj == null) {
+                continue;
+            }
+            String objectKey = obj.getKey();

Review Comment:
   [P2] Preserve the glob matcher when emitting HEAD results
   
   The prefix expander can produce a superset of the keys accepted by the 
existing matcher. For `s3://bucket/dir/file[ab&&bc].csv`, `globToRegex` 
preserves [Java's character-class 
intersection](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html)
 (only `fileb.csv` matches), but expansion produces `filea.csv`, `fileb.csv`, 
`file&.csv` and `filec.csv`. This helper returns every successful HEAD without 
applying the matcher, so `BrokerLoadPendingTask` (null cursor, zero limits) can 
now import a/c files that LIST excluded. Fall back for unsupported class syntax 
or validate expanded/returned keys against the same matcher before emitting 
entries.



##########
fe/fe-core/src/main/java/org/apache/doris/fs/FileSystemFactory.java:
##########
@@ -165,13 +184,23 @@ public static 
List<org.apache.doris.filesystem.properties.StorageProperties> bin
      */
     public static org.apache.doris.filesystem.FileSystem getFileSystem(
             FileSystemProperties spiProperties) throws IOException {
+        return getFileSystemWithEffectiveProperties(spiProperties, 
withRuntimeFileSystemProperties(
+                spiProperties.rawProperties(),
+                Config.s3_skip_list_for_deterministic_path, 
Config.s3_head_request_max_paths));
+    }
+
+    static org.apache.doris.filesystem.FileSystem 
getFileSystemWithEffectiveProperties(
+            FileSystemProperties spiProperties, Map<String, String> 
effectiveProperties) throws IOException {
         String providerName = spiProperties.providerName();
         FileSystemPluginManager mgr = pluginManager;
         List<FileSystemProvider> providers = mgr != null ? mgr.getProviders() 
: getProviders();
         for (FileSystemProvider provider : providers) {
             if (provider.name().equalsIgnoreCase(providerName)) {
                 try {
-                    return provider.createUntyped(spiProperties);
+                    // Rebind through the original provider so runtime 
controls do not change routing.
+                    FileSystemProperties effective = spiProperties instanceof 
S3CompatibleFileSystemProperties
+                            ? provider.bind(effectiveProperties) : 
spiProperties;

Review Comment:
   [P2] Preserve case-insensitive properties when rebinding
   
   `S3SourceOffsetProvider.getNextOffset()` and `fetchRemoteMeta()` 
intentionally bind a `TreeMap(String.CASE_INSENSITIVE_ORDER)` before creating 
the filesystem. With `fs.s3.support=true`, a private `s3.endpoint`, and 
`S3.REGION=us-east-1`, the initial binding contains a usable region. Its 
`rawProperties()` snapshot and this runtime overlay are `HashMap`s, however, so 
the new `provider.bind(effectiveProperties)` ignores `S3.REGION` and throws 
`Region is not set`. Those metadata/offset calls previously created the 
filesystem from the bound region; they now fail before LIST, even for positive 
limits or disabled HEAD. Preserve the lookup semantics or apply runtime 
controls without discarding bound values.



##########
fe/fe-filesystem/fe-filesystem-spi/src/main/java/org/apache/doris/filesystem/spi/S3CompatibleFileSystem.java:
##########
@@ -1151,11 +1236,28 @@ public GlobListing globListWithLimit(Location path, 
String startAfter, long maxB
         String base = uriBase(uri, parsed);
 
         String expandedKeyPattern = 
GlobPattern.expandNumericRanges(keyPattern);
+        Pattern matcher = Pattern.compile(globToRegex(expandedKeyPattern));
+        // Preserve #60414's eligibility: path-style addressing and 
incremental/limited reads
+        // must keep LIST semantics. Even an empty, non-null cursor selects 
the LIST path.
+        if (skipListForDeterministicPath && !usePathStyle
+                && maxBytes <= 0 && maxFiles <= 0 && startAfter == null) {

Review Comment:
   [P1] Route initial TVF listing requests through the HEAD fast path
   
   `ExternalFileTableValuedFunction.parseFile()` still calls 
`globListWithLimit(Location.of(path), "", 0, 0)` (line 190), so `startAfter == 
null` excludes every normal `S3(...)` TVF call, including the regression added 
here. With object reads/HEAD allowed and ListBucket denied, exact and brace 
paths still execute LIST and fail with the same 403 this PR is intended to fix. 
Keep cursor-based LIST semantics, but pass null from the initial TVF call and 
cover that production call chain.



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