What happened?
Beam version: 2.75.0 through current master (2.77.0-SNAPSHOT). SDK: Go. Filesystem: GCS (gs://).
Summary
fileio.MatchAll fails to return existing GCS objects when the glob pattern contains non-ASCII (multi-byte) characters. When empty matches are allowed, it returns no error at all; the affected objects are simply missing from the results.
This is a regression in 2.75.0 (#38099); the same patterns worked correctly in 2.74.0 and earlier.
Examples
A bucket gs://my-bucket containing:
gs://my-bucket/
├── singlebyte/
│ ├── english1.txt
│ └── english2.txt
├── multibyte/
│ ├── japanese.txt
│ └── 日本語.txt
└── multibytepath_é/
├── singlebyte_file1.txt
└── singlebyte_file2.txt
patterns := beam.Create(s,
"gs://my-bucket/singlebyte/*.txt", // all ASCII → OK
"gs://my-bucket/multibyte/*.txt", // ASCII pattern, non-ASCII object names → OK
"gs://my-bucket/multibyte/日本語.txt", // non-ASCII file name → broken
"gs://my-bucket/multibytepath_é/*.txt", // non-ASCII directory → broken
"gs://my-bucket/multibytepath_é/singlebyte_file1.txt", // non-ASCII directory, exact path → broken
)
matched := fileio.MatchAll(s, patterns) // PCollection<fileio.FileMetadata>
glob passed to MatchAll |
returned on 2.74 |
returned on 2.75+ |
.../singlebyte/*.txt |
english1.txt, english2.txt |
english1.txt, english2.txt |
.../multibyte/*.txt |
japanese.txt, 日本語.txt |
japanese.txt, 日本語.txt |
.../multibyte/日本語.txt |
日本語.txt |
(none) |
.../multibytepath_é/*.txt |
singlebyte_file1.txt, singlebyte_file2.txt |
(none) |
.../multibytepath_é/singlebyte_file1.txt |
singlebyte_file1.txt |
(none) |
When empty matches are allowed, no error is raised at all, so the missing objects are a silent data loss.
Root cause (summary)
globToRegex() in sdks/go/pkg/beam/io/filesystem/gcs/gcs.go iterates over the glob pattern byte by byte instead of rune by rune. In its default branch, it passes each byte c to regexp.QuoteMeta(string(c)).
For a multi-byte UTF-8 character, converting each byte separately with string(c) changes its encoding. As a result, globToRegex() produces a corrupted regular expression that does not match the original GCS object name.
This behavior was introduced by #38099 in version 2.75.0. Earlier versions used the rune-aware filepath.Match, so version 2.74.0 and earlier are unaffected.
Issue Priority
Priority: 2 (default / most bugs should be filed as P2)
Issue Components
What happened?
Beam version: 2.75.0 through current
master(2.77.0-SNAPSHOT). SDK: Go. Filesystem: GCS (gs://).Summary
fileio.MatchAllfails to return existing GCS objects when the glob pattern contains non-ASCII (multi-byte) characters. When empty matches are allowed, it returns no error at all; the affected objects are simply missing from the results.This is a regression in 2.75.0 (#38099); the same patterns worked correctly in 2.74.0 and earlier.
Examples
A bucket
gs://my-bucketcontaining:MatchAll.../singlebyte/*.txt.../multibyte/*.txt.../multibyte/日本語.txt.../multibytepath_é/*.txt.../multibytepath_é/singlebyte_file1.txtWhen empty matches are allowed, no error is raised at all, so the missing objects are a silent data loss.
Root cause (summary)
globToRegex()insdks/go/pkg/beam/io/filesystem/gcs/gcs.goiterates over the glob pattern byte by byte instead of rune by rune. In itsdefaultbranch, it passes each bytectoregexp.QuoteMeta(string(c)).For a multi-byte UTF-8 character, converting each byte separately with
string(c)changes its encoding. As a result,globToRegex()produces a corrupted regular expression that does not match the original GCS object name.This behavior was introduced by #38099 in version 2.75.0. Earlier versions used the rune-aware
filepath.Match, so version 2.74.0 and earlier are unaffected.Issue Priority
Priority: 2 (default / most bugs should be filed as P2)
Issue Components