mbutrovich commented on code in PR #7221:
URL: https://github.com/apache/datafusion-comet/pull/7221#discussion_r4391422263


##########
spark/src/main/scala/org/apache/comet/objectstore/AbfsAuthResolver.scala:
##########
@@ -467,6 +470,57 @@ private[comet] object AbfsAuthResolver extends Logging {
         Markers.ERROR_MESSAGE -> message)
   }
 
+  /**
+   * Why the native store cannot read `uri` under `outcome`, or None when it 
can: a mechanism
+   * Hadoop named that runs Java code per request, a resolution that failed, 
or resolved values
+   * the native store rejects. All are known on the driver, so the scan falls 
back to Spark's own
+   * reader, which authenticates through hadoop-azure, instead of planning 
natively and failing
+   * every task. For `Resolved`, native itself answers (see 
[[nativeStoreError]]), so the rules
+   * live in one place.
+   */
+  def nativeFallbackReason(uri: URI, outcome: Outcome): Option[String] = {
+    // The container and host only: a password in the user info stays out of 
the reason.
+    val authority = Option(uri.getRawAuthority).getOrElse("")
+    val at = authority.lastIndexOf('@')
+    val safeAuthority =
+      if (at < 0) authority
+      else authority.substring(0, at).takeWhile(_ != ':') + 
authority.substring(at)
+    val location = s"${uri.getScheme}://$safeAuthority"
+    outcome match {
+      case Declined(authType, providerClass, assertionProviderClass) =>
+        val classes = providerClass.map(c => s" with provider 
$c").getOrElse("") +
+          assertionProviderClass.map(c => s" and client assertion provider 
$c").getOrElse("")
+        Some(
+          s"Native scan does not support ABFS authentication auth type 
$authType$classes " +
+            s"for $location")
+      case resolved: Resolved =>
+        // The message names option keys, never their values.
+        nativeStoreError(s"$location/", toOptions(resolved)).map(message =>
+          s"Native scan cannot use the ABFS authentication for $location: 
$message")
+      case Failed(_, message) =>
+        // Some of Hadoop's own messages quote the whole URI, user info 
included.
+        val safeMessage =
+          if (authority.isEmpty) message else message.replace(authority, 
safeAuthority)
+        Some(s"Hadoop ABFS authentication failed on the driver for $location: 
$safeMessage")
+      case Unconfigured => None
+    }
+  }
+
+  /**
+   * Why native cannot build the store for `url` from `options`, from the same 
code the scan runs.
+   * When native cannot be asked (the library is not loaded), this assumes it 
can: such a build
+   * cannot run the native scan anyway.
+   */
+  private def nativeStoreError(url: String, options: Map[String, String]): 
Option[String] = {
+    val (keys, values) = options.toArray.unzip
+    try Option(NativeBase.abfsStoreError(url, keys, values))
+    catch {
+      case e @ (NonFatal(_) | _: LinkageError) =>
+        logDebug(s"Could not ask native whether it can build the ABFS store 
for $url", e)
+        None
+    }

Review Comment:
   The doc comment justifies assuming the store builds when the library is not 
loaded. That is the `LinkageError` case. `NonFatal(_)` also matches the 
`CometNativeException` that 
[`try_unwrap_or_throw`](https://github.com/apache/datafusion-comet/blob/beda9508f907a6b00bf94bb30c265b7b95295e65/native/jni-bridge/src/errors.rs#L1076-L1079)
 throws when `create_store` panics. As I read it, the scan then plans natively 
and the same panic fails every task, and the only trace on the driver is a 
`logDebug`.
   
   Should only `LinkageError` assume the store builds, with any other failure 
becoming a fallback reason such as "could not ask native whether it can build 
the ABFS store: ..."? Falling back is the safe direction, and the reason would 
show up in the explain output. A test for each case would also help: neither 
the library-not-loaded path nor a native failure is covered today.



##########
native/core/src/lib.rs:
##########
@@ -319,6 +320,35 @@ pub extern "system" fn 
Java_org_apache_comet_NativeBase_icebergStorageSchemes(
     })
 }
 
+/// JNI: why the native scan cannot build its ABFS store for `url` from the 
object store options
+/// `keys`/`values`, or null when it can. The driver asks this when it plans a 
scan whose ABFS
+/// authentication resolved, so a store native would reject falls back to 
Spark instead of every
+/// task failing. See `azure::store_error`.
+#[no_mangle]
+pub extern "system" fn Java_org_apache_comet_NativeBase_abfsStoreError(
+    env: EnvUnowned,
+    _: JClass,
+    url: JString,
+    keys: JObjectArray,
+    values: JObjectArray,
+) -> jstring {
+    try_unwrap_or_throw(&env, |env| {
+        let url: String = url.try_to_string(env)?;
+        let mut configs = HashMap::new();
+        for i in 0..keys.len(env)? {
+            let key = keys.get_element(env, i)?;
+            let key = unsafe { JString::from_raw(&*env, key.into_raw()) 
}.try_to_string(env)?;
+            let value = values.get_element(env, i)?;
+            let value = unsafe { JString::from_raw(&*env, value.into_raw()) 
}.try_to_string(env)?;
+            configs.insert(key, value);

Review Comment:
   What do you think about typing the two arrays as `JObjectArray<JString>`? In 
jni 0.22, `get_element` then returns a `JString`, so the two `unsafe { 
JString::from_raw(...) }` casts go away. The [0.22 migration 
guide](https://github.com/jni-rs/jni-rs/blob/5ae9458a4ec44c5318f37ddc7569c1d4ae8a69e7/crates/jni/docs/0.22-MIGRATION.md?plain=1#L231-L234)
 gives that as the reason for the type parameter. The element type then rests 
on the `String[]` declaration in `NativeBase.java`, the same way `url: JString` 
already rests on its `String` declaration.
   
   ```suggestion
       keys: JObjectArray<JString>,
       values: JObjectArray<JString>,
   ) -> jstring {
       try_unwrap_or_throw(&env, |env| {
           let url: String = url.try_to_string(env)?;
           let mut configs = HashMap::new();
           for i in 0..keys.len(env)? {
               let key = keys.get_element(env, i)?.try_to_string(env)?;
               let value = values.get_element(env, i)?.try_to_string(env)?;
               configs.insert(key, value);
           }
   ```



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