This is an automated email from the ASF dual-hosted git repository.

szehon-ho pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.x by this push:
     new e10e94993092 [SPARK-58311][SQL] Gate generated column values on write 
with a table capability
e10e94993092 is described below

commit e10e9499309282e82c463620ee4948be95090d87
Author: Szehon Ho <[email protected]>
AuthorDate: Fri Jul 24 18:48:33 2026 -0700

    [SPARK-58311][SQL] Gate generated column values on write with a table 
capability
    
    ### What changes were proposed in this pull request?
    
    This PR moves the gate that decides whether Spark auto-fills and enforces 
generated column values on a DSv2 write from a **catalog-level** capability to 
a **table-level** capability.
    
    Specifically:
    - Adds `TableCapability.GENERATE_COLUMN_VALUES_ON_WRITE`, checked via 
`Table.capabilities()`.
    - Removes `TableCatalogCapability.SUPPORT_GENERATED_COLUMN_ON_WRITE` (added 
by SPARK-57644 and still unreleased).
    - `GeneratedColumn.supportsGeneratedColumnsOnWrite` now takes a `Table` and 
checks the table's capabilities instead of the `TableCatalog`'s.
    - Updates the four call sites to consult the table:
      - `Analyzer` (exposing generation-expression metadata to 
`TableOutputResolver` so missing values are auto-filled),
      - `ResolveTableConstraints` (adding the generated-column `CheckInvariant` 
constraints),
      - `RewriteRowLevelCommand` (blocking MERGE/UPDATE),
      - `ResolveWriteToStream` (blocking streaming writes).
    - Test infra: the in-memory test table advertises the new capability, gated 
by a `generate-column-values-on-write` table property (default `true`), 
mirroring the existing `accept-any-schema` / `auto-schema-evolution` toggles.
    
    ### Why are the changes needed?
    
    Whether Spark should generate/enforce generated column values is a property 
of an individual **table**, not of the whole **catalog**. A single catalog can 
expose tables from formats or protocol versions with differing generated-column 
support, so a catalog-wide flag is too coarse.
    
    This also aligns with how Delta Lake gates the feature: it is gated 
per-table on the table's protocol. 
`GeneratedColumn.satisfyGeneratedColumnProtocol(protocol)` returns 
`protocol.isFeatureSupported(GeneratedColumnsTableFeature)` -- i.e. it checks 
whether the `GeneratedColumnsTableFeature` is enabled in that specific table's 
protocol. A per-table `TableCapability` is the natural DSv2 analogue and 
matches the existing pattern for other per-table write behaviors 
(`BATCH_WRITE`, `OVERWRI [...]
    
    ### Does this PR introduce _any_ user-facing change?
    
    No change relative to a released version. The catalog capability being 
removed (`SUPPORT_GENERATED_COLUMN_ON_WRITE`) was introduced by SPARK-57644 and 
has not shipped in any release, so this only reshapes an unreleased API within 
the development branch. Connectors opt in by having their `Table` advertise 
`TableCapability.GENERATE_COLUMN_VALUES_ON_WRITE`. Auto-filling missing 
generated column values applies to by-name writes; ordinary by-position writes 
must still provide a value for e [...]
    
    ### How was this patch tested?
    
    `GeneratedColumnWriteSuite` (69 tests) passes. The existing "connector 
without the write capability does not auto-fill or enforce generated columns" 
coverage is retained (renamed to "table without write capability ..."), now 
driven by creating a table with `TBLPROPERTIES 
('generate-column-values-on-write' = 'false')` instead of a dedicated 
no-capability catalog.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Cursor (Opus 4.8)
    
    Closes #57481 from szehon-ho/dsv2-generated-column-gate.
    
    Authored-by: Szehon Ho <[email protected]>
    Signed-off-by: Szehon Ho <[email protected]>
    (cherry picked from commit 7865fb840daf027ca49fed81d894e6ebbb97b18a)
    Signed-off-by: Szehon Ho <[email protected]>
---
 .../sql/connector/catalog/TableCapability.java     | 22 ++++++-
 .../connector/catalog/TableCatalogCapability.java  | 20 +-----
 .../spark/sql/catalyst/analysis/Analyzer.scala     |  4 +-
 .../analysis/ResolveTableConstraints.scala         |  2 +-
 .../catalyst/analysis/RewriteRowLevelCommand.scala |  2 +-
 .../spark/sql/catalyst/util/GeneratedColumn.scala  | 26 ++++----
 .../sql/connector/catalog/InMemoryBaseTable.scala  |  7 +-
 .../connector/catalog/InMemoryTableCatalog.scala   |  3 +-
 .../streaming/runtime/ResolveWriteToStream.scala   |  2 +-
 .../sql/connector/GeneratedColumnWriteSuite.scala  | 76 ++++++++--------------
 10 files changed, 72 insertions(+), 92 deletions(-)

diff --git 
a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCapability.java
 
b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCapability.java
index 0a01c0c266b9..6a7c9b704e42 100644
--- 
a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCapability.java
+++ 
b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCapability.java
@@ -106,5 +106,25 @@ public enum TableCapability {
    * write modes, like {@link #TRUNCATE}, and {@link #OVERWRITE_BY_FILTER}, 
but cannot support
    * {@link #OVERWRITE_DYNAMIC}.
    */
-  V1_BATCH_WRITE
+  V1_BATCH_WRITE,
+
+  /**
+   * Signals that the table wants Spark to auto-fill generated column values 
and enforce generated
+   * column constraints during writes.
+   * <p>
+   * When this capability is present, Spark will:
+   * <ul>
+   *   <li>Auto-compute missing generated column values using the generation 
expression for
+   *       by-name writes. Ordinary by-position writes still require the input 
to provide a
+   *       value for every table column.</li>
+   *   <li>Validate explicitly-provided generated column values against the 
generation
+   *       expression.</li>
+   * </ul>
+   * <p>
+   * Without this capability, the connector is responsible for handling 
generated column values
+   * during writes.
+   *
+   * @since 4.3.0
+   */
+  GENERATE_COLUMN_VALUES_ON_WRITE
 }
diff --git 
a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCatalogCapability.java
 
b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCatalogCapability.java
index ac829395b9bc..a60c827d5ace 100644
--- 
a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCatalogCapability.java
+++ 
b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCatalogCapability.java
@@ -92,23 +92,5 @@ public enum TableCatalogCapability {
    * {@link TableCatalog#createTable}.
    * See {@link Column#identityColumnSpec()}.
    */
-  SUPPORTS_CREATE_TABLE_WITH_IDENTITY_COLUMNS,
-
-  /**
-   * Signals that the TableCatalog supports Spark auto-filling generated 
column values and
-   * enforcing generated column constraints during writes.
-   * <p>
-   * When this capability is present, Spark will:
-   * <ul>
-   *   <li>Auto-compute missing generated column values using the generation 
expression.</li>
-   *   <li>Validate explicitly-provided generated column values against the 
generation
-   *       expression.</li>
-   * </ul>
-   * <p>
-   * Without this capability, the connector is responsible for handling 
generated column values
-   * during writes.
-   *
-   * @since 4.3.0
-   */
-  SUPPORT_GENERATED_COLUMN_ON_WRITE
+  SUPPORTS_CREATE_TABLE_WITH_IDENTITY_COLUMNS
 }
diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala
 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala
index c77e5e16accb..01513d8926c1 100644
--- 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala
+++ 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala
@@ -3935,11 +3935,11 @@ class Analyzer(
         val defaultValueFillMode =
           if (conf.coerceInsertNestedTypes && v2Write.schemaEvolutionEnabled) 
RECURSE
           else FILL
-        // Only let TableOutputResolver see generation expression metadata if 
the catalog
+        // Only let TableOutputResolver see generation expression metadata if 
the table
         // supports auto-filling generated columns on write.
         val expected = v2Write.table match {
           case r: DataSourceV2Relation
-            if !GeneratedColumn.supportsGeneratedColumnsOnWrite(r.catalog) =>
+            if !GeneratedColumn.supportsGeneratedColumnsOnWrite(r.table) =>
             r.output.map(GeneratedColumn.removeGenerationExpressionMetadata)
           case _ => v2Write.table.output
         }
diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveTableConstraints.scala
 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveTableConstraints.scala
index de4490143a83..2a4b5693c705 100644
--- 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveTableConstraints.scala
+++ 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveTableConstraints.scala
@@ -88,7 +88,7 @@ class ResolveTableConstraints(val catalogManager: 
CatalogManager) extends Rule[L
   private def buildGeneratedColumnConstraints(
       r: DataSourceV2Relation,
       v2Write: V2WriteCommand): Seq[Expression] = {
-    if (!GeneratedColumn.supportsGeneratedColumnsOnWrite(r.catalog, 
r.table.columns())) {
+    if (!GeneratedColumn.supportsGeneratedColumnsOnWrite(r.table, 
r.table.columns())) {
       return Seq.empty
     }
 
diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/RewriteRowLevelCommand.scala
 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/RewriteRowLevelCommand.scala
index 60143ae46c3c..a64bdb6cbf58 100644
--- 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/RewriteRowLevelCommand.scala
+++ 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/RewriteRowLevelCommand.scala
@@ -62,7 +62,7 @@ trait RewriteRowLevelCommand extends Rule[LogicalPlan] {
       relation: DataSourceV2Relation,
       command: Command): Unit = {
     if (GeneratedColumn.supportsGeneratedColumnsOnWrite(
-        relation.catalog, relation.table.columns())) {
+        relation.table, relation.table.columns())) {
       throw QueryCompilationErrors.unsupportedTableOperationError(
         relation.catalog.get, relation.identifier.get,
         s"${command.toString} with generated columns")
diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/GeneratedColumn.scala
 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/GeneratedColumn.scala
index 0469cfc7678e..f8ca60c3a40a 100644
--- 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/GeneratedColumn.scala
+++ 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/GeneratedColumn.scala
@@ -19,8 +19,8 @@ package org.apache.spark.sql.catalyst.util
 
 import org.apache.spark.sql.catalyst.expressions.Attribute
 import org.apache.spark.sql.catalyst.plans.logical.ColumnDefinition
-import org.apache.spark.sql.connector.catalog.{CatalogPlugin, Column, 
Identifier, TableCatalog,
-  TableCatalogCapability}
+import org.apache.spark.sql.connector.catalog.{Column, Identifier, Table, 
TableCapability,
+  TableCatalog, TableCatalogCapability}
 import org.apache.spark.sql.errors.QueryCompilationErrors
 import org.apache.spark.sql.types.{Metadata, MetadataBuilder, StructField, 
StructType}
 
@@ -92,28 +92,24 @@ object GeneratedColumn {
   }
 
   /**
-   * Whether the table catalog supports Spark auto-filling generated column 
values and enforcing
-   * generated column constraints during writes (the
-   * [[TableCatalogCapability.SUPPORT_GENERATED_COLUMN_ON_WRITE]] capability). 
Without it, the
-   * connector is responsible for handling generated column values.
+   * Whether the table wants Spark to auto-fill generated column values and 
enforce generated
+   * column constraints during writes (the
+   * [[TableCapability.GENERATE_COLUMN_VALUES_ON_WRITE]] capability). Without 
it, the connector is
+   * responsible for handling generated column values.
    */
-  def supportsGeneratedColumnsOnWrite(catalog: Option[CatalogPlugin]): Boolean 
= {
-    catalog.exists {
-      case tc: TableCatalog =>
-        
tc.capabilities().contains(TableCatalogCapability.SUPPORT_GENERATED_COLUMN_ON_WRITE)
-      case _ => false
-    }
+  def supportsGeneratedColumnsOnWrite(table: Table): Boolean = {
+    
table.capabilities().contains(TableCapability.GENERATE_COLUMN_VALUES_ON_WRITE)
   }
 
   /**
-   * Whether the catalog supports generated columns on write (see
+   * Whether the table supports generated columns on write (see
    * [[supportsGeneratedColumnsOnWrite]]) and the given columns include at 
least one generated
    * column.
    */
   def supportsGeneratedColumnsOnWrite(
-      catalog: Option[CatalogPlugin],
+      table: Table,
       columns: Array[Column]): Boolean = {
-    supportsGeneratedColumnsOnWrite(catalog) &&
+    supportsGeneratedColumnsOnWrite(table) &&
       columns.exists(_.columnGenerationExpression() != null)
   }
 
diff --git 
a/sql/catalyst/src/test/scala/org/apache/spark/sql/connector/catalog/InMemoryBaseTable.scala
 
b/sql/catalyst/src/test/scala/org/apache/spark/sql/connector/catalog/InMemoryBaseTable.scala
index 78775d80cfa6..fba80eb3d4cb 100644
--- 
a/sql/catalyst/src/test/scala/org/apache/spark/sql/connector/catalog/InMemoryBaseTable.scala
+++ 
b/sql/catalyst/src/test/scala/org/apache/spark/sql/connector/catalog/InMemoryBaseTable.scala
@@ -195,6 +195,8 @@ abstract class InMemoryBaseTable(
   private val acceptAnySchema = properties.getOrDefault("accept-any-schema", 
"false").toBoolean
   private val autoSchemaEvolution = 
properties.getOrDefault("auto-schema-evolution", "true")
     .toBoolean
+  private val generateColumnValuesOnWrite =
+    properties.getOrDefault("generate-column-values-on-write", 
"true").toBoolean
 
   partitioning.foreach {
     case _: IdentityTransform =>
@@ -488,7 +490,10 @@ abstract class InMemoryBaseTable(
   override def capabilities(): util.Set[TableCapability] =
     (baseCapabiilities ++
       (if (acceptAnySchema) Seq(TableCapability.ACCEPT_ANY_SCHEMA) else 
Seq.empty) ++
-      (if (autoSchemaEvolution) 
Seq(TableCapability.AUTOMATIC_SCHEMA_EVOLUTION) else Seq.empty))
+      (if (autoSchemaEvolution) 
Seq(TableCapability.AUTOMATIC_SCHEMA_EVOLUTION) else Seq.empty) ++
+      (if (generateColumnValuesOnWrite) {
+        Seq(TableCapability.GENERATE_COLUMN_VALUES_ON_WRITE)
+      } else Seq.empty))
       .asJava
 
   override def newScanBuilder(options: CaseInsensitiveStringMap): ScanBuilder 
= {
diff --git 
a/sql/catalyst/src/test/scala/org/apache/spark/sql/connector/catalog/InMemoryTableCatalog.scala
 
b/sql/catalyst/src/test/scala/org/apache/spark/sql/connector/catalog/InMemoryTableCatalog.scala
index 6466e3b3da30..bb137ba4830d 100644
--- 
a/sql/catalyst/src/test/scala/org/apache/spark/sql/connector/catalog/InMemoryTableCatalog.scala
+++ 
b/sql/catalyst/src/test/scala/org/apache/spark/sql/connector/catalog/InMemoryTableCatalog.scala
@@ -286,8 +286,7 @@ class InMemoryTableCatalog extends 
BasicInMemoryTableCatalog with SupportsNamesp
       TableCatalogCapability.SUPPORT_COLUMN_DEFAULT_VALUE,
       TableCatalogCapability.SUPPORT_TABLE_CONSTRAINT,
       TableCatalogCapability.SUPPORTS_CREATE_TABLE_WITH_GENERATED_COLUMNS,
-      TableCatalogCapability.SUPPORTS_CREATE_TABLE_WITH_IDENTITY_COLUMNS,
-      TableCatalogCapability.SUPPORT_GENERATED_COLUMN_ON_WRITE
+      TableCatalogCapability.SUPPORTS_CREATE_TABLE_WITH_IDENTITY_COLUMNS
     ).asJava
   }
 
diff --git 
a/sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/runtime/ResolveWriteToStream.scala
 
b/sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/runtime/ResolveWriteToStream.scala
index 2afb2ad2f4eb..4167f12885b0 100644
--- 
a/sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/runtime/ResolveWriteToStream.scala
+++ 
b/sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/runtime/ResolveWriteToStream.scala
@@ -67,7 +67,7 @@ object ResolveWriteToStream extends Rule[LogicalPlan] {
 
       // Streaming writes with generated columns are not yet supported.
       s.catalogAndIdent.foreach { case (catalog, ident) =>
-        if (GeneratedColumn.supportsGeneratedColumnsOnWrite(Some(catalog), 
s.sink.columns())) {
+        if (GeneratedColumn.supportsGeneratedColumnsOnWrite(s.sink, 
s.sink.columns())) {
           throw QueryCompilationErrors.unsupportedTableOperationError(
             catalog, ident, "streaming write with generated columns")
         }
diff --git 
a/sql/core/src/test/scala/org/apache/spark/sql/connector/GeneratedColumnWriteSuite.scala
 
b/sql/core/src/test/scala/org/apache/spark/sql/connector/GeneratedColumnWriteSuite.scala
index 54875c54a103..81b2ea59cad9 100644
--- 
a/sql/core/src/test/scala/org/apache/spark/sql/connector/GeneratedColumnWriteSuite.scala
+++ 
b/sql/core/src/test/scala/org/apache/spark/sql/connector/GeneratedColumnWriteSuite.scala
@@ -21,8 +21,7 @@ import org.apache.spark.SparkRuntimeException
 import org.apache.spark.sql.{AnalysisException, QueryTest, Row}
 import org.apache.spark.sql.catalyst.QueryPlanningTracker
 import org.apache.spark.sql.catalyst.expressions.CheckInvariant
-import org.apache.spark.sql.connector.catalog.{InMemoryCatalog, 
InMemoryRowLevelOperationTableCatalog,
-  TableCatalogCapability}
+import 
org.apache.spark.sql.connector.catalog.InMemoryRowLevelOperationTableCatalog
 import org.apache.spark.sql.execution.streaming.runtime.MemoryStream
 import org.apache.spark.sql.internal.SQLConf
 
@@ -40,16 +39,10 @@ class GeneratedColumnWriteSuite extends QueryTest with 
DatasourceV2SQLBase {
     }
   }
 
-  // A catalog that supports creating generated columns but does NOT declare
-  // SUPPORT_GENERATED_COLUMN_ON_WRITE, so Spark must not auto-fill or enforce 
them.
-  private val noWriteCapCat = "nowritecapcat"
-
-  private def withNoWriteCapCatalog(f: => Unit): Unit = {
-    withSQLConf(s"spark.sql.catalog.$noWriteCapCat" ->
-      classOf[InMemoryNoGenColWriteCatalog].getName) {
-      f
-    }
-  }
+  // A table property that makes the in-memory test table omit the
+  // GENERATE_COLUMN_VALUES_ON_WRITE capability, so Spark must not auto-fill 
or enforce
+  // generated columns for it.
+  private val noWriteCapProp = "generate-column-values-on-write"
 
   private def hasCheckInvariant(sqlText: String): Boolean = {
     val parsed = spark.sessionState.sqlParser.parsePlan(sqlText)
@@ -1154,44 +1147,29 @@ class GeneratedColumnWriteSuite extends QueryTest with 
DatasourceV2SQLBase {
     }
   }
 
-  test("catalog without write capability does not auto-fill or enforce 
generated columns") {
-    withNoWriteCapCatalog {
-      val tblName = "my_tab"
-      withTable(s"$noWriteCapCat.$tblName") {
-        sql(s"""CREATE TABLE $noWriteCapCat.$tblName(
-               |  a INT,
-               |  b INT GENERATED ALWAYS AS (a + 1)
-               |) USING foo""".stripMargin)
+  test("table without write capability does not auto-fill or enforce generated 
columns") {
+    val tblName = "my_tab"
+    withTable(s"testcat.$tblName") {
+      sql(s"""CREATE TABLE testcat.$tblName(
+             |  a INT,
+             |  b INT GENERATED ALWAYS AS (a + 1)
+             |) USING foo TBLPROPERTIES ('$noWriteCapProp' = 
'false')""".stripMargin)
 
-        // A user-provided value that does NOT match the generation expression 
is written
-        // as-is: no constraint is enforced because the catalog does not opt 
in.
-        sql(s"INSERT INTO $noWriteCapCat.$tblName(a, b) VALUES (5, 999)")
-        checkAnswer(spark.table(s"$noWriteCapCat.$tblName"), Row(5, 999))
-
-        // Omitting the generated column does not auto-fill; it is treated as 
a regular
-        // nullable column and filled with null.
-        sql(s"INSERT INTO $noWriteCapCat.$tblName(a) VALUES (7)")
-        checkAnswer(
-          spark.table(s"$noWriteCapCat.$tblName"),
-          Row(5, 999) :: Row(7, null) :: Nil)
-
-        // No CheckInvariant is added to the plan for the generated column.
-        assert(!hasCheckInvariant(s"INSERT INTO $noWriteCapCat.$tblName(a, b) 
VALUES (5, 6)"),
-          "No CheckInvariant should be added when the catalog lacks the write 
capability")
-      }
-    }
-  }
-}
+      // A user-provided value that does NOT match the generation expression 
is written
+      // as-is: no constraint is enforced because the table does not opt in.
+      sql(s"INSERT INTO testcat.$tblName(a, b) VALUES (5, 999)")
+      checkAnswer(spark.table(s"testcat.$tblName"), Row(5, 999))
 
-/**
- * A catalog that supports creating tables with generated columns but does NOT 
declare
- * [[TableCatalogCapability.SUPPORT_GENERATED_COLUMN_ON_WRITE]], so Spark 
leaves generated
- * column handling to the connector (no auto-fill, no constraint enforcement 
on write).
- */
-class InMemoryNoGenColWriteCatalog extends InMemoryCatalog {
-  override def capabilities: java.util.Set[TableCatalogCapability] = {
-    val caps = new 
java.util.HashSet[TableCatalogCapability](super.capabilities)
-    caps.remove(TableCatalogCapability.SUPPORT_GENERATED_COLUMN_ON_WRITE)
-    caps
+      // Omitting the generated column does not auto-fill; it is treated as a 
regular
+      // nullable column and filled with null.
+      sql(s"INSERT INTO testcat.$tblName(a) VALUES (7)")
+      checkAnswer(
+        spark.table(s"testcat.$tblName"),
+        Row(5, 999) :: Row(7, null) :: Nil)
+
+      // No CheckInvariant is added to the plan for the generated column.
+      assert(!hasCheckInvariant(s"INSERT INTO testcat.$tblName(a, b) VALUES 
(5, 6)"),
+        "No CheckInvariant should be added when the table lacks the write 
capability")
+    }
   }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to