Re: [PR] [python] Introduce where for Python CLI table read [paimon]

2026-03-10 Thread via GitHub


JingsongLi merged PR #7389:
URL: https://github.com/apache/paimon/pull/7389


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



Re: [PR] [python] Introduce where for Python CLI table read [paimon]

2026-03-09 Thread via GitHub


plusplusjiajia commented on PR #7389:
URL: https://github.com/apache/paimon/pull/7389#issuecomment-4028936939

   LGTM, +1


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



Re: [PR] [python] Introduce where for Python CLI table read [paimon]

2026-03-09 Thread via GitHub


JingsongLi commented on code in PR #7389:
URL: https://github.com/apache/paimon/pull/7389#discussion_r2909526251


##
paimon-python/pypaimon/cli/where_parser.py:
##
@@ -0,0 +1,359 @@
+#  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.
+
+"""
+SQL WHERE clause parser for Paimon CLI.
+
+Parses simple SQL-like WHERE expressions into Predicate objects.
+
+Supported operators:
+  =, !=, <>, <, <=, >, >=,
+  IS NULL, IS NOT NULL,
+  IN (...), NOT IN (...),
+  BETWEEN ... AND ...,
+  LIKE '...'
+
+Supported connectors: AND, OR (AND has higher precedence than OR).
+Parenthesized grouping is supported.
+
+Examples:
+  "age > 18"
+  "name = 'Alice' AND age >= 20"
+  "status IN ('active', 'pending')"
+  "score BETWEEN 60 AND 100"
+  "name LIKE 'A%'"
+  "deleted_at IS NULL"
+  "age > 18 OR (name = 'Bob' AND status = 'active')"
+"""
+
+import re
+from typing import Any, Dict, List, Optional
+
+from pypaimon.common.predicate import Predicate
+from pypaimon.common.predicate_builder import PredicateBuilder
+from pypaimon.schema.data_types import AtomicType, DataField
+
+
+def extract_fields_from_where(where_string: str, available_fields: set) -> set:
+"""Extract all field names referenced in a WHERE clause.
+
+Args:
+where_string: The WHERE clause string.
+available_fields: Set of valid field names from the table schema.
+
+Returns:
+A set of field names referenced in the WHERE clause.
+"""
+if not where_string or not where_string.strip():
+return set()
+
+tokens = _tokenize(where_string.strip())
+referenced_fields = set()
+for token in tokens:
+if token in available_fields:
+referenced_fields.add(token)
+return referenced_fields
+
+
+def parse_where_clause(where_string: str, fields: List[DataField]) -> 
Optional[Predicate]:
+"""Parse a SQL-like WHERE clause string into a Predicate.
+
+Args:
+where_string: The WHERE clause string (without the 'WHERE' keyword).
+fields: The table schema fields for type resolution.
+
+Returns:
+A Predicate object, or None if the string is empty.
+
+Raises:
+ValueError: If the WHERE clause cannot be parsed.
+"""
+where_string = where_string.strip()
+if not where_string:
+return None
+
+field_type_map = _build_field_type_map(fields)
+predicate_builder = PredicateBuilder(fields)
+tokens = _tokenize(where_string)
+predicate, remaining = _parse_or_expression(tokens, predicate_builder, 
field_type_map)
+
+if remaining:
+raise ValueError(
+f"Unexpected tokens after parsing: {' '.join(remaining)}"
+)
+
+return predicate
+
+
+def _build_field_type_map(fields: List[DataField]) -> Dict[str, str]:
+"""Build a mapping from field name to its base type string."""
+result = {}
+for field in fields:
+if isinstance(field.type, AtomicType):
+result[field.name] = field.type.type.upper()
+else:
+result[field.name] = str(field.type).upper()
+return result
+
+
+def _cast_literal(value_str: str, type_name: str) -> Any:
+"""Cast a literal string to the appropriate Python type based on the field 
type."""
+integer_types = {'TINYINT', 'SMALLINT', 'INT', 'INTEGER', 'BIGINT'}
+float_types = {'FLOAT', 'DOUBLE'}
+
+base_type = type_name.split('(')[0].strip()
+
+if base_type in integer_types:
+return int(value_str)
+if base_type in float_types:
+return float(value_str)
+if base_type.startswith('DECIMAL') or base_type in ('DECIMAL', 'NUMERIC', 
'DEC'):
+return float(value_str)
+if base_type == 'BOOLEAN':
+return value_str.lower() in ('true', '1', 'yes')
+return value_str
+
+
+_TOKEN_PATTERN = re.compile(
+r"""
+  '(?:[^'\\]|\\.)*'   # single-quoted string
+| "(?:[^"\\]|\\.)*"# double-quoted string
+| <=   # <=
+| >=   # >=
+| <>   # <>
+| !=   # !=
+| [=<>]# single-char operators
+| [(),]# punctuation
+| [^\s,()=<>!'"]+  # unquoted word / number
+""",

Re: [PR] [python] Introduce where for Python CLI table read [paimon]

2026-03-09 Thread via GitHub


plusplusjiajia commented on code in PR #7389:
URL: https://github.com/apache/paimon/pull/7389#discussion_r2909492766


##
paimon-python/pypaimon/cli/where_parser.py:
##
@@ -0,0 +1,359 @@
+#  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.
+
+"""
+SQL WHERE clause parser for Paimon CLI.
+
+Parses simple SQL-like WHERE expressions into Predicate objects.
+
+Supported operators:
+  =, !=, <>, <, <=, >, >=,
+  IS NULL, IS NOT NULL,
+  IN (...), NOT IN (...),
+  BETWEEN ... AND ...,
+  LIKE '...'
+
+Supported connectors: AND, OR (AND has higher precedence than OR).
+Parenthesized grouping is supported.
+
+Examples:
+  "age > 18"
+  "name = 'Alice' AND age >= 20"
+  "status IN ('active', 'pending')"
+  "score BETWEEN 60 AND 100"
+  "name LIKE 'A%'"
+  "deleted_at IS NULL"
+  "age > 18 OR (name = 'Bob' AND status = 'active')"
+"""
+
+import re
+from typing import Any, Dict, List, Optional
+
+from pypaimon.common.predicate import Predicate
+from pypaimon.common.predicate_builder import PredicateBuilder
+from pypaimon.schema.data_types import AtomicType, DataField
+
+
+def extract_fields_from_where(where_string: str, available_fields: set) -> set:
+"""Extract all field names referenced in a WHERE clause.
+
+Args:
+where_string: The WHERE clause string.
+available_fields: Set of valid field names from the table schema.
+
+Returns:
+A set of field names referenced in the WHERE clause.
+"""
+if not where_string or not where_string.strip():
+return set()
+
+tokens = _tokenize(where_string.strip())
+referenced_fields = set()
+for token in tokens:
+if token in available_fields:
+referenced_fields.add(token)
+return referenced_fields
+
+
+def parse_where_clause(where_string: str, fields: List[DataField]) -> 
Optional[Predicate]:
+"""Parse a SQL-like WHERE clause string into a Predicate.
+
+Args:
+where_string: The WHERE clause string (without the 'WHERE' keyword).
+fields: The table schema fields for type resolution.
+
+Returns:
+A Predicate object, or None if the string is empty.
+
+Raises:
+ValueError: If the WHERE clause cannot be parsed.
+"""
+where_string = where_string.strip()
+if not where_string:
+return None
+
+field_type_map = _build_field_type_map(fields)
+predicate_builder = PredicateBuilder(fields)
+tokens = _tokenize(where_string)
+predicate, remaining = _parse_or_expression(tokens, predicate_builder, 
field_type_map)
+
+if remaining:
+raise ValueError(
+f"Unexpected tokens after parsing: {' '.join(remaining)}"
+)
+
+return predicate
+
+
+def _build_field_type_map(fields: List[DataField]) -> Dict[str, str]:
+"""Build a mapping from field name to its base type string."""
+result = {}
+for field in fields:
+if isinstance(field.type, AtomicType):
+result[field.name] = field.type.type.upper()
+else:
+result[field.name] = str(field.type).upper()
+return result
+
+
+def _cast_literal(value_str: str, type_name: str) -> Any:
+"""Cast a literal string to the appropriate Python type based on the field 
type."""
+integer_types = {'TINYINT', 'SMALLINT', 'INT', 'INTEGER', 'BIGINT'}
+float_types = {'FLOAT', 'DOUBLE'}
+
+base_type = type_name.split('(')[0].strip()
+
+if base_type in integer_types:
+return int(value_str)
+if base_type in float_types:
+return float(value_str)
+if base_type.startswith('DECIMAL') or base_type in ('DECIMAL', 'NUMERIC', 
'DEC'):
+return float(value_str)
+if base_type == 'BOOLEAN':
+return value_str.lower() in ('true', '1', 'yes')
+return value_str
+
+
+_TOKEN_PATTERN = re.compile(
+r"""
+  '(?:[^'\\]|\\.)*'   # single-quoted string
+| "(?:[^"\\]|\\.)*"# double-quoted string
+| <=   # <=
+| >=   # >=
+| <>   # <>
+| !=   # !=
+| [=<>]# single-char operators
+| [(),]# punctuation
+| [^\s,()=<>!'"]+  # unquoted word / number
+

Re: [PR] [python] Introduce where for Python CLI table read [paimon]

2026-03-09 Thread via GitHub


Copilot commented on code in PR #7389:
URL: https://github.com/apache/paimon/pull/7389#discussion_r2909476656


##
docs/content/pypaimon/cli.md:
##
@@ -94,10 +95,53 @@ paimon table read mydb.users -l 50
 # Read specific columns
 paimon table read mydb.users -s id,name,age
 
-# Combine select and limit
-paimon table read mydb.users -s id,name -l 50
+# Filter with WHERE clause
+paimon table read mydb.users --where "age > 18"
+
+# Combine select, where, and limit
+paimon table read mydb.users -s id,name -w "age >= 20 AND city = 'Beijing'" -l 
50
 ```
 
+**WHERE Operators**
+
+The `--where` option supports SQL-like filter expressions:
+
+| Operator | Example |
+|---|---|
+| `=`, `!=`, `<>` | `name = 'Alice'` |
+| `<`, `<=`, `>`, `>=` | `age > 18` |
+| `IS NULL`, `IS NOT NULL` | `deleted_at IS NULL` |
+| `IN (...)`, `NOT IN (...)` | `status IN ('active', 'pending')` |
+| `BETWEEN ... AND ...` | `age BETWEEN 20 AND 30` |
+| `LIKE` | `name LIKE 'A%'` |

Review Comment:
   The `NOT BETWEEN` operator is supported by the parser (and tested in 
`where_parser_test.py`) but is missing from the WHERE Operators table in the 
documentation. Both `NOT IN` and `NOT BETWEEN` are useful operators that users 
may want to know about. The table should include a row for `NOT BETWEEN ... AND 
...` alongside the existing `BETWEEN ... AND ...` entry.



##
paimon-python/pypaimon/cli/cli_table.py:
##
@@ -63,21 +63,46 @@ def cmd_table_read(args):
 # Build read pipeline
 read_builder = table.new_read_builder()
 
-# Apply projection (select columns) if specified
+available_fields = set(field.name for field in table.table_schema.fields)
+
+# Parse select and where options
 select_columns = args.select
+where_clause = args.where
+user_columns = None
+extra_where_columns = []
+
 if select_columns:
 # Parse column names (comma-separated)
-columns = [col.strip() for col in select_columns.split(',')]
-
+user_columns = [col.strip() for col in select_columns.split(',')]
+
 # Validate that all columns exist in the table schema
-available_fields = set(field.name for field in 
table.table_schema.fields)
-invalid_columns = [col for col in columns if col not in 
available_fields]
-
+invalid_columns = [col for col in user_columns if col not in 
available_fields]
 if invalid_columns:
 print(f"Error: Column(s) {invalid_columns} do not exist in table 
'{table_identifier}'.", file=sys.stderr)
 sys.exit(1)
-
-read_builder = read_builder.with_projection(columns)
+
+# When both select and where are specified, ensure where-referenced fields
+# are included in the projection so the filter can work correctly.
+if user_columns and where_clause:
+from pypaimon.cli.where_parser import extract_fields_from_where
+where_fields = extract_fields_from_where(where_clause, 
available_fields)
+user_column_set = set(user_columns)
+extra_where_columns = [f for f in where_fields if f not in 
user_column_set]

Review Comment:
   `where_fields` is returned as a `set` from `extract_fields_from_where`, so 
iterating over it to build `extra_where_columns` at line 90 gives a 
non-deterministic ordering. This makes the `projection_columns` list (and thus 
the projected `read_type`) non-deterministic when more than one 
WHERE-referenced field is missing from the user's selection. Consistently 
deterministic behavior (and especially correct predicate-index computation 
after fixing the related index bug) requires that `extra_where_columns` have a 
stable, reproducible order. Consider preserving the order from the table schema 
instead, e.g., building it as `[f.name for f in table.table_schema.fields if 
f.name not in user_column_set and f.name in where_fields]`.



##
paimon-python/pypaimon/cli/cli_table.py:
##
@@ -63,21 +63,46 @@ def cmd_table_read(args):
 # Build read pipeline
 read_builder = table.new_read_builder()
 
-# Apply projection (select columns) if specified
+available_fields = set(field.name for field in table.table_schema.fields)
+
+# Parse select and where options
 select_columns = args.select
+where_clause = args.where
+user_columns = None
+extra_where_columns = []
+
 if select_columns:
 # Parse column names (comma-separated)
-columns = [col.strip() for col in select_columns.split(',')]
-
+user_columns = [col.strip() for col in select_columns.split(',')]
+
 # Validate that all columns exist in the table schema
-available_fields = set(field.name for field in 
table.table_schema.fields)
-invalid_columns = [col for col in columns if col not in 
available_fields]
-
+invalid_columns = [col for col in user_columns if col not in 
available_fields]
 if invalid_columns:
 print(f"Error: Colu

Re: [PR] [python] Introduce where for Python CLI table read [paimon]

2026-03-09 Thread via GitHub


JingsongLi commented on code in PR #7389:
URL: https://github.com/apache/paimon/pull/7389#discussion_r2909461616


##
docs/content/pypaimon/cli.md:
##
@@ -94,10 +95,53 @@ paimon table read mydb.users -l 50
 # Read specific columns
 paimon table read mydb.users -s id,name,age
 
-# Combine select and limit
-paimon table read mydb.users -s id,name -l 50
+# Filter with WHERE clause
+paimon table read mydb.users --where "age > 18"
+
+# Combine select, where, and limit
+paimon table read mydb.users -s id,name -w "age >= 20 AND city = 'Beijing'" -l 
50
 ```
 
+**WHERE Operators**
+
+The `--where` option supports SQL-like filter expressions:
+
+| Operator | Example |
+|---|---|
+| `=`, `!=`, `<>` | `name = 'Alice'` |
+| `<`, `<=`, `>`, `>=` | `age > 18` |
+| `IS NULL`, `IS NOT NULL` | `deleted_at IS NULL` |
+| `IN (...)`, `NOT IN (...)` | `status IN ('active', 'pending')` |
+| `BETWEEN ... AND ...` | `age BETWEEN 20 AND 30` |

Review Comment:
   I do not document it, it is just supported by AI, but the application 
scenarios are extremely rare



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



Re: [PR] [python] Introduce where for Python CLI table read [paimon]

2026-03-09 Thread via GitHub


plusplusjiajia commented on code in PR #7389:
URL: https://github.com/apache/paimon/pull/7389#discussion_r2909454464


##
docs/content/pypaimon/cli.md:
##
@@ -94,10 +95,53 @@ paimon table read mydb.users -l 50
 # Read specific columns
 paimon table read mydb.users -s id,name,age
 
-# Combine select and limit
-paimon table read mydb.users -s id,name -l 50
+# Filter with WHERE clause
+paimon table read mydb.users --where "age > 18"
+
+# Combine select, where, and limit
+paimon table read mydb.users -s id,name -w "age >= 20 AND city = 'Beijing'" -l 
50
 ```
 
+**WHERE Operators**
+
+The `--where` option supports SQL-like filter expressions:
+
+| Operator | Example |
+|---|---|
+| `=`, `!=`, `<>` | `name = 'Alice'` |
+| `<`, `<=`, `>`, `>=` | `age > 18` |
+| `IS NULL`, `IS NOT NULL` | `deleted_at IS NULL` |
+| `IN (...)`, `NOT IN (...)` | `status IN ('active', 'pending')` |
+| `BETWEEN ... AND ...` | `age BETWEEN 20 AND 30` |

Review Comment:
   seems also support NOT BETWEEN



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



Re: [PR] [python] Introduce where for Python CLI table read [paimon]

2026-03-09 Thread via GitHub


Copilot commented on code in PR #7389:
URL: https://github.com/apache/paimon/pull/7389#discussion_r2909420254


##
paimon-python/pypaimon/cli/where_parser.py:
##
@@ -0,0 +1,357 @@
+#  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.
+
+"""
+SQL WHERE clause parser for Paimon CLI.
+
+Parses simple SQL-like WHERE expressions into Predicate objects.
+
+Supported operators:
+  =, !=, <>, <, <=, >, >=,
+  IS NULL, IS NOT NULL,
+  IN (...), NOT IN (...),
+  BETWEEN ... AND ...,
+  LIKE '...'
+
+Supported connectors: AND, OR (AND has higher precedence than OR).
+Parenthesized grouping is supported.
+
+Examples:
+  "age > 18"
+  "name = 'Alice' AND age >= 20"
+  "status IN ('active', 'pending')"
+  "score BETWEEN 60 AND 100"
+  "name LIKE 'A%'"
+  "deleted_at IS NULL"
+  "age > 18 OR (name = 'Bob' AND status = 'active')"
+"""
+
+import re
+from typing import Any, Dict, List, Optional
+
+from pypaimon.common.predicate import Predicate
+from pypaimon.common.predicate_builder import PredicateBuilder
+from pypaimon.schema.data_types import AtomicType, DataField
+
+
+def extract_fields_from_where(where_string: str, available_fields: set) -> set:
+"""Extract all field names referenced in a WHERE clause.
+
+Args:
+where_string: The WHERE clause string.
+available_fields: Set of valid field names from the table schema.
+
+Returns:
+A set of field names referenced in the WHERE clause.
+"""
+if not where_string or not where_string.strip():
+return set()
+
+tokens = _tokenize(where_string.strip())
+referenced_fields = set()
+for token in tokens:
+if token in available_fields:
+referenced_fields.add(token)
+return referenced_fields
+
+
+def parse_where_clause(where_string: str, fields: List[DataField]) -> 
Optional[Predicate]:
+"""Parse a SQL-like WHERE clause string into a Predicate.
+
+Args:
+where_string: The WHERE clause string (without the 'WHERE' keyword).
+fields: The table schema fields for type resolution.
+
+Returns:
+A Predicate object, or None if the string is empty.
+
+Raises:
+ValueError: If the WHERE clause cannot be parsed.
+"""
+where_string = where_string.strip()
+if not where_string:
+return None
+
+field_type_map = _build_field_type_map(fields)
+predicate_builder = PredicateBuilder(fields)
+tokens = _tokenize(where_string)
+predicate, remaining = _parse_or_expression(tokens, predicate_builder, 
field_type_map)
+
+if remaining:
+raise ValueError(
+f"Unexpected tokens after parsing: {' '.join(remaining)}"
+)
+
+return predicate
+
+
+def _build_field_type_map(fields: List[DataField]) -> Dict[str, str]:
+"""Build a mapping from field name to its base type string."""
+result = {}
+for field in fields:
+if isinstance(field.type, AtomicType):
+result[field.name] = field.type.type.upper()
+else:
+result[field.name] = str(field.type).upper()
+return result
+
+
+def _cast_literal(value_str: str, type_name: str) -> Any:
+"""Cast a literal string to the appropriate Python type based on the field 
type."""
+integer_types = {'TINYINT', 'SMALLINT', 'INT', 'INTEGER', 'BIGINT'}
+float_types = {'FLOAT', 'DOUBLE'}
+
+base_type = type_name.split('(')[0].strip()
+
+if base_type in integer_types:
+return int(value_str)
+if base_type in float_types:
+return float(value_str)
+if base_type.startswith('DECIMAL') or base_type in ('DECIMAL', 'NUMERIC', 
'DEC'):
+return float(value_str)
+if base_type == 'BOOLEAN':
+return value_str.lower() in ('true', '1', 'yes')
+return value_str
+
+
+_TOKEN_PATTERN = re.compile(
+r"""
+  '(?:[^'\\]|\\.)*'   # single-quoted string
+| "(?:[^"\\]|\\.)*"# double-quoted string
+| <=   # <=
+| >=   # >=
+| <>   # <>
+| !=   # !=
+| [=<>]# single-char operators
+| [(),]# punctuation
+| [^\s,()=<>!'"]+  # unquoted word / number
+""",
+ 

Re: [PR] [python] Introduce where for Python CLI table read [paimon]

2026-03-09 Thread via GitHub


JingsongLi commented on PR #7389:
URL: https://github.com/apache/paimon/pull/7389#issuecomment-4028751135

   3.10 Passed in 
https://github.com/JingsongLi/paimon/actions/runs/22886340765/job/66399708840


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



[PR] [python] Introduce where for Python CLI table read [paimon]

2026-03-09 Thread via GitHub


JingsongLi opened a new pull request, #7389:
URL: https://github.com/apache/paimon/pull/7389

   
   
   ### Purpose
   
   Read data from a Paimon table and display it in a tabular format.
   
   ```shell
   paimon table read mydb.users
   ```
   
   **Options:**
   
   - `--select, -s`: Select specific columns to read (comma-separated)
   - `--where, -w`: Filter condition in SQL-like syntax
   - `--limit, -l`: Maximum number of results to display (default: 100)
   
   **Examples:**
   
   ```shell
   # Read with limit
   paimon table read mydb.users -l 50
   
   # Read specific columns
   paimon table read mydb.users -s id,name,age
   
   # Filter with WHERE clause
   paimon table read mydb.users --where "age > 18"
   
   # Combine select, where, and limit
   paimon table read mydb.users -s id,name -w "age >= 20 AND city = 'Beijing'" 
-l 50
   ```
   
   **WHERE Operators**
   
   The `--where` option supports SQL-like filter expressions:
   
   | Operator | Example |
   |---|---|
   | `=`, `!=`, `<>` | `name = 'Alice'` |
   | `<`, `<=`, `>`, `>=` | `age > 18` |
   | `IS NULL`, `IS NOT NULL` | `deleted_at IS NULL` |
   | `IN (...)`, `NOT IN (...)` | `status IN ('active', 'pending')` |
   | `BETWEEN ... AND ...` | `age BETWEEN 20 AND 30` |
   | `LIKE` | `name LIKE 'A%'` |
   
   Multiple conditions can be combined with `AND` and `OR` (AND has higher 
precedence). Parentheses are supported for grouping:
   
   ```shell
   # AND condition
   paimon table read mydb.users -w "age >= 20 AND age <= 30"
   
   # OR condition
   paimon table read mydb.users -w "city = 'Beijing' OR city = 'Shanghai'"
   
   # Parenthesized grouping
   paimon table read mydb.users -w "(age > 18 OR name = 'Bob') AND city = 
'Beijing'"
   
   # IN list
   paimon table read mydb.users -w "city IN ('Beijing', 'Shanghai', 'Hangzhou')"
   
   # BETWEEN
   paimon table read mydb.users -w "age BETWEEN 25 AND 35"
   
   # LIKE pattern
   paimon table read mydb.users -w "name LIKE 'A%'"
   
   # IS NULL / IS NOT NULL
   paimon table read mydb.users -w "email IS NOT NULL"
   ```
   
   Literal values are automatically cast to the appropriate Python type based 
on the table schema (e.g., `INT` fields cast to `int`, `DOUBLE` to `float`).
   
   Output:
   ```
idname  age  city
 1   Alice   25   Beijing
 2 Bob   30  Shanghai
 3 Charlie   35 Guangzhou
 4   David   28  Shenzhen
 5 Eve   32  Hangzhou
   ```
   
   ### Tests
   
   
   
   ### API and Format
   
   
   
   ### Documentation
   
   
   
   ### Generative AI tooling
   
   
   
   Claude 4.6 Opus


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