Copilot commented on code in PR #6860:
URL: https://github.com/apache/texera/pull/6860#discussion_r3780016020


##########
common/config/src/main/scala/org/apache/texera/common/config/EnvironmentalVariable.scala:
##########
@@ -35,7 +35,10 @@ object EnvironmentalVariable {
   /**
     * FileService related endpoint
     */
-  val ENV_FILE_SERVICE_GET_PRESIGNED_URL_ENDPOINT = 
"FILE_SERVICE_GET_PRESIGNED_URL_ENDPOINT"
+  val ENV_FILE_SERVICE_GET_DATASET_PRESIGNED_URL_ENDPOINT =
+    "FILE_SERVICE_GET_DATASET_PRESIGNED_URL_ENDPOINT"
+  val ENV_FILE_SERVICE_GET_MODEL_PRESIGNED_URL_ENDPOINT =
+    "FILE_SERVICE_GET_MODEL_PRESIGNED_URL_ENDPOINT"

Review Comment:
   Removing the existing `FILE_SERVICE_GET_PRESIGNED_URL_ENDPOINT` name is a 
silent configuration break. Deployments that inject a custom FileService URL 
under the old name will now ignore it and fall back to `localhost` in both JVM 
and Python dataset clients. Preserve the old variable as a deprecated fallback 
(or provide an explicit migration path) while introducing the resource-specific 
names.



##########
common/workflow-core/src/main/scala/org/apache/texera/amber/core/storage/FileResolver.scala:
##########
@@ -76,106 +85,203 @@ object FileResolver {
   }
 
   /**
-    * Parses a dataset logical path into its components, or None if it is not 
a well-formed dataset path.
-    * Expected format: 
/datasets/ownerEmail/datasetName/versionName/fileRelativePath
-    * The legacy unprefixed format is also accepted.
+    * Parses a versioned-resource file path into its components, or None if it 
is not well-formed.
+    *
+    * Two accepted forms:
+    *   - Prefixed:  
/<prefix>/ownerEmail/resourceName/versionName/fileRelativePath (>= 5 segments)
+    *     where the leading segment names a known [[ResourceType]] (dataset, 
model, …), so a single
+    *     caller can dispatch to the right backing table.
+    *   - Legacy unprefixed (datasets only, backward compat): 
/ownerEmail/datasetName/versionName/
+    *     fileRelativePath (>= 4 segments), resolved as a dataset. Models are 
new and always require
+    *     the /models/ prefix.
     *
-    * @param fileName The file path to parse
-    * @return Some((ownerEmail, datasetName, versionName, fileRelativePath)) 
if valid, None otherwise
+    * @param fileName the file path to parse
+    * @return Some((resourceType, ownerEmail, resourceName, versionName, 
fileRelativePath)) if valid,
+    *         None otherwise
     */
-  private def parseDatasetFilePath(
+  private def parsePrefixedPath(
       fileName: String
-  ): Option[(String, String, String, Array[String])] = {
+  ): Option[(ResourceType.Value, String, String, String, Array[String])] = {
     val filePath = Paths.get(fileName)
     val pathSegments = (0 until 
filePath.getNameCount).map(filePath.getName(_).toString).toArray
 
-    // TODO(datasets-prefix): require the prefix once all stored paths are 
migrated (36.sql).
-    if (pathSegments.headOption.exists(ResourceType.isValidPrefix)) {
-      if (pathSegments.length < 5) None
-      else Some((pathSegments(1), pathSegments(2), pathSegments(3), 
pathSegments.drop(4)))
-    } else if (pathSegments.length >= 4) {
-      Some((pathSegments(0), pathSegments(1), pathSegments(2), 
pathSegments.drop(3)))
-    } else None
+    pathSegments.headOption.flatMap(ResourceType.fromPrefix) match {
+      case Some(resourceType) =>
+        // Prefixed: /<prefix>/ownerEmail/resourceName/versionName/<file> (>= 
5 segments).
+        if (pathSegments.length < 5) None
+        else
+          Some(
+            (resourceType, pathSegments(1), pathSegments(2), pathSegments(3), 
pathSegments.drop(4))
+          )
+      case None =>
+        // Legacy unprefixed dataset path (backward compat): 
/ownerEmail/datasetName/versionName/<file>.
+        // TODO(datasets-prefix): require the prefix once all stored paths are 
migrated (36.sql).
+        if (pathSegments.length >= 4)

Review Comment:
   This fallback reintroduces unprefixed dataset paths, contradicting this PR's 
stated “required-prefix enforcement” and the stacked #6502 contract that the 
prefix selects the backing table. It also means an unknown five-segment prefix 
is interpreted as an owner email and can resolve if matching dataset rows 
exist. Remove the legacy fallback and update the legacy-path test to assert 
rejection.



##########
sql/updates/37.sql:
##########
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+\c texera_db
+
+SET search_path TO texera_db;
+
+BEGIN;
+
+-- Introduce ML models as a first-class resource (own primary key `mid`, own 
LakeFS repo namespace)
+-- and add model-specific attributes (framework, format).
+
+CREATE TABLE IF NOT EXISTS model
+(
+    mid             SERIAL PRIMARY KEY,
+    owner_uid       INT NOT NULL,
+    name            VARCHAR(128) NOT NULL,
+    repository_name VARCHAR(128),
+    is_public       BOOLEAN NOT NULL DEFAULT TRUE,
+    is_downloadable BOOLEAN NOT NULL DEFAULT TRUE,
+    description     TEXT NOT NULL,
+    creation_time   TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    cover_image     varchar(255),
+    framework       VARCHAR(32),
+    format          VARCHAR(32),
+    FOREIGN KEY (owner_uid) REFERENCES "user"(uid) ON DELETE CASCADE,
+    UNIQUE (owner_uid, name)
+);
+
+CREATE TABLE IF NOT EXISTS model_version
+(
+    mvid          SERIAL PRIMARY KEY,
+    mid           INT NOT NULL,
+    creator_uid   INT NOT NULL,
+    name          VARCHAR(128) NOT NULL,
+    version_hash  VARCHAR(64) NOT NULL,
+    creation_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    FOREIGN KEY (mid) REFERENCES model(mid) ON DELETE CASCADE

Review Comment:
   A model version is addressed by `(mid, name)`, but the table permits 
duplicate names for the same model. If duplicates are inserted (including by 
concurrent requests), `lookupModel`'s `fetchOneInto` can throw a too-many-rows 
exception, so the logical model path no longer resolves to one stable commit. 
Add a unique constraint on `(mid, name)`.



##########
sql/texera_ddl.sql:
##########
@@ -418,6 +419,36 @@ CREATE TABLE IF NOT EXISTS dataset_upload_session_part
         ON DELETE CASCADE
 );
 
+-- ML models
+CREATE TABLE IF NOT EXISTS model
+(
+    mid             SERIAL PRIMARY KEY,
+    owner_uid       INT NOT NULL,
+    name            VARCHAR(128) NOT NULL,
+    repository_name VARCHAR(128),
+    is_public       BOOLEAN NOT NULL DEFAULT TRUE,
+    is_downloadable BOOLEAN NOT NULL DEFAULT TRUE,
+    description     TEXT NOT NULL,
+    creation_time   TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    cover_image     varchar(255),
+    framework       VARCHAR(32),
+    format          VARCHAR(32),
+    FOREIGN KEY (owner_uid) REFERENCES "user"(uid) ON DELETE CASCADE,
+    UNIQUE (owner_uid, name)
+    );
+
+-- model_version
+CREATE TABLE IF NOT EXISTS model_version
+(
+    mvid          SERIAL PRIMARY KEY,
+    mid           INT NOT NULL,
+    creator_uid   INT NOT NULL,
+    name          VARCHAR(128) NOT NULL,
+    version_hash  VARCHAR(64) NOT NULL,
+    creation_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    FOREIGN KEY (mid) REFERENCES model(mid) ON DELETE CASCADE

Review Comment:
   Keep the bootstrap DDL consistent with migration 37 by enforcing uniqueness 
of a version name within its model. Without this constraint, a logical 
`/models/.../<version>/...` path can match multiple rows and fail resolution.



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

Reply via email to