You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by al...@apache.org on 2023/03/20 04:32:47 UTC

[kudu] branch master updated: [Python] Add support for immutable column

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

alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new 1e037b03e [Python] Add support for immutable column
1e037b03e is described below

commit 1e037b03e2f6ec85bbe8e31ba2b96288aa21911e
Author: Marton Greber <gr...@gmail.com>
AuthorDate: Sun Mar 5 15:28:43 2023 +0000

    [Python] Add support for immutable column
    
    This patch is a followup on: f20dcf57ad76e9b1bb57fe60b27ea3a8f02df233,
    it introduces the immutable column type in the Python client.
    
    There is a slight difference compared to the C++ API. In the C++ client
    one can set two type of KuduColumnSpec: immutable or mutable. In the
    Python client there is a single mutable ColumnSpec, which accepts a
    boolean value. This is not a new type of difference between the clients,
    for example the nullability works the same way, in terms of client
    implementations.
    
    Change-Id: I09bc3fe54ea7b40ccefa046c668e3622794b22e9
    Reviewed-on: http://gerrit.cloudera.org:8080/19586
    Tested-by: Kudu Jenkins
    Reviewed-by: Yingchun Lai <la...@apache.org>
    Reviewed-by: Alexey Serbin <al...@apache.org>
---
 python/kudu/libkudu_client.pxd   |  3 ++
 python/kudu/schema.pyx           | 22 +++++++++
 python/kudu/tests/test_client.py | 96 ++++++++++++++++++++++++++++++++++++++++
 python/kudu/tests/test_schema.py | 14 ++++++
 4 files changed, 135 insertions(+)

diff --git a/python/kudu/libkudu_client.pxd b/python/kudu/libkudu_client.pxd
index f7e1950db..671aca067 100644
--- a/python/kudu/libkudu_client.pxd
+++ b/python/kudu/libkudu_client.pxd
@@ -173,6 +173,7 @@ cdef extern from "kudu/client/schema.h" namespace "kudu::client" nogil:
         string& name()
         c_bool is_nullable()
         DataType type()
+        c_bool is_immutable()
         KuduColumnTypeAttributes type_attributes()
         string& comment()
 
@@ -208,6 +209,8 @@ cdef extern from "kudu/client/schema.h" namespace "kudu::client" nogil:
          KuduColumnSpec* NonUniquePrimaryKey()
          KuduColumnSpec* NotNull()
          KuduColumnSpec* Nullable()
+         KuduColumnSpec* Immutable()
+         KuduColumnSpec* Mutable()
          KuduColumnSpec* Type(DataType type_)
 
          KuduColumnSpec* Precision(int8_t precision);
diff --git a/python/kudu/schema.pyx b/python/kudu/schema.pyx
index 739dff1f9..00394e2b4 100644
--- a/python/kudu/schema.pyx
+++ b/python/kudu/schema.pyx
@@ -246,6 +246,10 @@ cdef class ColumnSchema:
         def __get__(self):
             return self.schema.is_nullable()
 
+    property mutable:
+        def __get__(self):
+            return not self.schema.is_immutable()
+
     property type_attributes:
         def __get__(self):
             cdef ColumnTypeAttributes result = ColumnTypeAttributes()
@@ -478,6 +482,24 @@ cdef class ColumnSpec:
             self.spec.NotNull()
         return self
 
+    def mutable(self, bint is_mutable=True):
+        """
+        Set mutable (True) or immutable (False)
+
+        Parameters
+        ----------
+        is_mutable : boolean, default True
+
+        Returns
+        -------
+        self
+        """
+        if is_mutable:
+            self.spec.Mutable()
+        else:
+            self.spec.Immutable()
+        return self
+
     def rename(self, new_name):
         """
         Change the column name.
diff --git a/python/kudu/tests/test_client.py b/python/kudu/tests/test_client.py
index be46ef4bb..ce76a87d3 100755
--- a/python/kudu/tests/test_client.py
+++ b/python/kudu/tests/test_client.py
@@ -341,6 +341,102 @@ class TestClient(KuduTestBase, unittest.TestCase):
         scanner = table.scanner().open()
         assert len(scanner.read_all_tuples()) == 0
 
+    def test_insert_and_mutate_immutable_column(self):
+        table_name = 'insert_and_mutate_immutable_column'
+        try:
+            builder = kudu.schema_builder()
+            (builder.add_column('key', kudu.int32)
+            .nullable(False)
+            .primary_key())
+            builder.add_column('immutable_data', kudu.string).mutable(False)
+            builder.add_column('mutable_data', kudu.string).mutable(True)
+            schema = builder.build()
+
+            self.client.create_table(
+                table_name, schema,
+                partitioning=Partitioning().add_hash_partitions(['key'], 2))
+
+            table = self.client.table(table_name)
+            session = self.client.new_session()
+            op = table.new_insert()
+            op['key'] = 1
+            op['immutable_data'] = 'text'
+            op['mutable_data'] = 'text'
+            session.apply(op)
+            session.flush()
+
+            # Update the mutable columns
+            op = table.new_update()
+            op['key'] = 1
+            op['mutable_data'] = 'new_text'
+            session.apply(op)
+            session.flush()
+
+            # Update the immutable column
+            op = table.new_update()
+            op['key'] = 1
+            op['immutable_data'] = 'new_text'
+            session.apply(op)
+            try:
+                session.flush()
+            except KuduBadStatus:
+                message = 'Immutable: UPDATE not allowed for immutable column'
+                errors, overflow = session.get_pending_errors()
+                assert not overflow
+                assert len(errors) == 1
+                assert message in repr(errors[0])
+
+            # Update ignore on both mutable and immutable columns. The error is ignored.
+            op = table.new_update_ignore()
+            op['key'] = 1
+            op['immutable_data'] = 'new_text'
+            op['mutable_data'] = 'new_text'
+            session.apply(op)
+            session.flush()
+
+            # Update ignore the immutable column
+            op = table.new_update_ignore()
+            op['key'] = 1
+            op['immutable_data'] = 'new_text'
+            session.apply(op)
+            try:
+                session.flush()
+            except KuduBadStatus:
+                message = 'Invalid argument: No fields updated'
+                errors, overflow = session.get_pending_errors()
+                assert not overflow
+                assert len(errors) == 1
+                assert message in repr(errors[0])
+
+            # TODO: test upsert ignore, once it is supported by the Python client.
+
+            # Upsert the mutable columns
+            op = table.new_upsert()
+            op['key'] = 1
+            op['mutable_data'] = 'new_text'
+            session.apply(op)
+            session.flush()
+
+            # Upsert the immutable column
+            op = table.new_upsert()
+            op['key'] = 1
+            op['immutable_data'] = 'new_text'
+            session.apply(op)
+            try:
+                session.flush()
+            except KuduBadStatus:
+                message = 'Immutable: UPDATE not allowed for immutable column'
+                errors, overflow = session.get_pending_errors()
+                assert not overflow
+                assert len(errors) == 1
+                assert message in repr(errors[0])
+
+        finally:
+            try:
+                self.client.delete_table(table_name)
+            except:
+                pass
+
     def test_insert_with_auto_incrementing_column(self):
 
         table_name = 'test_insert_with_auto_incrementing_column'
diff --git a/python/kudu/tests/test_schema.py b/python/kudu/tests/test_schema.py
index b3ba26b2c..069201f64 100644
--- a/python/kudu/tests/test_schema.py
+++ b/python/kudu/tests/test_schema.py
@@ -291,6 +291,20 @@ class TestSchema(unittest.TestCase):
         assert schema[3].nullable
         assert not schema[4].nullable
 
+    def test_mutable_immutable(self):
+        builder = kudu.schema_builder()
+        (builder.add_column('key', 'int64', nullable=False)
+         .primary_key())
+
+        builder.add_column('data1', 'double').mutable(True)
+        builder.add_column('data2', 'double').mutable(False)
+
+        schema = builder.build()
+
+        assert schema[0].mutable
+        assert schema[1].mutable
+        assert not schema[2].mutable
+
     def test_column_comment(self):
         comment = "test_comment"
         builder = kudu.schema_builder()