OIiveirra commented on code in PR #69527:
URL: https://github.com/apache/doris/pull/69527#discussion_r4367402592
##########
fe/fe-core/src/main/java/org/apache/doris/tablefunction/LocalTableValuedFunction.java:
##########
@@ -135,6 +143,38 @@ private void getFileListFromBackend() throws
AnalysisException {
}
}
+ /** Apply LOCAL admission to sinks that select the built-in local JNI
writer as well. */
+ public static void checkSinkEnabled(String tvfName, Map<String, String>
properties) {
+ String writerClass = properties.getOrDefault("writer_class", "");
+ // JNI consumes a C string, and ScannerLoader accepts slash-separated
class names.
+ int nul = writerClass.indexOf('\0');
+ if (nul >= 0) {
+ writerClass = writerClass.substring(0, nul);
+ }
+ if ("local".equalsIgnoreCase(tvfName)
+ ||
"org.apache.doris.writer.LocalFileJniWriter".equals(writerClass.replace('/',
'.'))) {
Review Comment:
The remaining consumer-identity variants are addressed in
d1b3962540b16751ca205419f022d8cbfcb082f7. One normalized admission module
distinguishes native output paths/properties from Java cleanup. It covers
default authority aliases, explicit resources, VDFS/runtime delegation, raw
convert_path ordering before Hadoop parsing, non-prefix NameNode ambiguity, and
release relative/trimmed Java cleanup paths. The actual cleanup path is checked
before filesystem creation.
The current revision passed 86 FE and 22 BE tests. Real bundled Hadoop and
storage-normalizer oracles validate path/property identity; cleanup tests
assert zero filesystem/executor interactions and perform no real deletion.
Explicit remote providers and valid native relative-path controls remain
covered.
##########
be/src/format_v2/table/lance_reader.cpp:
##########
@@ -35,11 +39,79 @@
#include "format_v2/lance/lance_reader_helper.h"
#include "format_v2/lance/lance_runtime_filter_helper.h"
#include "format_v2/lance/lance_session_manager.h"
+#include "io/fs/local_file_system.h"
#include "runtime/file_scan_profile.h"
#include "runtime/runtime_state.h"
namespace doris::format::lance {
+namespace {
+std::string expand_home_path(const std::string& path) {
+ if (path != "~" && !path.starts_with("~/")) {
+ return path;
+ }
+ // Match the SDK's std::env::home_dir: HOME first, then the current user's
passwd entry.
+ std::string home;
+ if (const char* value = std::getenv("HOME"); value != nullptr && value[0]
!= '\0') {
+ home = value;
+ } else {
+ std::vector<char> buffer(1024);
+ struct passwd entry {};
+ struct passwd* result = nullptr;
+ int error;
+ while ((error = getpwuid_r(getuid(), &entry, buffer.data(),
buffer.size(), &result)) ==
+ ERANGE) {
+ buffer.resize(buffer.size() * 2);
+ }
+ if (error == 0 && result != nullptr && entry.pw_dir != nullptr) {
+ home = entry.pw_dir;
+ }
+ }
+ if (home.empty()) {
+ return path;
+ }
+ return path == "~" ? home : (io::Path(home) / path.substr(2)).string();
+}
+
+Status resolve_dataset_uri(const TFileRangeDesc& range, const
TFileScanRangeParams& scan_params,
+ std::string* uri) {
+ const auto& requested_uri =
range.table_format_params.lance_params.dataset_uri;
+ // Modern plans tag each range; older plans put the type in the scan
parameters.
+ // Untagged catalog scans retain their original storage-provider selection.
+ const bool is_local = range.__isset.file_type
+ ? range.file_type == TFileType::FILE_LOCAL
+ : scan_params.__isset.file_type &&
+ scan_params.file_type ==
TFileType::FILE_LOCAL;
+ // LOCAL TVF uses version zero for both schema discovery and whole-dataset
scans.
+ // Catalog scans pin a positive snapshot version, including file://
catalogs whose
+ // operator-configured warehouse is independent of the LOCAL TVF secure
directory.
+ if (is_local && range.table_format_params.lance_params.version == 0) {
+ // LOCAL Lance bypasses glob because it opens a dataset directory.
Apply the same
+ // canonical secure-root check before either schema discovery or a
cached dataset open.
+ std::string local_input = requested_uri;
+ const auto colon = requested_uri.find(':');
+ const auto slash = requested_uri.find('/');
+ const bool bare_path =
+ colon == std::string::npos || (slash != std::string::npos &&
slash < colon);
+ if (bare_path && !requested_uri.empty() && requested_uri.find('\0') ==
std::string::npos &&
+ !config::user_files_secure_path.empty()) {
+ // Lance's PathAbs fallback resolves bare dot segments before
following symlinks.
+ // Anchor relative TVF paths to the canonical secure root, not the
process cwd.
+ std::string secure_root;
+ RETURN_IF_ERROR(io::global_local_filesystem()->canonicalize(
+ config::user_files_secure_path, &secure_root));
+ local_input = (io::Path(secure_root) /
expand_home_path(requested_uri))
+ .lexically_normal()
+ .string();
+ }
+ return io::global_local_filesystem()->canonicalize_local_file(
Review Comment:
The manifest-reference gap remaining after the earlier descendant walk is
addressed in d1b3962540b16751ca205419f022d8cbfcb082f7. Physical-tree checks
remain, and SDK metadata now validates every registered manifest base before
TVF schema/delegated reads. LOCAL external bases must remain inside the secure
root; remote TVFs reject local bases. Validation includes
dataset-root/direct-directory bases and cached/session reuse, without changing
unmarked fixed-version catalog policy.
Official SDK-generated shallow clones reject out-of-root source bases and
return actual rows 1–4 when wholly in root. All 86 FE and 22 BE tests passed;
isolated S3 negative/positive tests execute without skips. Matching Lance
header/archive requirements and mixed-version limits are documented in the PR.
##########
fe/fe-core/src/main/java/org/apache/doris/tablefunction/S3TableValuedFunction.java:
##########
@@ -63,6 +76,11 @@ public S3TableValuedFunction(Map<String, String> properties,
List<TBrokerFileSta
} catch (UserException e) {
throw new RuntimeException(e);
}
+ // Validate the final URI, including when BrokerProperties passes it
through unchanged.
+ // A local dataset must not bypass the LOCAL switch, privileges or BE
secure directory.
+ if (isLanceFormat() && !isRemoteLanceUri(filePath)) {
Review Comment:
Fixed in d1b3962540b16751ca205419f022d8cbfcb082f7. Java TVF metadata loading
inspects manifest bases immediately after opening the primary snapshot and
before BASIC metadata use. BE TVFs use the new metadata-only Lance C API to
inspect SDK-decoded primary and every registered base before
schema/scanner/index/row-id work: remote TVFs reject local bases; LOCAL bases
must canonicalize inside the secure root and their reachable trees are checked.
Both dataset-root and direct-data-directory bases are covered. Policy is part
of the dataset key and is enforced on cached/reused handles; unmarked
fixed-version catalogs retain their configured policy.
All 86 selected FE and 22 selected BE tests passed with zero
failures/errors/skips. Official SDK-generated fixtures prove outside-root
rejection and actual in-root rows 1–4. Isolated S3 tests execute Java
rejection, BE remote/local-base rejection and actual remote rows 1–4. The C
ABI/SDK tests and matching header/archive checks passed. Complete enforcement
requires upgraded serving FE/BE versions; the PR describes the optional TVF
marker and mixed-version boundary.
--
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]